Column labels, left to right.
Row labels, top row first.
points[row][col], rows top-first, aligned with y and x.
A cell the chart drew no value at is null, or any non-finite number --
the same spelling BarPoint uses for a gap, and read through the
same toBarValue. It is not 0: a grid is a rectangle, and an
adapter whose data does not fill it had no way to say so, so three of
them filled the holes with zeros and announced a value the chart never
drew (#1191). Measured on Highcharts 13.0.1, a 3x2 heatmap omitting one
cell and the same heatmap stating that cell as 0 produced byte-identical
payloads, while the first drew five cells and the second six.
A calendar heat map is the case that makes it unavoidable rather than merely wrong: Google draws every day of every year its data spans, so a two-year chart of ten records is 731 cells with 721 holes.
Data structure for heatmap charts with x/y labels and 2D point values.
Rows run top-first:
y[0]names the row drawn at the top of the chart andpoints[0]holds it, so the two arrays read the way a sighted reader reads the grid. Heatmap turns both over on construction, so that its own row 0 is the bottom of the drawn grid and ↑, which increments the row index, moves visually upward.Stated here because it cannot be recovered from the payload: a matrix of numbers looks the same either way up, so a layer written bottom-first is not wrong in any way the core could notice. It loads, it navigates, and every value is still announced against its own label -- both arrays having been reversed together -- while ↑ walks down the chart and the cursor enters at the top instead of the bottom. A reader who then reports what the top row contains has it exactly backwards (#971).
Producers therefore have to know which way their own library counts. matplotlib's array is top-first and needs nothing; plotly numbers a heatmap's rows from the bottom and its adapter turns them over.