Datasets:
id string | family string | title string | tests string | level int64 | n_series int64 | n_points int64 | axis_scales unknown | image string | truth string | spec string | file_name string |
|---|---|---|---|---|---|---|---|---|---|---|---|
A01 | line | Rail-jump lattice | colour-based tracing that swaps identity at a crossing | 2 | 8 | 3,200 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A01.png | truth/A01.truth.json | spec/A01.spec.json | images/A01.png |
A02 | line | Dense stochastic bundle | token budget and sampling honesty | 2 | 12 | 24,000 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A02.png | truth/A02.truth.json | spec/A02.spec.json | images/A02.png |
A03 | line | Near-parallel pair | sub-stroke separation between two series | 2 | 2 | 1,000 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A03.png | truth/A03.truth.json | spec/A03.spec.json | images/A03.png |
A04 | line | Hairline on dark ground | ink thresholding and halo attribution | 2 | 3 | 1,800 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A04.png | truth/A04.truth.json | spec/A04.spec.json | images/A04.png |
A05 | line | Dash-pattern identity | series identity carried by stroke, not hue | 2 | 3 | 900 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A05.png | truth/A05.truth.json | spec/A05.spec.json | images/A05.png |
A06 | line | Step and spline in one frame | corner preservation vs dense resampling | 2 | 2 | 416 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A06.png | truth/A06.truth.json | spec/A06.spec.json | images/A06.png |
A07 | line | Spike train | peak preservation under resampling | 2 | 1 | 1,200 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A07.png | truth/A07.truth.json | spec/A07.spec.json | images/A07.png |
A08 | line | Sawtooth near Nyquist | honesty about recoverable resolution | 2 | 1 | 3,000 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A08.png | truth/A08.truth.json | spec/A08.spec.json | images/A08.png |
A09 | line | Four-decade log crossing | scale type before geometry | 2 | 6 | 2,400 | {
"panel0": {
"x": "linear",
"y": "log"
}
} | images/A09.png | truth/A09.truth.json | spec/A09.spec.json | images/A09.png |
A10 | line | Broken axis | piecewise transforms | 2 | 2 | 50 | {
"panel0": {
"x": "linear",
"y": "linear"
},
"panel1": {
"x": "linear",
"y": "linear"
}
} | images/A10.png | truth/A10.truth.json | spec/A10.spec.json | images/A10.png |
A11 | line | Twin axes, mirrored ranges | axis binding | 2 | 2 | 600 | {
"panel0": {
"x": "linear",
"y": "linear"
},
"panel1": {
"x": "linear",
"y": "linear"
}
} | images/A11.png | truth/A11.truth.json | spec/A11.spec.json | images/A11.png |
A12 | line | Inline value labels on the marks | datum vs annotation | 2 | 3 | 48 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A12.png | truth/A12.truth.json | spec/A12.spec.json | images/A12.png |
A13 | line | Legend inside the plot box | furniture exclusion | 2 | 4 | 1,200 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A13.png | truth/A13.truth.json | spec/A13.spec.json | images/A13.png |
A14 | line | Clipped excursion | gap honesty | 2 | 1 | 500 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A14.png | truth/A14.truth.json | spec/A14.spec.json | images/A14.png |
A15 | line | Band over its own line | boundary vs fill | 2 | 3 | 900 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A15.png | truth/A15.truth.json | spec/A15.spec.json | images/A15.png |
A16 | line | Iso-luminant palette | greyscale-based separation | 2 | 6 | 1,800 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A16.png | truth/A16.truth.json | spec/A16.spec.json | images/A16.png |
A17 | line | Gridline collision | furniture vs mark | 2 | 2 | 800 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A17.png | truth/A17.truth.json | spec/A17.spec.json | images/A17.png |
A18 | line | Abbreviated irregular time axis | tick coverage | 2 | 1 | 9 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/A18.png | truth/A18.truth.json | spec/A18.spec.json | images/A18.png |
B01 | scatter | Overplotted cloud | recall under merging | 2 | 1 | 5,000 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/B01.png | truth/B01.truth.json | spec/B01.spec.json | images/B01.png |
B02 | scatter | Bubble with two channels | the size channel surviving | 2 | 1 | 60 | {
"panel0": {
"x": "log",
"y": "linear"
}
} | images/B02.png | truth/B02.truth.json | spec/B02.spec.json | images/B02.png |
B03 | scatter | Shape-coded groups | grouping carried by shape | 2 | 4 | 160 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/B03.png | truth/B03.truth.json | spec/B03.spec.json | images/B03.png |
B04 | scatter | Jittered categorical strips | jitter read as data | 2 | 6 | 270 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/B04.png | truth/B04.truth.json | spec/B04.spec.json | images/B04.png |
B05 | scatter | Log–log decades | two scale calls at once | 2 | 1 | 220 | {
"panel0": {
"x": "log",
"y": "log"
}
} | images/B05.png | truth/B05.truth.json | spec/B05.spec.json | images/B05.png |
B06 | scatter | Fit line with envelope | three marks, one hue | 2 | 4 | 690 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/B06.png | truth/B06.truth.json | spec/B06.spec.json | images/B06.png |
B07 | scatter | Two-dimensional error bars | recording uncertainty | 2 | 1 | 14 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/B07.png | truth/B07.truth.json | spec/B07.spec.json | images/B07.png |
B08 | scatter | Touching markers | blob splitting | 2 | 1 | 120 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/B08.png | truth/B08.truth.json | spec/B08.spec.json | images/B08.png |
B09 | scatter | Far outliers | outliers kept, not trimmed | 2 | 1 | 124 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/B09.png | truth/B09.truth.json | spec/B09.spec.json | images/B09.png |
B10 | scatter | Connected scatter | ordering is data | 2 | 1 | 40 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/B10.png | truth/B10.truth.json | spec/B10.spec.json | images/B10.png |
B11 | scatter | Scatter with marginals | panel decomposition | 2 | 1 | 400 | {
"panel0": {
"x": "linear",
"y": "linear"
},
"panel1": {
"x": "linear",
"y": "linear"
},
"panel2": {
"x": "linear",
"y": "linear"
}
} | images/B11.png | truth/B11.truth.json | spec/B11.spec.json | images/B11.png |
C01 | area | Hundred-per-cent stack | cumulative vs segment, stack order | 2 | 5 | 1,300 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/C01.png | truth/C01.truth.json | spec/C01.spec.json | images/C01.png |
C02 | area | Streamgraph with wiggle baseline | a computed baseline | 2 | 4 | 1,200 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/C02.png | truth/C02.truth.json | spec/C02.spec.json | images/C02.png |
C03 | area | Overlapping translucent areas | composited colours | 2 | 3 | 900 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/C03.png | truth/C03.truth.json | spec/C03.spec.json | images/C03.png |
C04 | area | Signed area across zero | the datum edge flips | 2 | 1 | 400 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/C04.png | truth/C04.truth.json | spec/C04.spec.json | images/C04.png |
C05 | area | Ridgeline with overlap | panels that must not stack | 2 | 8 | 2,400 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/C05.png | truth/C05.truth.json | spec/C05.spec.json | images/C05.png |
C06 | area | Band between two curves | both boundaries recorded | 2 | 2 | 600 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/C06.png | truth/C06.truth.json | spec/C06.spec.json | images/C06.png |
C07 | area | Shaded integral region | hatching is annotation | 2 | 1 | 400 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/C07.png | truth/C07.truth.json | spec/C07.spec.json | images/C07.png |
C08 | area | Sliver layer | a thin layer surviving | 2 | 4 | 1,200 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/C08.png | truth/C08.truth.json | spec/C08.spec.json | images/C08.png |
C09 | area | Area on a log axis | fill area ≠ magnitude | 2 | 1 | 300 | {
"panel0": {
"x": "linear",
"y": "log"
}
} | images/C09.png | truth/C09.truth.json | spec/C09.spec.json | images/C09.png |
D01 | compound | Pressure–volume indicator diagram | a non-functional closed curve | 2 | 1 | 400 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/D01.png | truth/D01.truth.json | spec/D01.spec.json | images/D01.png |
D02 | compound | Psychrometric chart | a non-Cartesian frame | 2 | 5 | 1,000 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/D02.png | truth/D02.truth.json | spec/D02.spec.json | images/D02.png |
D03 | compound | Pump curve against system curve | preserving an intersection | 2 | 3 | 601 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/D03.png | truth/D03.truth.json | spec/D03.spec.json | images/D03.png |
D04 | compound | Bode plot | linked panels, shared log frequency | 2 | 2 | 800 | {
"panel0": {
"x": "log",
"y": "linear"
},
"panel1": {
"x": "log",
"y": "linear"
}
} | images/D04.png | truth/D04.truth.json | spec/D04.spec.json | images/D04.png |
D05 | compound | Nyquist plot | the variable lives on the curve | 2 | 1 | 500 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/D05.png | truth/D05.truth.json | spec/D05.spec.json | images/D05.png |
D06 | compound | Phase diagram | boundaries meeting at a triple point | 2 | 4 | 401 | {
"panel0": {
"x": "linear",
"y": "log"
}
} | images/D06.png | truth/D06.truth.json | spec/D06.spec.json | images/D06.png |
D07 | compound | Titration curve with derivative | one series derived from the other | 2 | 3 | 801 | {
"panel0": {
"x": "linear",
"y": "linear"
},
"panel1": {
"x": "linear",
"y": "linear"
}
} | images/D07.png | truth/D07.truth.json | spec/D07.spec.json | images/D07.png |
D08 | compound | Stress–strain curve | construction lines that look like data | 2 | 2 | 409 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/D08.png | truth/D08.truth.json | spec/D08.spec.json | images/D08.png |
D09 | compound | Moody diagram | a converging family of curves | 2 | 11 | 3,300 | {
"panel0": {
"x": "log",
"y": "log"
}
} | images/D09.png | truth/D09.truth.json | spec/D09.spec.json | images/D09.png |
D10 | compound | Smith chart | a curvilinear coordinate system | 2 | 1 | 60 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/D10.png | truth/D10.truth.json | spec/D10.spec.json | images/D10.png |
D11 | compound | Bars with an overlaid rate line | three geometries, two axes | 2 | 3 | 40 | {
"panel0": {
"x": "linear",
"y": "linear"
},
"panel1": {
"x": "linear",
"y": "linear"
}
} | images/D11.png | truth/D11.truth.json | spec/D11.spec.json | images/D11.png |
D12 | compound | Candlestick with volume | colour encodes direction, not identity | 2 | 7 | 420 | {
"panel0": {
"x": "linear",
"y": "linear"
},
"panel1": {
"x": "linear",
"y": "linear"
}
} | images/D12.png | truth/D12.truth.json | spec/D12.spec.json | images/D12.png |
M01 | market | Three Funds Against the Benchmark | identity through a scheduled crossing: BLC x TGR at 30% and 73%, RSP punching up through both at ~50% and ~90% | 3 | 3 | 756 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/M01.png | truth/M01.truth.json | spec/M01.spec.json | images/M01.png |
M02 | market | Fund and Its Index Tracker | two series that are meant to be identical — reporting them as two, or saying plainly that they could not be separated | 3 | 3 | 756 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/M02.png | truth/M02.truth.json | spec/M02.spec.json | images/M02.png |
M03 | market | One Breakaway, Four in the Pack | a 4x breakaway sets the y range, compressing four crossing series into the bottom fifth of the plot | 3 | 5 | 1,260 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/M03.png | truth/M03.truth.json | spec/M03.spec.json | images/M03.png |
M04 | market | Sector Constituents, One Year | series count and identity at ten, with the crossings clustered at two shared drawdowns rather than spread out | 3 | 10 | 2,520 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/M04.png | truth/M04.truth.json | spec/M04.spec.json | images/M04.png |
M05 | market | Sector Rotation: Leadership Changes Hands | ordering — the answer is a sequence of rank changes, not five curves | 3 | 5 | 1,260 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/M05.png | truth/M05.truth.json | spec/M05.spec.json | images/M05.png |
M06 | market | Four Funds Hugging the Benchmark | accumulated identity error: no single crossing is hard, there are simply a great many between series that look alike by construction | 3 | 5 | 1,260 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/M06.png | truth/M06.truth.json | spec/M06.spec.json | images/M06.png |
M07 | market | Shared Crash, Different Recoveries | behaviour through a shared event — every series turns in the same session, so direction cannot disambiguate the crossings | 3 | 4 | 1,008 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/M07.png | truth/M07.truth.json | spec/M07.spec.json | images/M07.png |
M08 | market | High Beta Against Low Beta | crossings as events with a common cause: all three turn at every market turn, so every crossing happens where the cue is shared | 3 | 3 | 756 | {
"panel0": {
"x": "linear",
"y": "linear"
}
} | images/M08.png | truth/M08.truth.json | spec/M08.spec.json | images/M08.png |
ChartParse-Bench
58 synthetic charts whose exact values were written before the pixels existed.
Every gate in chart extraction measures self-consistency: redraw the extraction, check it lands on the same ink, call it verified. That passes a series traced confidently off the wrong axis. Because each chart here ships the array it was drawn from, the question stops being is this answer plausible and becomes is this answer right.
Contents
| family | charts | series | what it isolates |
|---|---|---|---|
| A — line | 18 | 62 | identity through crossings, resampling honesty, axis binding |
| B — scatter | 11 | 22 | recall under overplotting, size/shape channels, outliers |
| C — area | 9 | 29 | stacking, computed baselines, composited fills |
| D — compound | 12 | 43 | non-Cartesian frames, linked panels, mixed geometries |
| M — market | 8 | 38 | correlated series that all turn in the same session |
| total | 58 | 194 | 77,929 ground-truth points |
1–12 series per chart. 8 panels on log axes (3 log-y, 3 log-x, 2 log-log).
Each chart isolates one named failure, declared in its spec before it is drawn — A01 "colour-based tracing that swaps identity at a crossing", A14 "gap honesty", C07 "hatching is annotation", D10 "a curvilinear coordinate system", M06 "accumulated identity error: no single crossing is hard, there are simply a great many".
Files
images/<ID>.png the figure, as an extractor receives it
truth/<ID>.truth.json the exact values used to draw it, plus axis_scales and
`printed` flags on labels that actually appear
spec/<ID>.spec.json what the chart tests, its difficulty level, and for the
M set the measured crossing schedule
metadata.jsonl one row per chart
index.json the same rows as a single array
Suggested metric
score = series_recall x coverage x accuracy x mark_recall
Kept as a product, not an average: recovering one of eight series perfectly must not read as a good parse. Three properties matter more than the formula:
- Pair series by shape, not by name (Hungarian assignment), so a system cannot win on labelling.
- Error relative to each series' own range, and compared in log10 space on a log axis — otherwise a four-decade residual is dominated entirely by the top decade.
- Compare nothing where the extractor declared a gap. A declared absence is neither credited as coverage nor bridged and charged as error. Saying "I could not see this" has to be allowed to be the right answer.
Use RMS rather than median: a median hid a log-axis magnitude error at 12% where RMS showed 22%.
Known limitations
- C05 is drawn with no numeric y ticks, so its density values are not recoverable from the image at all. It tests shape only.
- A14 and C03 store values across a span wider than the figure can show (tails decaying to ~4e-6 of the peak). A coverage measure taken against the stored array rather than against visible ink will understate a correct parse on those two.
- D10 is a Smith chart stored in reflection coefficient. A system answering in normalised impedance is not wrong; the conversion is arithmetic an extractor should not be expected to perform.
- Synthetic throughout. These charts are matplotlib output, so they carry none of the compression artefacts, rotated scans, or hand-drawn annotation of a real document. They measure numeric correctness, not robustness to bad inputs.
- Downloads last month
- 22