I mapped The Odyssey and its two newest adaptations as a graph, and learned that some data errors are invisible until you stop reading and start looking.

Two enormous adaptations of the same 2,700-year-old poem have taken over the internet recently—EPIC: The Musical, a musical retelling of The Odyssey that found its cast on TikTok, and Christopher Nolan’s film The Odyssey, with Matt Damon in the lead. Same source material, wildly different choices, and endless fandom arguments about who cut what, who kept what, and why. I wanted to settle some of those arguments with data, so I did what any reasonable person would do and spent my evenings hand-building a dataset of the entire story.
The spreadsheet phase
The data lived in two CSV files. One for every hero, god, monster, mortal, place, song, and actor, 130 in all. And one for the connections between them. That ran to 235 rows, with each one reading like a tiny sentence:
| from | to | type |
| Odysseus | Polyphemus | BLINDS |
| Polyphemus | Odysseus | CURSES |
| Poseidon | Odysseus | OPPOSES |
I worked from Emily Wilson’s translation of the poem, cross-checked against the cast lists for the musical and the film, and noted my sources row by row. When I scrolled those spreadsheets, they looked fairly normal: tidy columns and consistent naming. Every row I checked was true.
However, that was the trap: every row was true, and that is exactly why the error survived. The error existed between the rows, and a table had no way of directly showing me that.
Turning the story into a graph
Because the data was shaped like sentences—NOUN → VERB → NOUN—it slotted naturally into a graph database (I used Neo4j Aura’s free tier, whose built-in visualisation tool needs no setup). Loading it took two short Cypher scripts. Then came the moment that justified every evening of data entry: watching the story assemble itself as a diagram, connections blooming outward wave by wave until all 130 nodes hung on the canvas like a constellation.

(If you’d like to watch it assemble on your own screen, the build-it-yourself tutorial version of this project—full dataset, every script—is free on GraphAcademy.)
It was beautiful. Colour-coded gods and monsters, the musical’s songs orbiting the characters they feature, two casts of actors pointing at their roles. I sat back to admire it.
And then I stopped admiring. Something was wrong.
Why is Agamemnon all alone over there?
Off to one side, separated from the story by a strip of empty canvas, floated a little island of data points: Agamemnon, Menelaus, Helen of Troy, Clytemnestra, and the actors who play them. The nodes were connected to each other but not to anything else in the story.

Every one of those nodes was in my careful spreadsheets. Every relationship among them was correct. But not one of the 235 rows in my table linked that family to Odysseus’s side of the story. The Trojan War plotline was structurally severed from the rest of the data, and my tables had hidden that gap for weeks without me noticing.
No amount of scrolling through tables would have found it. The graph layout did in one glance. A force-directed layout positions nodes by their connections. Things pull together when linked, and drift apart when not, which means disconnection becomes distance. Whilst decorating my data, the algorithm was also analysing it, and letting me know—through a gap in the graph itself—that something was wrong.
The 10-second fix
The repair was almost comically small. In Book 4 of the poem, Odysseus’s son Telemachus visits Menelaus, seeking news of his father (film spoiler: this is when Tom Holland’s character visits Jon Bernthal’s character to ask if his father is alive). All I had to do was write three lines of Cypher—which read almost like the sentence they create—“Telemachus → VISITS → Menelaus”:
MATCH (t:Character {name: "Telemachus"})
MATCH (m:Character {name: "Menelaus"})
MERGE (t)-[:VISITS]->(m)
Once I ran this query, adding the relationship to the dataset, the island of data snapped right back into the story.

The graph caught right away what weeks of table-reading had missed, and the fix took 10 seconds.
Tables and graphs see different things
In a table, each row is a thing and each column is a fact about it. For example: Odysseus would get a row of his own, but his connection to Polyphemus—Odysseus BLINDS Polyphemus—would have no row of its own.
A table can only hint at their relationship through matching values; however, a graph stores the relationship itself as an actual arrow between two nodes.

What tables catch best
The table allowed me to see that my count was off. When I checked my data, I ran a simple count and looked at the result as a table: it showed 125 nodes, which I immediately knew was wrong, because my spreadsheets held 130. Seeing those two numbers, side by side in a table, allowed me to catch the error. That’s what tables are good at: catching errors you can count or compare—such as a total that’s off, a duplicate entry, an empty cell, a date that isn’t right. If the mistake lives inside a value, lining values up in rows and columns will expose it.
What graphs catch best
Graphs catch errors in how things are connected—such as a link that should exist but doesn’t, or a relationship pointing the wrong way. These kinds of errors are invisible in a table, because a table has no cell where a missing connection would go. There’s nothing to count because the problem is an absence, and those same invisible absences hide in everyday data. A company’s org chart lives happily in a spreadsheet until someone looks at it and finds a team connected to no manager at all. A website’s pages can each look fine in a list, while a map would show a cluster of orphaned pages that have no links. Through a graph, fraud investigators see five “unrelated” bank accounts sharing one phone number.
So the rule I go by is that if the error is in a value, a table will catch it. If the error is in a connection, a graph will show it—because only a graph treats connections as real things that can be visibly missing.
When the graph is the finding
Once the missing link was fixed, the data started answering questions correctly. Who has played Odysseus? Two actors, it turns out—Matt Damon in the film, and Jorge Rivera-Herrans, who wrote the musical and cast it on TikTok. On the canvas, that answer is a triangle: two Actor nodes, arrows converging on one man. Nobody needs a legend, query, or caption to read it.

For connected data, a graph layout does the analysis the moment it renders: clustering what belongs together, exiling what’s orphaned, and drawing your eye to exactly the empty space where a relationship should be.
I went looking for a fandom argument and came back with a debugging method. A table with a missing row doesn’t look broken, and that’s the whole problem. Try visualising your data as a graph. Somewhere in it, there’s an Agamemnon waiting to be discovered.
This story began life as a hands-on tutorial: the dataset, the queries, and the graph that found the plot hole are all in the original version on GraphAcademy. The full dataset lives on GitHub—so feel free to download the data model and CSVs and import them into Neo4j Aura.

Kemi Elizabeth Ojogbede
Kemi Elizabeth Ojogbedeis a Technical Curriculum Developer at Neo4j's GraphAcademy, with a background in data science, developer education, and journalism—and a habit of turning stories into datasets. When she isn't speaking at conferences or building courses, she's making a data visualisation of whatever she's obsessed with this month.



