11# Release notes
22
3- ## Changes from 4.8.1 to 4.8.2
4-
5- XXX version-specific blurb XXX
3+ ## Changes from 4.8.1 to 4.9.0
4+
5+ This release is about cooperation: ` CTable ` now speaks the tabular
6+ ecosystem's own protocols instead of asking it to speak blosc2's. Arrow
7+ tools (pyarrow, DuckDB, Polars, pandas >= 2.2) can consume or produce a
8+ ` CTable ` directly through the Arrow PyCapsule interface; a new ` utf8() `
9+ string column stores text in Arrow's own offsets+bytes layout and reads
10+ back as NumPy ` StringDType ` ; and ` engine=blosc2.jit ` now runs correctly
11+ inside pandas 3 itself. Alongside that, ` CTable ` gained a proper
12+ missing-data story (` fillna ` /` dropna ` , null-safe arithmetic and
13+ comparisons) and a pandas-3-style chaining API (` assign() ` /` col() ` ,
14+ UDF aggregations, ` CTable.apply() ` ).
615
716### New features
817
18+ #### Ecosystem interop
19+
20+ - ** Arrow PyCapsule interchange** : ` CTable.__arrow_c_stream__ ` lets
21+ pyarrow, DuckDB, Polars, and pandas >= 2.2 consume a ` CTable ` directly
22+ as a stream of record batches, with bounded memory — no
23+ ` to_arrow() ` /copy step required. ` CTable.from_arrow() ` now accepts any
24+ object implementing the same protocol on ingest (single-argument
25+ form), in addition to the existing ` (schema, batches) ` form.
26+ - ** ` blosc2.utf8() ` ** : a new column type for high-cardinality/free-text
27+ strings, storing each column as two companion NDArrays — int64 row
28+ offsets plus a UTF-8 byte blob — the same layout Arrow uses for
29+ ` large_string ` . A row costs exactly its encoded byte length (7-13x
30+ smaller uncompressed than fixed-width ` string() ` on high-cardinality
31+ text), and reads materialize as NumPy ` StringDType ` arrays (NumPy >=
32+ 2.0 required; older NumPy falls back to ` vlstring ` on Arrow/Parquet
33+ import with a clear message). Full query surface: comparisons,
34+ ` where() ` , ` sort_by ` , ` group_by ` keys, ` fillna ` , and Arrow export
35+ (` large_string ` , sentinel-null mask) / import (Arrow/Parquet string
36+ columns now default to ` utf8 ` instead of ` vlstring ` ). Measured on the
37+ 1e7-row NYC-taxi ` company ` column: ingest 3622 ms -> 598.6 ms
38+ (** 6.05x** faster, only 1.22x slower than ` string() ` and 2.63x faster
39+ than ` vlstring() ` ), full-column read 2472.6 ms -> 165.3 ms (** 14.96x**
40+ faster), equality filter ~ 1900 ms -> 162 ms (** ~ 11.7x** faster), and
41+ groupby-key factorization 3304 ms -> 558 ms (** 2.83x** , down from a
42+ 16.9x-slower initial fallback, now faster than fixed-width ` string() `
43+ keys). See the new "Choosing a string column type" guide in the
44+ ` CTable ` reference and ` examples/ctable/utf8_strings.py ` . Known gaps:
45+ string-expression filters (` t.where("name == 'x'") ` ) and
46+ ` create_index ` on ` utf8 ` columns still raise a clear
47+ ` NotImplementedError ` ; use fixed-width ` string() ` if you need those.
48+ - ** pandas engine, pandas 3 compatible** : ` engine=blosc2.jit ` for
49+ ` DataFrame.apply ` now returns a properly indexed ` DataFrame ` /` Series `
50+ under pandas 3.0.3's default ` raw=False ` (previously a raw NumPy
51+ array, so results only matched by value, never by type).
52+ ` Series.map(func, engine=blosc2.jit) ` is now implemented (it
53+ previously always raised ` NotImplementedError ` ). Non-numeric columns
54+ now raise a clear ` ValueError ` instead of a deep ` numexpr ` error. See
55+ the guide "Using Blosc2 as a pandas engine" and
56+ ` bench/bench_pandas_engine.py ` .
57+
58+ #### pandas-style CTable API
59+
960- ` CTable.assign(**named_exprs) ` : return a view with additional computed
1061 columns, without mutating the table or copying column data. Pairs with
1162 the new ` blosc2.col(name) ` — an unbound column expression that defers
1263 operator replay until it's bound to a table (` assign() ` , ` t[...] ` ,
1364 ` where() ` ) — to write pandas-3-style chains:
1465 ` t.assign(profit=col("revenue") - col("cost"))[col("profit") > 0].sort_by("profit", ascending=False).head(10) ` .
66+ - ** Missing data** : ` Column.fillna(value) ` replaces sentinel/` None `
67+ values for scalar, dictionary, and varlen-scalar columns;
68+ ` CTable.dropna(subset=None) ` returns a view excluding rows where any
69+ nullable column (or a chosen subset) is null. Column arithmetic
70+ (` + - * / // % ** ` ) and comparisons (` < <= > >= == != ` ) on nullable
71+ int/timestamp/bool columns now propagate nulls instead of operating on
72+ the raw sentinel: arithmetic promotes to ` float64 ` /NaN, comparisons
73+ follow SQL ` WHERE ` semantics (a null operand never satisfies any
74+ comparison) — fixing filters like ` t[t.x < 0] ` wrongly matching null
75+ rows. Also fixes null detection for timestamp columns
76+ (` is_null() ` /` null_count() ` /` dropna() ` previously missed every
77+ ` NaT ` ).
78+ - ** UDF aggregations** : ` group_by().agg() ` accepts a custom callable as
79+ the op via the named form (` output_name=(column, callable[, dtype]) ` );
80+ it receives each group's live, non-null values as a 1-D NumPy array.
81+ ` CTable.apply(func, columns=None, dtype=None, engine="auto") ` applies
82+ a UDF across the table's live rows, sugar over ` blosc2.lazyudf() ` .
83+ ` group_by(engine=...) ` now accepts ` "auto" ` /` "numpy" ` explicitly
84+ alongside the existing default.
85+ - ` CTable ` views are now read-only for value writes: ` Column.__setitem__ `
86+ and ` Column.assign() ` raise ` ValueError ` on a view, pointing at
87+ ` take() ` /` copy() ` as the escape hatch (structural mutations already
88+ raised; this closes the one unguarded cell-write path).
89+ - ` NDArray.iter_sorted() ` /` argsort() ` on a ` FULL ` -indexed array now reads
90+ the sidecar range directly instead of building the full permutation —
91+ ~ 52x faster and ~ 193x less memory on a 20M-element array for
92+ ` iter_sorted(start=-k) ` -style tail queries. See the new optimization
93+ tip.
94+
95+ ### Improvements
96+
97+ - String-key ` group_by() ` (fixed-width ` string() ` keys) now factorizes
98+ via an exact hash of each row's raw bytes instead of a NumPy
99+ UTF-32 argsort, with a vectorized collision-checked verify pass
100+ keeping the result bit-identical: 1157 ms -> 737 ms on a 1e7-row
101+ benchmark. Every caller benefits automatically; no ` engine= ` switch
102+ involved.
103+ - An example showing ctables and ndarrays bundled together in the same
104+ ` TreeStore ` .
105+ - C-Blosc2 bumped to 3.2.3.
15106
16107### Bug fixes
17108
@@ -24,14 +115,9 @@ XXX version-specific blurb XXX
24115 ignored ` _cached_live_positions ` and built a plain physical-order mask
25116 instead, so ` t.where(...).sort_by("x", ascending=False).head(10) ` came
26117 back in the wrong order.
27- - Fixed ` engine=blosc2.jit ` for ` DataFrame.apply ` against pandas 3.0.3: with
28- the default ` raw=False ` , the engine returned a raw NumPy array instead of
29- a properly indexed ` DataFrame ` /` Series ` , so results only matched plain
30- ` apply() ` by value, never by type. ` Series.map(func, engine=blosc2.jit) `
31- is now implemented (it previously always raised ` NotImplementedError ` ).
32- Non-numeric columns now raise a clear ` ValueError ` instead of a deep
33- ` numexpr ` error. See the new guide "Using Blosc2 as a pandas engine" and
34- ` bench/bench_pandas_engine.py ` .
118+ - Fixed a ` NameError ` when ` nan ` /` inf ` scalars appeared in lazy
119+ expressions; ` ShapeInferencer ` no longer ignores user-provided shapes
120+ for ` nan ` /` inf ` .
35121
36122## Changes from 4.8.0 to 4.8.1
37123
@@ -46,6 +132,7 @@ XXX version-specific blurb XXX
46132 2.5x/4.4x/4.5x faster wall time for 1/4/8 readers in our benchmark
47133 (` bench/optim_tips/tip_10_mmap_many_readers.py ` ).
48134- Reduced memory consumption in ` CTable.extend() ` when passed an ` NDArray ` .
135+ - C-Blosc2 bumped to 3.2.2.
49136
50137### Bug fixes
51138
0 commit comments