Lesson 08 / 18
Accessible Charts
When eight series are encoded with a default palette made of two hue families and four lightness steps, all eight are distinguished by the full decoder, while a decoder that does not read the hue distinction is left with only four. When the shape channel is distributed in sequence, the number stays at four, because the shapes start over after four series and the color collision also occurs four series apart; when the shape is distributed by collision, it rises to eight. A legend box asks for 16 transitions, an ordered legend 6, direct labeling 0. Of nine lightness steps, five are distinguished at an interval of two, and with four shapes the ceiling of a single display comes out as 20 series.
Published Updated
In this lesson
- Encodes color as two numbers made of hue family and lightness step and counts the number of series that can be distinguished with two separate decoders
- Shows, by comparing two shape distributions, that the axis on which redundant encoding is added determines the result
- Counts the number of transitions required by the legend box, ordered legend and direct labeling layouts
- Computes the largest number of series that can be carried in a single display from the number of distinguishable steps and shapes
Contents
Over seven lessons what was measured was the number the channel carries, and each time it was assumed that the reader can read the channel. This lesson measures that assumption. If there is more than one series in a display, the first question is not whether the values of the series are read correctly; the first question is which series is which. If a series cannot be distinguished from its neighbor, its value is also counted as not reported.
The contrast ratio calculation, color systems and accessibility criteria were established in the Fundamentals of Interface Design course and in the accessibility courses; none of them is repeated here and no color system name is written. The question here is specific to the data and is measured by a single number: the number of distinguishable series. When color is the only encoding channel, what is this number, and to what does redundant encoding raise it.
- CD43. Color is not a name but two numbers. In the model, color is made of a hue family and a lightness step. The encoder assigns a pair to a series; no color name or system is mentioned.
- CD44. The reading resolution is two steps. Two lightness steps cannot be distinguished unless they differ by at least two steps, because in the display the series are thin lines and stand far from one another; this is not the distinction of two wide surfaces placed side by side.
- CD45. Two decoders run. The full decoder reads both the hue family and the lightness step. The reduced decoder does not read the hue distinction and sees only the lightness step; both are applied to the same encoding.
- CD46. The number of distinguishable series is the number of clusters. Series that cannot be told apart from one another are gathered into a single cluster; the number of series read back is the number of clusters and is smaller than the total of the series.
- CD47. The place of redundant encoding determines the result. Adding a second channel is not enough by itself; the channel has to change on the axis on which the color collides.
- CD48. A single display has a ceiling. The ceiling is the product of the number of distinguishable steps and the number of shapes; above it, the number of series cannot be read back without splitting the display.
When Color Is the Only Channel
# series.py — MODEL: the measurement network of K03-K05 is generated # with the same seed. The eight series are subsets of the same 3,199 readings: # five regions and three tariff tiers. Color is not a name but two numbers: # hue family and lightness step. import math SEED, RAW, M32 = 20260218, 1400, 0xFFFFFFFF PERIODS = ["2026-01", "2026-02", "2026-03"] TARIFF = [10, 25, 40] REGION = [("north", 0.28, 21), ("south", 0.22, 17), ("east", 0.18, 26), ("west", 0.14, 14), ("central", 0.18, 23)] def rng(t): x = ((t ^ (t >> 16)) * 2246822507) & M32 x = ((x ^ (x >> 13)) * 3266489909) & M32 s = [(x ^ (x >> 16)) & M32] def next_value(): s[0] = (s[0] * 1664525 + 1013904223) & M32 return s[0] / 4294967296 return next_value def pick(u, w): t = 0.0 for i, x in enumerate(w): t += x if u < t: return i return len(w) - 1 _kept, _dropped = [], [] for i in range(RAW): r = rng(SEED + i) b = REGION[pick(r(), [x[1] for x in REGION])] pick(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05]) pick(r(), [0.08, 0.14, 0.27, 0.34, 0.17]) (_dropped if r() < 0.046 else _kept).append( {"subscriber_id": 10001 + i, "region": b[0], "base": b[2]}) VALID = {a["subscriber_id"] for a in _kept} READINGS = [] for a in _kept + _dropped: r = rng(SEED + 7000 + a["subscriber_id"]) for d in range(pick(r(), [0.05, 0.12, 0.21, 0.62])): t = 0.0 if r() < 0.038 else math.floor( a["base"] * math.exp((r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100 + 0.5) / 100 if a["subscriber_id"] not in VALID: r() continue READINGS.append({"subscriber_id": a["subscriber_id"], "region": a["region"], "period": PERIODS[d], "consumption_m3": t}) r() def tier(v): return 1 + sum(1 for t in TARIFF if v >= t) def average(f): v = [o["consumption_m3"] for o in READINGS if f(o)] return sum(v) / len(v), len(v) SERIES = [] for name, _, _ in REGION: m, n = average(lambda o, x=name: o["region"] == x) SERIES.append((name, m, n)) for tr in (2, 3, 4): m, n = average(lambda o, x=tr: tier(o["consumption_m3"]) == x) SERIES.append((f"tier{tr}", m, n)) # Color channel: hue family (0/1) and lightness step (0-8). The palette picks # four steps from each of two families; this is a natural default distribution # for eight series. LEVELS, SEPARATION, SHAPES = 9, 2, 4 PALETTE = [(i // 4, [1, 3, 5, 7][i % 4]) for i in range(len(SERIES))] def full_decoder(x, y): """Are two series distinguishable: the hue differs, or the step difference is SEPARATION.""" return x[0] != y[0] or abs(x[1] - y[1]) >= SEPARATION def reduced_decoder(x, y): """The decoder that does not read the hue distinction sees only the lightness step.""" return abs(x[1] - y[1]) >= SEPARATION def distinguishable(code, decoder): """Series that cannot be told apart form one cluster; the number of clusters is the series read back.""" root = list(range(len(code))) def find(i): while root[i] != i: root[i] = root[root[i]] i = root[i] return i for i in range(len(code)): for j in range(i + 1, len(code)): if not decoder(code[i], code[j]): root[find(i)] = find(j) return len({find(i) for i in range(len(code))}) print(f"{len(SERIES)} series, all subsets of the same {len(READINGS)} readings") print(f"{'series':11s} {'count':>6s} {'mean':>9s} {'hue':>4s} {'light':>8s}") for (name, m, n), (t, light) in zip(SERIES, PALETTE): print(f"{name:11s} {n:6d} {m:9.3f} {t:4d} {light:8d}") print(f"reading resolution: two lightness steps cannot be distinguished unless " f"they differ by at least {SEPARATION} steps") print(f"distinguishable series with the full decoder: " f"{distinguishable(PALETTE, full_decoder)}/{len(SERIES)}") print(f"with the decoder that does not read the hue distinction: " f"{distinguishable(PALETTE, reduced_decoder)}/{len(SERIES)}")
8 series, all subsets of the same 3199 readings series count mean hue light north 921 20.210 0 1 south 645 16.602 0 3 east 557 24.674 0 5 west 476 13.778 0 7 central 600 22.396 1 1 tier2 2209 17.506 1 3 tier3 692 30.009 1 5 tier4 58 44.171 1 7 reading resolution: two lightness steps cannot be distinguished unless they differ by at least 2 steps distinguishable series with the full decoder: 8/8 with the decoder that does not read the hue distinction: 4/8
The palette looks flawless. Each of the eight series has received a separate color pair, no pair has been repeated, and the full decoder distinguishes all eight. This is the result seen by the person who chose the palette.
The last line says what happens to the same palette under the second decoder: 4 of 8 series are left. North and central carry the same lightness step, south and the second tariff tier are the same, east and the third are the same, west and the fourth are the same. Four pairs of series fall to a single series each, and which member of each pair is being read remains unclear. The report thinks it shows eight subsets of the 3,199 readings, while it shows four.
This is not a palette flaw, it is a flaw of structure. The palette crossed two hue families with four lightness steps and filled eight cells; when the hue family cannot be read, the cross table is left with columns and no rows. If color is the only encoding channel, this result is unavoidable, and increasing the number of colors in the palette does not help.
Where Redundant Encoding Is Added
# redundant.py — the shape channel is added. Where it is added determines the result. SEQUENTIAL = [(p[0], p[1], i % SHAPES) for i, p in enumerate(PALETTE)] BY_COLLISION = [(p[0], p[1], i // SHAPES) for i, p in enumerate(PALETTE)] def shaped(x, y): return reduced_decoder(x, y) or x[2] != y[2] print(f"{'layer':29s}{'full decoder':>13s}{'reduced decoder':>17s}") for name, code, dec in (("color only", PALETTE, reduced_decoder), ("color + shape (sequential)", SEQUENTIAL, shaped), ("color + shape (by collision)", BY_COLLISION, shaped)): full = distinguishable([k[:2] for k in code], full_decoder) print(f"{name:28s} {full:12d} {distinguishable(code, dec):16d}") print(f"the shapes distributed sequentially number {SHAPES} and start over at the " f"{SHAPES}th series; since the color collision is also {SHAPES} series apart, " f"two collisions overlap")
layer full decoder reduced decoder color only 8 4 color + shape (sequential) 8 4 color + shape (by collision) 8 8 the shapes distributed sequentially number 4 and start over at the 4th series; since the color collision is also 4 series apart, two collisions overlap
The middle line is the most expensive finding of this lesson. The shape channel was added, four separate shapes were distributed to the series in sequence, and the number read by the narrowed decoder stayed at four. Redundant encoding was added, but nothing improved.
The reason is in the last line. There are four shapes and they start over at the fifth series; the color collision of the palette is also exactly four series apart. When the two cycles overlap, the same colliding pairs this time share both the same step and the same shape. The second channel collides together with the first, in the same place.
The third line gives the correction: when the shape is distributed not by order but by collision — that is, when the first four series get one shape and the next four series get another — the narrowed decoder also distinguishes all eight. The number of shapes used fell from four to two, and the result improved. The measure of redundant encoding is not the number of channels but where the channel changes.
Legend Box and Transition Count
# transitions.py — the legend box sends the reader back and forth between the # display and the box. The right-edge position is a third channel and carries the order. ROWS = 12 lowest, highest = min(m for _, m, _ in SERIES), max(m for _, m, _ in SERIES) position = [round((m - lowest) / (highest - lowest) * (ROWS - 1)) for _, m, _ in SERIES] overlapping = sum(1 for i in range(len(SERIES)) for j in range(i + 1, len(SERIES)) if position[i] == position[j]) unique = [i for i in range(len(SERIES)) if all(position[i] != position[j] for j in range(len(SERIES)) if j != i)] print(f"right-edge positions ({ROWS} rows): {position}") print(f"of {len(SERIES)} series, {len(unique)} have a unique position, " f"{overlapping} pair falls on the same row") box = 2 * len(SERIES) ordered = 2 + 2 * (len(SERIES) - len(unique)) print(f"{'layout':27s}{'transitions':>14s}{'distinguishable series':>23s}") for name, g, s in (("legend box", box, distinguishable(BY_COLLISION, shaped)), ("ordered legend box", ordered, distinguishable(BY_COLLISION, shaped)), ("direct labeling", 0, len(SERIES))): print(f"{name:26s} {g:13d} {s:22d}")
right-edge positions (12 rows): [2, 1, 4, 0, 3, 1, 6, 11] of 8 series, 6 have a unique position, 1 pair falls on the same row layout transitions distinguishable series legend box 16 8 ordered legend box 6 8 direct labeling 0 8
Distinguishability is a necessary condition, not a sufficient one. The eight series may be told apart from one another and the reader still does not know which one is which region; this they learn only by looking at the legend box. The measure here is the number of transitions: how many times the gaze goes back and forth between the display and the legend box.
An ordinary legend box asks for one trip there and one trip back for each series; eight series come to 16 transitions. When the order in the legend box is made the same as the order of the series on the right edge, the reader solves the whole list with a single mapping and the transitions fall to 6. The remaining four come from the single pair that falls on the same row on the right edge: at the resolution of 12 rows, south and the second tariff tier cannot be separated, and those two series have to be matched one by one.
Direct labeling brings the number of transitions down to zero, because the name is written where the series ends and there is no matching job left. Ordering and direct labeling are also layers of redundant encoding; their difference is that they carry not distinguishability but recognition. All three layouts distinguish the same eight series, but the work they ask of the reader is 16, 6 and 0.
The Ceiling of a Single Display
# ceiling.py — how many series can be carried in a single display. DISTINCT_LEVELS = len(range(0, LEVELS, SEPARATION)) CEILING = DISTINCT_LEVELS * SHAPES print(f"of {LEVELS} lightness steps, {DISTINCT_LEVELS} are distinguishable at an " f"interval of {SEPARATION}; with {SHAPES} shapes the ceiling is {CEILING} series") print(f"{'series':>6s}{'distinguishable':>18s}{'parts':>7s}") for n in (8, 12, 16, 20, 24, 30, 40): code = [(0, (i % DISTINCT_LEVELS) * SEPARATION, (i // DISTINCT_LEVELS) % SHAPES) for i in range(n)] e = distinguishable(code, shaped) print(f"{n:6d} {e:17d} {max(1, -(-n // CEILING)):6d}") print(f"at most {CEILING} series can be distinguished in a single display; above that " f"the number of series cannot be read back without splitting the display")
of 9 lightness steps, 5 are distinguishable at an interval of 2; with 4 shapes the ceiling is 20 series
series distinguishable parts
8 8 1
12 12 1
16 16 1
20 20 1
24 20 2
30 20 2
40 20 2
at most 20 series can be distinguished in a single display; above that the number of series cannot be read back without splitting the display
The ceiling comes out of two numbers. Of nine lightness steps, five can be distinguished at a resolution of two steps; its product with four shapes is 20. Up to twenty every series is distinguished, at 24 series the number stays at 20 and four become unreadable; at 40 series it is still 20.
This is the hardest limit of the display and it cannot be exceeded by design. If more than twenty series are to be carried, the display is split: the series are divided into groups that do not exceed the ceiling and each group is drawn in its own display. The price is that comparison between groups can no longer be read from the same display. The rule of the topic comes from here — a display carries as many series as the number of distinguishable series; the rest counts as drawn but not reported.
Summary
- Color is not a name but two numbers, and in the model it is made of a hue family and a lightness step; the reading resolution is two steps.
- The default palette that crosses two hue families with four lightness steps has eight series distinguished by the full decoder and four by the decoder that does not read the hue distinction.
- When the shape channel is distributed in sequence, the number stays at four, because the shape starts over after four series and overlaps with the color collision; when distributed by collision it rises to eight with two shapes.
- Distinguishability does not give recognition: the same eight series ask for 16 transitions with a legend box, 6 with an ordered legend, and 0 with direct labeling.
- The ceiling of a single display is the product of the number of distinguishable steps and the number of shapes; with five steps and four shapes it is 20 series, and above that the display has to be split.
Next Step
Over eight lessons a single display was built correctly. The question type was matched to the channel, the shape of the distribution came down to a number, the slice without a baseline was measured, the point where the relationship becomes unreadable was found, the slope and the season were separated on the time axis, the weight of the area channel was counted, the four distortions were printed one by one, and finally how many series are distinguished was measured. The measure was the same in all of them: a display is as correct as the number the reader can read back.
A report, however, is not made of a single display. The reader meets more than one number at a glance, and they are encoded with different channels and stand at different resolutions. The next topic carries the measure here: how many criteria can be read back at a glance, how the same criterion can be moved without moving how it measures, and whether a report gives the same number when it is produced a second time. The reader is no longer a person who reads a number back, but a person who makes a decision with that number.
To keep your progress and take notes, Log in
My notes
Log in to take notes.