--- name: chart-type-and-library-selection description: Picks the chart form that an already prepared result and its shape call for, then the rendering library the destination calls for, rather than the one that was easiest to reach. Use when it is not obvious which kind of plot a result needs, when choosing between a bar, line, scatter, area or pie, when a value has to be plotted over time, when a relationship between measures calls for a scatter rather than bars, when a result has more categories or a longer tail than the chosen form can carry, when a series has gaps or points that are not in order, or when deciding what to render a plot with for a report, a slide or a web page. --- # Chart type and library selection ## Overview A chart is a claim rendered as a shape, and the shape decides how accurately anyone can read it. That is not a matter of taste: the accuracy with which people judge encodings has a known order, and it is the same order every time. **Position along a common scale** is read most accurately, then **position on separate scales**, then **length**, then **angle and slope**, then **area**, and last **colour and volume**. Every chart choice is a decision about how far down that list to push the number that matters. A pie chart asks the reader to compare angles. A bar chart asks them to compare positions along a shared axis. The data does not change; the accuracy of reading it does. ``` question → what is being compared → the encoding that reads most accurately → the form │ destination and volume → the library ``` ## When to Use Use this skill whenever a result is about to become a picture, and whenever an existing chart is being reused for new data whose shape has changed underneath it. Use it when a chart is failing in a way people describe as cosmetic: too many slices, categories too small to label, a line that looks smooth across a period with no data. Do **not** use it to decide what colours the chart uses or whether it can be read on both backgrounds, which is the `dashboard-color-and-visual-hierarchy` skill, nor how several charts sit together on a page, which is the `dashboard-layout-by-widget-mix` skill. What the chart is allowed to claim belongs to the `insight-narrative-integrity` skill, and whether the data underneath was cleaned belongs to `etl-before-dashboard`. ## Core Process ### Step 1: Say what is being compared, in one sentence Before choosing anything: what does the reader need to compare, and how precisely? "How the five channels rank by revenue" and "whether revenue and units move together" are different questions and no single form answers both. Write the sentence down, one per chart, and keep it next to the chart in whatever you hand back. A chart chosen without it is a chart chosen by habit, and a list of the forms you picked is not the same thing: the forms say what you did, the sentences say what each chart is for, and the second is what somebody checks the chart against later. ### Step 2: Let the data's shape rule out forms before preference chooses one Measure, then choose. These are disqualifications, not preferences: | Shape of the data | What it rules out | | --- | --- | | More than about five categories | A pie or donut. Angle comparison collapses, and the slices become too thin to label. | | A long tail, where a few categories hold most of the total | Any form that gives every category equal visual weight. Sort the bars and say how much the tail holds. | | Two continuous variables | A bar chart. The question is a relationship, so it needs two positional axes. | | A time series with gaps | A line drawn straight through the gap. A line asserts continuity, so a missing period must break it or be marked. | | Points that are not ordered by the axis variable | A line at all. A line between unordered points draws a path that does not exist. | | Parts that do not sum to the whole | A stacked form. Stacking claims the segments compose a total. | | Many overlapping points | A plain scatter without transparency, sampling or binning; the dense region is unreadable and its density is the finding. | ### Step 3: Choose the form that puts the important comparison highest on the accuracy order Once the disqualified forms are gone, prefer the one that encodes the key comparison as position, then length, and only then anything else. - **Ranking or comparing amounts across categories:** horizontal bars, sorted by value. Labels fit, and length on a common baseline reads accurately. The baseline starts at zero, because length is the encoding and a truncated axis multiplies the apparent difference. - **A quantity over time:** a line, with the axis in time order and gaps left broken. - **A relationship between two quantities:** a scatter, with the dependent variable on the vertical axis. - **A distribution:** a histogram or a box plot; a mean and a bar hide the shape entirely, and the shape was the question. - **Composition, when it genuinely sums to a whole and has few parts:** a stacked bar rather than a pie, because a shared baseline is still position. - **A matrix of two categorical dimensions:** a heatmap, accepting that colour is last on the accuracy order, so the cells carry their values as text when they must be read exactly. State which comparison you optimised for, because that is the decision. **When the requested form is one the shape rules out, build the one that works, not just a recommendation of it.** An alternative that is described and not produced leaves the person holding only the version that misleads, and an artefact beats a suggestion every time. If the requested form is produced as well, it carries on its face what it costs the reader. Collapsing the data until it fits the requested form is not a compromise either: it answers a different question than the one that was asked. ### Step 4: Decide what the reader must be able to do, before deciding what draws it The form is decided by the data. What the reader can do with the result is decided by who they are, and it eliminates more libraries than the form does, so it comes first. Answer these four, in order, and write the answers down: 1. **Does the reader answer one question here, or the same question about different slices?** "One question" is the answer that removes the most work, which is why it is the one that gets given, so it has to be demonstrated rather than asserted. Read the table the page is built on: **a table with a time dimension and two or more categorical dimensions is not one question.** To call it one anyway, write the question down and say, for each of those dimensions, why no reader would want to hold it fixed while reading the rest. If that cannot be said, it is several slices, and filtering is a commitment, not a feature. Controls on a page that really does answer one question are furniture; declaring one question to avoid the controls is the same page with the work left out. 2. **If there is filtering, does it apply to every panel that shares the grain?** A control that filters one panel while its neighbours keep showing everything puts two different populations side by side with nothing saying they differ. That is worse than no filter, because the page now reports a comparison nobody made. Linked filtering is the default; a panel deliberately left unlinked says so on its face. 3. **Can the reader see what is currently applied, and clear it?** A filter with no visible state is a page that answers a narrowed question in the voice of the whole dataset. The active selection is displayed, and clearing it is one action. 4. **Does a view have to survive being handed to someone else?** If two people must discuss the same slice, the state belongs somewhere linkable; otherwise the only way to share it is a screenshot, which carries no numbers and no filter. And if new data will arrive, the deliverable is the pipeline that regenerates the page, not the page. Only then the library, decided by where the chart has to work: - **What renders it:** a notebook, a static report, a web page, a printed document. A library that produces an interactive widget is a broken image in a PDF. - **What the answers above require.** A library with no linked-view mechanism turns question 2 into a hand-written graph of event handlers, which is where cross-filtering quietly becomes one filtered panel and five stale ones. - **How many points it has to draw.** Above roughly tens of thousands of marks, a library that emits one element per point in a document becomes unusable in the browser; that is the point where aggregation, binning, or a canvas-based renderer stops being optional. - **Whether interaction carries meaning or decoration.** Tooltips that hold the exact values are meaning. Animation on load is decoration, and it costs the reader time on every visit. - **What the project already has.** A second charting library is a dependency, a second visual language and a second set of defaults to override. Adding one is a decision worth stating. Name what the reader must be able to do, then the destination, then the library, then why. "It is what I know" is the answer this step exists to replace. Then name what was ruled out. Step 2 does not allow a chart form to be chosen without stating the forms the data disqualified, and a library is chosen the same way: write down at least one other library that could plausibly have rendered this, and the specific thing that ruled it out — a destination it cannot reach, an interaction it has no mechanism for, a point count it cannot draw. A library that appears alone was not chosen. It was reached for, and the most common thing within reach is whatever code was in front of you. #### One instantiation of that shape: linked filtering in one file, with no build step **The code below is one way to satisfy the six points, not a recommendation of the library in it.** Several libraries can do all six. This one is named so the shape is concrete, and lifting the name out of it instead of running the decision above is precisely the shortcut this step exists to prevent: a library that arrives by being the only concrete example anybody saw is a library nobody chose. Question 2 is the one that gets abandoned, because "make the panels filter each other" sounds like it needs a framework. It does not, and reaching for one has a cost worth naming: a bundler puts a compile step between the code and the artefact, and an agent with no live preview has to verify that step blind. A script that writes a file either wrote it or did not. The shape is six things in one document: 1. **One load of the library, for every panel.** Not one per chart. Five files each embedding their own copy is the same page five times over, and the `dashboard-layout-by-widget-mix` skill refuses it. 2. **The rows embedded once**, with each panel a view computed from them rather than a separate extract. Two panels aggregating from two extracts is how they come to disagree. 3. **One map from category to colour**, read by every panel, so a category is the same colour wherever it appears. The `dashboard-color-and-visual-hierarchy` skill owns why. 4. **One redraw that recomputes every panel from the current filter**, so no panel can be stale. 5. **The active filter visible, clearable in one action, and in the URL** so a filtered view can be handed to somebody. 6. **Something on the page that says the panel can be clicked**, and the clicked mark showing that it is the selected one. A filter nobody knows is there is a feature that exists only in the source: the reader sees a static chart, and the one affordance a mouse gives them is a cursor that never changes shape. ```html
Click a channel to filter every panel. Click it again to clear.
``` `react` takes new data and diffs it against what is drawn, which is what a changed aggregation needs; `restyle` changes attributes of traces already drawn, which is why the selected bar is highlighted with it rather than by redrawing the panel that was just clicked. Every panel that shares the grain goes inside `draw`. A panel left outside it is the failure question 2 describes: two populations on one screen, with nothing saying they differ. Those two calls belong to the library in the example. Every library that can satisfy point 4 has an equivalent pair — one that replaces a panel's data, one that changes the appearance of marks already drawn — and that pair is the thing to look for when the decision above lands somewhere else. If a candidate has no way to change a drawn mark without redrawing the panel, that is a disqualification worth writing down, which is what the step above is asking for. ### Step 5: Label so the chart survives being separated from its context Every chart travels: it gets screenshotted, pasted, and shown without whatever was said next to it. So the chart itself carries the units, the period, the filter that produced it, and the table it was built from. If a period is missing from the data, the chart says so on its face rather than in a caption that will not travel with it. ## Common Rationalizations | Excuse | Reality | | --- | --- | | "A pie chart is more intuitive for shares." | It is more familiar. Reading it means comparing angles, which is measurably less accurate than comparing lengths, and it degrades fast past a few slices. | | "Starting the axis at zero wastes space." | The length is the encoding. Cutting the baseline changes the ratio the reader sees, which is the one thing the chart was for. | | "The line looks better without the break." | It looks better because it is asserting data that does not exist. | | "There are only twelve categories, a pie is fine." | Twelve slices means twelve angles and twelve labels that do not fit. Sorted bars cost nothing and read exactly. | | "The library is the one I always use." | Then the destination never entered the decision, and the chart will be checked against it later by somebody else. | | "Interactivity makes it richer." | Interaction that carries values is useful. Interaction that carries motion is a delay the reader pays for every time. | | "The charts are interactive, so the page is." | Zooming one panel is not filtering the page. If selecting a category does not move the other panels, the reader has to hold the filter in their head while reading numbers that ignore it. | | "The page answers one question, so it needs no controls." | That is the conclusion, not the evidence for it. A table carrying a period and two categories offers a reader at least three things to hold fixed, and a page that names none of them has asserted the cheap answer rather than tested it. | | "The skill showed this library, so this is the library." | The example is there to make the shape concrete, and it says so. A form gets chosen by naming what the data ruled out; a library gets chosen the same way. A name lifted out of a code block is a decision nobody made. | | "Filters can be added later; the charts are the hard part." | Filtering decides which library can do the job, so adding it later means rewriting the charts. It is the first decision, not the last. | | "Linked filtering needs a framework." | It needs one library load, the rows in one place, and one redraw. A build step between the code and the artefact is a compile that has to be verified blind by anything without a live preview. | | "The charts are clickable, so the filter is discoverable." | Nothing about a chart says it is clickable. The fix is a control that is one — a real button carries its own affordance, its own keyboard and its own state — not a line of text explaining how to work the page. A page that needs a manual has the wrong control. | | "A dashboard is a set of charts on a page." | That is a report. A dashboard is a page someone interrogates; if there is nothing to interrogate it with, build the report deliberately and say that is what it is. | | "It is just an exploratory chart." | Exploratory charts are the ones that get pasted into messages, where nothing says they were exploratory. | ## Red Flags Stop when any of these is true: - A pie or donut has more than five slices. - A bar chart's value axis does not start at zero. - A line crosses a period for which there is no data. - A line joins points that are not ordered by the axis variable. - A stacked chart's segments do not sum to something meaningful. - A chart is being built and the sentence describing what is being compared was never written. - The page is declared to answer one question, and the table behind it has a date column and two or more categorical columns, with nothing said about why neither is a slice. - The library was chosen before the destination was named. - A library is named in the answer and nothing says what else could have rendered this, or what ruled that alternative out. - The library in the answer is the one the example in this skill happens to use, and the reasons given are the example's rather than this page's. - A chart carries no units, no period and no source table. - A panel filters the page and the only way to discover that is a sentence telling the reader to click. Instructions for working the page are a symptom: the control should say what it is by being one. - Each chart was written to its own file. Panels in separate files cannot filter each other, cannot be compared side by side, and each carries its own copy of the library. - A second charting library appears in the project's declared dependencies and nothing states what distinguishes it from the first. Two libraries are two visual languages, two sets of defaults and two ways the same bar ends up a different colour; having two is allowed, arriving at two without deciding is not. `scripts/verify-output.sh --project .` reads the dependency declarations and fails on an unexplained second one. ## Verification - [ ] The comparison the chart is for is written down in one sentence, before the form was chosen. - [ ] The forms ruled out by the shape of the data are named, with the measurement that ruled them out, such as the category count or the presence of gaps. - [ ] The chosen form encodes the key comparison as position or length, or the reason for going lower on the accuracy order is stated. - [ ] Any bar chart's value axis starts at zero and any time axis is in order with gaps broken. - [ ] What the reader must be able to do is written down before any library was named, and any filtering is stated as linked across the panels that share a grain, or the exceptions are named. - [ ] If the page is declared to answer one question, the dimensions of the table behind it are listed, each with the reason no reader would hold it fixed. A time dimension plus two or more categorical ones is several slices until that is written down. - [ ] At least one other library that could have rendered this is named, with the specific thing that ruled it out, in the same form Step 2 requires for the chart's shape. - [ ] Where filtering exists, the active selection is visible and can be cleared in one action, the filter is reachable as a real control rather than only as a mark to click, and the current selection is shown on that control rather than explained in a sentence. - [ ] Every panel reads the same category-to-colour map, so a category is one colour on the page. - [ ] The library is named together with the destination and the interaction it has to support, and any new dependency is stated as one. - [ ] The chart carries its units, its period, the filter behind it and the table it came from.