CSV to Graph

Drop a CSV and this page plots it as a graph: a line when there is a date column, a scatter when you name two measures, an area when the filled magnitude is the point. The time grain is written into the caption, the correlation is reported on the sub-caption, and every number stays inside your browser tab.

No file on hand right now? Try with sample sales data · a year of orders, plotted the second it loads.

Want a bar-first ordering instead? The chart page runs the same engine.

A graph is about movement, not magnitude

A bar chart hands you a ranking. A graph hands you a rate of change. Those are separate questions and people reach for the wrong one all the time, usually because the tool they opened had a bar as its first button. So this page reorders the menu. The Chart dropdown starts on Auto and then reads Line, Area, Scatter, Bar, Column, Donut, Histogram, Boxplot. The bar shapes are still there. They just are not the default reflex.

The reason the ordering matters is that a line encodes something a bar cannot. The eye reads the slope between two adjacent points as a rate. That is enormously useful and it is also a claim about the horizontal axis, namely that equal horizontal distance means equal elapsed time. When a graph honors that claim, a steep segment really does mean fast growth. When it does not, the graph is confidently wrong and nothing on the canvas says so.

Everything on this page is arranged around keeping that claim true. The date column is bucketed onto a regular grid before anything is drawn, so a week with four orders and a week with nine still occupy one bucket each. The grain of that grid is picked from the span of the data and then stated in the caption, out loud, in the same mono type as the axis ticks. I would rather a reader disagree with the bucketing than never learn what it was.

Which shape answers which question

All eight shapes redraw from rows that are already in memory, so switching between them costs nothing and the file is never read twice.

  • Line. Did this go up. The stroke carries the direction and the axis carries the size. Use it whenever the horizontal axis is time and you care about the shape of the movement more than the height of any one point.
  • Area. The same stroke with a low-opacity fill beneath it. The fill reads as accumulated volume, so pick it when the quantity under the curve is meaningful, such as total revenue booked in a quarter. Because the fill encodes magnitude, an area graph really does want a zero baseline in a way a plain line does not.
  • Scatter. Does this move with that. Set Measure to one numeric column and the Y measure dropdown appears for the second. Every row becomes a point, no aggregation happens, and the Pearson coefficient lands in the caption. Add a Dimension and the points take a color per category, up to eight groups.
  • Bar and Column. Ranking rather than trend. Bar lays the categories down the left where long labels are readable without rotation; Column stands them up, which suits short labels and short time sequences.
  • Donut. Parts of a whole, with the share in the legend. Worth resisting past about six slices.
  • Histogram. One measure, binned. This is the shape that tells you whether the average you have been quoting sits anywhere near a typical record.
  • Boxplot. Spread per group, with whiskers at 1.5 times the interquartile range and every point beyond them drawn as an outlier dot.

The remaining controls are the same six you would expect. Measure and Dimension pick the columns. Aggregate offers sum, average, count rows, count distinct, minimum, maximum and median. Show and Sort trim and order a categorical axis. Time grain overrides the bucketing.

Three ways a line quietly misleads

None of these are exotic. I have shipped all three by accident at one time or another.

  • Unequal spacing. Plot a column of dates as labels rather than as time and the axis becomes a list. Two readings a day apart and two readings a quarter apart get the same horizontal gap, so the slope stops meaning a rate at all. Bucketing to a fixed grain fixes this by construction, which is why it happens before drawing rather than as an option afterwards.
  • Too many buckets. Around 200 points on a panel this wide, the line stops being a line and becomes a gray band with a texture. Any real movement is buried under sampling noise. If a daily graph looks like static, step the grain up one level and look again; the underlying rows have not changed, only the size of the window you are viewing them through.
  • The zero baseline argument. The rule that every chart must start at zero is a bar-chart rule. A bar encodes its value as a length from a baseline, so cutting the baseline inflates the length and lies. A line encodes value as position and change as slope, so a tight axis on a line is often the honest choice: forcing a range of 98 to 102 down to a zero baseline flattens a real 4 percent swing into a straight horizontal stroke, which is its own kind of lie. The moment you switch that line to an area, though, the fill starts encoding magnitude, and the zero baseline is back in force.

The fourth trap is subtler and there is no automatic defense for it: a line drawn through a gap. If a source system stopped reporting for three weeks, a continuous stroke across that hole implies smooth behavior nobody observed. Check the preview table under the graph for the buckets you expected to see, and if a stretch is missing, say so in the sentence next to the picture.

Worked example: a year of orders

Seven columns, one date, three categories and three numbers, 180 rows across 2024. The first few:

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,East,Gizmo,Direct,7,172.02,1204.15

Dropped here with nothing clicked, you get a line graph titled revenue over time, a kicker reading TREND in the top right corner, and a caption reading Sum of revenue · monthly totals · 12 points. The vertical axis is marked $0, $40k, $80k, because revenue was read as money rather than as a bare number, and the horizontal axis carries three labels: first bucket, middle bucket, last bucket.

Monthly is not an arbitrary pick. 180 rows spread over twelve months would be 180 daily ticks on a panel about 700 pixels wide, roughly four pixels each. Twelve monthly points are legible from across a room. Set Time grain to Week and the same rows redraw as about 52 points, the caption updates to say so, and the seasonality that was smooth at monthly grain turns into a visible sawtooth. Neither picture is wrong. Only one of them is what you meant, and the caption is how a reader six months from now finds out which one they are looking at.

Now switch Chart to Scatter, leave Measure on units, and set Y measure to revenue. The title becomes revenue against units, all 180 rows appear as individual points, and the caption reads 180 points · r = 0.62 · moderate positive. Underneath, a note repeats it in a full sentence and adds that correlation is not causation. That 0.62 is doing real work here. Revenue is units times unit price, so a perfect 1.00 would only appear if every product cost the same; the gap between 0.62 and 1.00 is the price mix, and seeing it is the reason to plot the scatter at all.

Set Dimension to product while still on Scatter and the cloud splits into colored groups. What looked like one moderately correlated blob resolves into several tight, steeply sloped lines, one per product, each with its own price. That is the whole argument for scatter over a summary statistic: the aggregate said moderate, the picture says several strong relationships stacked on top of each other. Finish with Download chart data and you get the exact plotted pairs as CSV, so the appendix and the slide cannot drift apart.

Reading the correlation without overreading it

The coefficient in the sub-caption is Pearson's r over every row carrying a readable number in both columns. Rows missing either value are skipped, never zero-filled, because a zero-filled pair is a fabricated observation sitting at the origin and it drags the whole coefficient toward it. The words attached to the number are fixed: 0.70 and above is strong, 0.40 and above is moderate, anything below is weak, and the sign decides positive or negative.

Three cautions worth holding onto. Pearson measures straight-line association only, so a perfectly clean U-shaped relationship can score near zero. A single extreme point can pull r a long way, which is exactly why the points are drawn rather than replaced by the number. And a strong r across pooled groups can be an artifact of the pooling: put the dimension in and check whether the trend inside each group runs the same direction as the trend across them.

Exports are the same four everywhere on this site. Download PNG writes a 2x raster on the panel's own background, so it does not become a black rectangle on a dark slide. Copy image puts that PNG on the clipboard. Download SVG re-renders through a vector path renderer, which stays sharp at poster size and opens in Figma or Illustrator. Download chart data saves the plotted numbers as CSV. To put the graph on a web page rather than in a document, build an embed, where the data and the configuration travel compressed inside the iframe URL.

What this page will not do

The genuine edges, listed because these are the ones people run into within the first minute:

  • No second line. One series per graph. There is no multi-line comparison and no stacked area here. Two measures at once is what Scatter is for; a genuine multi-series line belongs in the app's chart builder, which has per-series aggregation and reference lines.
  • No trend line and no forecast. The scatter reports r and stops there. No fitted line is drawn, and no dashed continuation runs off the right edge of a time graph. A projection drawn from twelve noisy points looks authoritative and earns none of it.
  • No dual axis. Two measures on two vertical scales let you manufacture any crossing point you like by choosing the ranges. If the comparison is real, normalize both series first and plot them on one scale.
  • No log scale. The vertical axis is linear. For growth spanning several orders of magnitude, use the app.
  • No filtering. Every row in the file is plotted. Filter first in the app, or split the file with the CSV splitter.
  • A header row is required. If the first line is data, its values become the column names. Add a header, or promote a row in the app.
  • Dates must be parseable. Unrecognized date formats stop the graph with a message rather than falling back to alphabetical order. ISO dates always work.

Comma, semicolon, tab and pipe delimited files all load without configuration, so pasting cells straight from a spreadsheet works. Excel workbooks belong on excel-to-chart and nested JSON on json-to-chart; both flatten to the same rectangle and behave identically from there.

Frequently Asked Questions

What is the difference between this page and the chart page?

The engine is identical. The difference is what the Chart dropdown offers first. Here it opens on Auto and then lists Line, Area, Scatter, Bar, Column, Donut, Histogram and Boxplot, so the shapes that answer a question about movement or relationship sit at the top of the list where your hand lands. If your file has no date column and no second measure, you are almost certainly better served by the bar-first ordering on the chart page.

Why does the caption tell me the time grain?

Because a line without a stated grain is an unlabeled claim. The same file plotted daily, weekly and monthly gives three visibly different pictures, and only one of them is the one you meant. The caption under the title says which bucketing was used and how many points resulted, so nobody reading a screenshot months later has to guess. The grain is picked from the span of the date column and you can override it with the Time grain dropdown.

How is the correlation coefficient calculated?

It is the Pearson coefficient over every row that carries a readable number in both columns. Rows missing either value are skipped rather than treated as zero. The sub-caption reports it to two decimals with a word attached: strong at 0.7 or above, moderate from 0.4, weak below that, and positive or negative according to sign. A note under the graph repeats the number and adds that correlation is not causation, which is there because people screenshot the graph and not the paragraph.

When does a line graph mislead?

Three cases come up constantly. First, unequal spacing: a line drawn over irregularly sampled dates implies the gaps were even, so the slope between two points months apart looks like the slope between two points a day apart. Second, too many buckets, where 300 daily points on a narrow panel become a texture rather than a trend. Third, the baseline argument: a line chart does not need to start at zero, because a line encodes change rather than magnitude, but the moment you fill under it as an area the fill does encode magnitude and a cut baseline starts lying.

Can I plot two measures against each other?

Yes, that is what Scatter is for. Set Measure to the first numeric column and the Y measure dropdown appears; pick the second column there. Every row becomes a point, so scatter is the one shape on this page that is not an aggregate. If you also set a Dimension, the points are colored by that category up to eight groups, which is how you find out whether a relationship that looks like one cloud is really two clouds sitting next to each other.

What happens when my dates are not parseable?

The graph stops and says so, in the words No parseable dates found, naming the column and noting that ISO dates such as 2024-03-15 always work. It does not silently fall back to plotting the strings in alphabetical order, which is the failure mode that turns December into the second point of the year. Fix the column format, or pick a category dimension and read the file as a breakdown instead.

Why is my scatter plot empty?

If no row carries a readable number in both chosen columns, the page reports that no rows carry a number in both columns, so there is nothing to plot. This usually means one of the two columns is text that looks numeric, such as a measurement with a unit suffix in every cell, or that the two columns come from different sections of a file and never co-occur. Check the preview table under the graph and pick a pair that overlaps.

Does anything leave my browser?

No. The file is read by the tab, the aggregation runs in the tab, and the graph is drawn in the tab. There is no upload endpoint behind this page and no request that carries your rows anywhere. Once the page itself has loaded you can pull the network cable out and keep working, which is the simplest proof of the claim that I can offer.

Plot your file

Drop the CSV, read the slope, check the grain in the caption, take the SVG. No sign-up and no upload.

Back to the graph plotter