CSV to Sankey

Name two columns and a number, and this page draws the volume moving between them. Link width is the aggregate, nodes are the distinct values on each side, blank ends are dropped, and only positive flows are drawn. No account, no upload, no preparation of your file into an edge list first.

Nothing handy? Take the sample sales file for a spin · from region, to product, flow by revenue.

Need to reshape the file first? Open the app

A sankey answers where, not how much

Every chart on this site turns a table into a picture, but a sankey is doing something the others cannot. A bar chart of revenue by region and a bar chart of revenue by product are two separate facts sitting on two separate pages. A sankey holds the join between them: not just how much the West did and how much Doohickey earned, but how much of the West's money was Doohickey money. That crossing is what a flow diagram is for, and it is why people reach for one when a funnel or a bar chart leaves them still asking questions.

Most sankey tools online demand an edge list: a file with exactly the columns source, target and value, one row per link, already aggregated. That is a reasonable format for a library and a miserable one for a person, because it means doing the grouping by hand in a spreadsheet before you are allowed to see anything. This page takes the raw table instead. Four hundred order lines with a region column and a product column are enough; the grouping happens when you name the two ends, and it happens again instantly whenever you change your mind about which two columns are interesting.

Because of that, the fastest way to use this page is to treat it as an exploration rather than a rendering step. Point From at one categorical column and To at another, look at the picture, then swap one of them. Channel to product, region to channel, status to owner. Two or three swaps usually surface the imbalance somebody was going to ask about in the meeting, and each swap is a dropdown rather than a round trip through Excel.

Five controls

The rows are parsed once. Every control after that re-groups numbers already in memory, so the diagram redraws immediately and the file is never touched again.

  • From. The column the volume leaves. Its distinct values become the nodes on the left. Leave it unset and the page stops with a message naming the control: A sankey diagram needs a column to group by. Pick one under "From".
  • To. The column the volume arrives at, and the nodes on the right. Its own message is A sankey diagram needs a target column. Pick one under "To". Both errors name the control rather than a variable, because you should never have to map an error message onto a dropdown yourself.
  • Flow. The number that becomes ribbon width. Set it to Count rows and the diagram measures traffic instead of value, which is often what you want for a file of events, tickets or applications where the interesting quantity is how many rather than how much.
  • Aggregate. Sum, average, count, count distinct, minimum, maximum, median. Sum is the default and the one that makes a sankey behave like a physical flow, where the ribbons leaving a node add up to what entered it. Average is legitimate but changes the meaning: the ribbons no longer add up, so read the caption before drawing conclusions from thickness.
  • Show. Top 5, 10, 20, 50 or everything, multiplied by six to get the link cap, with a floor of four. Links are ranked by size first, so the cut always removes the thinnest traffic and never the interesting ribbon.

Worked example: where each region's money goes

The sample file holds 180 order lines from 2024. Its first rows look like this:

date,region,product,channel,units,unit_price,revenue
2024-01-02,West,Doohickey,Direct,58,211.64,12275.12
2024-01-04,West,Sprocket,Partner,13,40.73,529.49
2024-01-06,North,Sprocket,Retail,31,147.97,4587.07
2024-01-08,South,Gizmo,Direct,33,13.81,455.73
2024-01-10,North,Gizmo,Direct,20,55.49,1109.80

Set From to region, To to product, Flow to revenue. The panel is titled region to product, kicked with FLOW, and captioned 20 flows · 9 nodes · width is sum of revenue. Four region nodes stand on the left, five product nodes on the right, and twenty ribbons cross between them.

Nine nodes is worth pausing on. Four plus five is nine, which is only true because the two sides are kept separate internally. If a value ever appeared in both columns, say a file where From and To are both status, the naive approach would merge them into one node and hand the layout a cycle to resolve. The layout would either refuse or draw a ribbon curling back into its own source. Each side is tagged before layout and untagged before labelling, so the picture is always a clean left-to-right and the labels never show the tag.

Hovering a ribbon gives the pair and the value, formatted as money because revenue was read as money: West to Doohickey · $54.9k. The interesting reading is rarely the biggest ribbon. It is usually the region whose ribbons all go to one product while every other region spreads across four, which is the kind of concentration a pair of bar charts hides completely.

Now switch From to channel. The title becomes channel to product, the node count drops to nine again with four channels on the left, and the caption's flow count changes to match whatever pairs actually occur in the data. Switch Flow to Count rows and the caption's last clause reads width is row count, which is the version to use when you care about how often a route is taken rather than what it is worth.

Three rules that keep the diagram honest

A sankey is unusually easy to render wrongly in a way that still looks convincing, so three decisions are made before anything is drawn.

  • No cycles, ever. Source names and target names live in separate namespaces internally, so a value present on both sides can never collapse into one node with a self-referential ribbon. The label you see is the original text with the internal marker stripped off, so this costs you nothing in readability and saves the layout from an unsolvable graph.
  • Only positive flows. Pairs aggregating to zero or below are excluded. Width has no sign, and a ribbon drawn from a negative number is a picture that means the opposite of what it shows. If nothing survives, the page stops rather than drawing an empty frame: No positive flows found. A sankey needs a measure that is above zero.
  • Blank ends are dropped. A row with an empty From or an empty To has no flow to describe, so it is excluded rather than becoming a node named after nothing. That is a deliberate difference from the grouped charts elsewhere on the site, where an empty category is a real bucket and gets shown as one.

All three rules are visible in the output rather than buried here. The caption counts the flows and the nodes that survived, so a diagram thinner than you expected announces itself, and the export lists exactly the pairs that were drawn.

Getting it out of the browser

  • Download the flows. Three columns, from, to and the aggregated measure, one row per ribbon, ordered largest first exactly as the diagram ranked them. This is the file to attach when someone asks whether the picture is right.
  • Download PNG. Two-times density on the panel's own background, so the ribbons keep their contrast when the image lands on a slide with a dark theme.
  • Download SVG. Rendered again through a vector renderer, so the ribbons are real curved paths and the node labels are real text. A sankey suffers more than most charts from being scaled up as a raster, so this is the export to use for anything printed or projected large.
  • Copy image. The PNG onto the clipboard for a document or a chat message, without a file touching disk.

For a diagram that lives on a web page rather than in a document, the embed builder packs the flows and the configuration into an iframe URL. The chart renders from the link, with no server involved and nothing about your data stored anywhere.

What this page will not do

The real limits, stated up front rather than discovered at eleven at night:

  • One hop only. From and To, two columns of nodes. No multi-stage energy-flow diagram with four vertical bands, because that needs an explicit edge list and a different input contract from the plain table this page promises to accept.
  • No manual node ordering. The layout decides vertical placement to minimize crossings. You cannot pin a node to the top, and dragging is not persisted into the export.
  • No negatives. Covered above. Signed movements belong in a waterfall, which draws increases and decreases in different colors on a running total.
  • No filtering. Every row that survives the blank check is grouped. Filter first in the app, or cut the file down with the CSV splitter.
  • Not a good fit for hundreds of pairs. Even with the cap raised, a diagram with several hundred ribbons is a grey wash. When that is the shape of your data, a heatmap of the same two columns stays readable where a sankey gives up.
  • A header row is required. Without one, the first line of data becomes the column names and the dropdowns fill with values.

Comma, semicolon, tab and pipe delimited files all parse with no configuration, so a block of cells copied from a spreadsheet works in the paste tab. Workbooks and JSON come in through excel-to-chart and json-to-chart.

Frequently Asked Questions

What decides how thick a link is?

The aggregate of the Flow column for every row sharing that From and To pair. With Flow set to revenue and Aggregate on Sum, a ribbon twice as thick carries twice the money. The caption states this in words, reading something like width is sum of revenue, so a reader who finds the picture without the page still knows what the thickness means. Set Flow to Count rows and the ribbons measure how many records took each route instead.

Why do the same names appear on both sides without joining up?

Because source and target names are deliberately kept distinct behind the scenes. A value that shows up in both the From column and the To column, which is normal in any file describing a transition between states, would otherwise be a single node with a link leaving it and arriving at it. That is a cycle, and a sankey layout cannot resolve one: the renderer either throws or draws a ribbon looping back on itself. Each side is tagged internally so the two roles stay separate, and the tag is stripped again for the label you read.

Can it show negative flows?

No, and it will not fake them. Only pairs whose aggregate comes out above zero become links, for the same reason a treemap refuses negative area: a ribbon has a width and a width has no sign. If nothing in the file is positive the page stops with the message No positive flows found. A sankey needs a measure that is above zero. When your numbers are genuinely signed movements, a waterfall is the shape that handles them properly.

What happens to rows with a blank From or To?

They are dropped. A flow needs both ends to exist, and inventing a node called blank would put a phantom category into the diagram that no reader could interpret. This is different from how the grouped charts treat blanks, where an empty category is a legitimate bucket worth seeing. If a lot of rows are vanishing, the usual cause is a trailing section in the file or an export that leaves the target empty for records still in progress.

How many links will it draw?

The Show setting sets the cap and the sankey multiplies it by six, with a floor of four links. Top 10 therefore keeps the sixty largest flows. The multiplier exists because a sankey's unit is a pair, not a category: ten sources crossed with ten targets is a hundred possible ribbons, so a cap of ten would hide most of a perfectly ordinary diagram. Links are sorted by size before the cut, so what survives is always the heaviest traffic.

Is this only two columns wide?

Yes. One From, one To, one hop. Multi-stage sankeys with four or five columns of nodes are beautiful and they need a file shaped as an explicit edge list, which is a different input contract from the one this page accepts. You can approximate a second hop by stacking two diagrams, or by building the edge list yourself in the app and coming back with it. What this page guarantees is that a plain two-column CSV works with no preparation at all.

Does anything get uploaded?

No. Parsing, grouping and layout all happen inside your browser tab, and there is no endpoint behind this page that could receive a file. Close the tab and the data is gone with it. The page also keeps working after you disconnect the network, which is the simplest way to confirm the claim yourself.

Can I export the flows as numbers?

Yes. Download the flows writes a CSV with three columns, from, to and the aggregated measure, one row per ribbon in the picture, in the same order the diagram used. Because the export is generated from the same computed links as the drawing, the table in your appendix and the diagram in your deck cannot drift apart. PNG, SVG and copy-to-clipboard exports of the picture sit alongside it.

Follow the volume

Drop the CSV, name two columns, watch where it goes. No sign-up, no upload, no edge list required.

Back to the sankey maker