ddn.columnar.categorical
The categorical-column layer of ddn.df (ADR-2026-10-23-13/-14): int32 codes in values, the labels chunk in children[0]. Nulls are validity-0 slots (no code); codes never compare directly - equality, hashing and reconciliation go through the labels (dictionary merge by first-appearance label order, deterministic across threads).
Copyright
Types 3
Builds one CATEGORICAL chunk: int32 codes validated against the dictionary (or -1 for nulls), the labels chunk as the child.
private int[] _codesprivate ulong[] _validityprivate size_t _lengthprivate bool _anyNullprivate bool _sealedvoid append(int code) @safe pure nothrowAppends one valid slot with the dictionary code (validated at finish against the dictionary's length).Reconciles two dictionaries into a merged first-appearance dictionary plus code remappings. Deterministic: the merged label order is the left's labels then the right's unseen labels.
Parameters
left | the left labels chunk |
right | the right labels chunk |
Returns
right codes' remap (both indexable by the original code).
The reconciled form of a categorical Series' chunks: one shared dictionary every code chunk indexes, in force after every producing operation (the Series-level dictionary invariant, ADR-2026-10-23-14's 2026-10-28 amendment).
Functions 11
void validateCategoricalChunk(const ArrayData chunk) @safeValidates a CATEGORICAL chunk: codes within the dictionary or -1.Series categoricalSeries(string name, Series labels, int[] codes)Builds a CATEGORICAL Series from a labels Series and per-slot codes.Series categoricalLabels(Series s) @trustedThe labels of a CATEGORICAL Series as a STRING Series (slot `i` reads the dictionary label of code `i`); nulls stay null.ReconciledCategorical reconcileCategorical(
const ArrayData[] chunks, DataType dtype) @trustedBrings a chunk list to the single-dictionary invariant. Fast paths: zero/one chunks, and identical dictionaries (the same payload buffer - slices share it). Otherwise the dictionaries fold through ...Series categoricalWhen(Series mask, Series a, Series b) @trustedThe elementwise select for categorical branches (the `when` contract of ADR-2026-10-23-08: a null mask slot reads null; the selected branch's null stays null; the output carries one shared dictiona...ArrayData[] categoricalAppend(const ArrayData[] left,
const ArrayData[] right, DataType dtype) @trustedAppends two categorical chunk lists under the invariant: the dictionaries reconcile once, both sides' codes remap onto the merged dictionary, and the result chunks share it.ArrayData categoricalTake(const ArrayData[] chunks,
const size_t[] chunkStarts, const(size_t)[] indices,
DataType dtype) @trustedGathers categorical slots at global indices: codes and validity gather onto the reconciled dictionary (the invariant's output holds by construction). An index of `size_t.max` is the null marker (th...ArrayData categoricalRechunk(const ArrayData[] chunks,
DataType dtype) @trustedRebuilds multi-chunk categorical storage as one chunk: codes concatenate onto the reconciled dictionary, validity merges in row order.