Skip to content

Commit ae5bc05

Browse files
committed
Getting ready for release 4.9.0
1 parent 2714aa3 commit ae5bc05

6 files changed

Lines changed: 1261 additions & 1478 deletions

File tree

ANNOUNCE.rst

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,55 @@
1-
Announcing Python-Blosc2 4.8.1
1+
Announcing Python-Blosc2 4.9.0
22
==============================
33

4-
This is a maintenance release with a solid batch of bug fixes — including
5-
use-after-free hazards around zero-copy cframes, wrong-chunk deletion with
6-
negative-step slices, and inconsistent ``DictStore`` overwrite semantics —
7-
plus read-only memory mapping for ``CTable`` stores and a documentation
8-
restructuring with a new Optimization Tips section.
4+
Last year's big push was making ``NDArray`` a good ecosystem citizen — array
5+
API compliance, protocols, interop. This release does the same for
6+
``CTable``: instead of asking the tabular ecosystem to learn Blosc2's ways,
7+
``CTable`` now speaks its protocols directly. Arrow tools can read and write
8+
a ``CTable`` with zero glue code, a new string column type stores text in
9+
Arrow's own layout, and ``engine=blosc2.jit`` runs correctly *inside*
10+
pandas 3 itself. Compression doesn't have to mean an island — it can just be
11+
a fast, compact layer underneath the tools you already use.
912

1013
The main highlights are:
1114

12-
- **Read-only mmap for ``CTable`` stores**: ``CTable.open()`` gains an
13-
``mmap_mode="r"`` parameter, mirroring ``blosc2.open()``. All members of a
14-
read-only store — scalar, list, varlen and dictionary columns alike — are
15-
read from mapped pages; for ``.b2z`` archives, in place inside the single
16-
mapped container file. With several concurrent readers on one file this
17-
pays off quickly: 2.5x/4.4x/4.5x faster wall time for 1/4/8 readers in
18-
our benchmark.
19-
20-
- **Zero-copy cframe fix**: ``schunk_from_cframe()`` /
21-
``ndarray_from_cframe()`` with ``copy=False`` (the default) returned
22-
objects pointing into the caller's bytes buffer without keeping it alive,
23-
so a temporary cframe could be reclaimed under the live object, corrupting
24-
reads. The buffer is now pinned on the returned object.
25-
26-
- **More correctness fixes**: negative-step slice deletion in
27-
``BatchArray``/``ObjectArray`` removed the wrong chunks; ``DictStore``
28-
overwrite semantics depended on value size (now uniformly dict-like);
29-
``stack()``/``vecdot()`` shape inference was off for negative axes;
30-
chunked ``matmul()`` mishandled broadcast batch dims; and
31-
``ListArray.extend_arrow()`` could reorder unflushed rows.
32-
33-
- **Faster ``.b2z``/``.b2d`` opens**: a builtin-shadowing bug made store
34-
detection in ``blosc2.open()`` silently recurse ~250 times on every open.
35-
36-
- **Docs restructuring**, with a new `Optimization tips
37-
<https://www.blosc.org/python-blosc2/guides/optimization_tips.html>`_
38-
section, including tips on grouping related data into a single
39-
memory-mapped ``.b2z`` file and on using ``mmap_mode="r"`` with many
40-
concurrent readers.
15+
- **Arrow PyCapsule interchange**: ``CTable.__arrow_c_stream__`` lets
16+
pyarrow, DuckDB, Polars, and pandas >= 2.2 consume a ``CTable`` directly as
17+
a stream of record batches, with bounded memory — no ``to_arrow()`` copy
18+
step needed::
19+
20+
import duckdb, blosc2
21+
t = blosc2.CTable.open("trips.b2z")
22+
duckdb.sql("SELECT company, avg(fare) FROM t GROUP BY company").show()
23+
24+
``CTable.from_arrow()`` now accepts any object implementing the same
25+
protocol on ingest too — a pyarrow Table, a Polars DataFrame, a Parquet
26+
reader — in addition to the existing ``(schema, batches)`` form.
27+
28+
- **``blosc2.utf8()``: string columns that speak Arrow's layout**. Instead of
29+
a blosc2-specific encoding, ``utf8()`` stores text exactly as Arrow does —
30+
int64 row offsets plus a UTF-8 byte blob — and reads back as NumPy
31+
``StringDType``. It's the new recommended default for free text: 7-13x
32+
smaller uncompressed than fixed-width ``string()`` on high-cardinality
33+
text, with a full query surface (comparisons, ``where()``, ``sort_by``,
34+
``group_by`` keys, ``fillna``, Arrow round-trip).
35+
36+
- **pandas engine, pandas 3 ready**: ``engine=blosc2.jit`` for
37+
``DataFrame.apply`` now returns a properly indexed ``DataFrame``/``Series``
38+
under pandas 3's default ``raw=False``, and ``Series.map(func,
39+
engine=blosc2.jit)`` is implemented for the first time.
40+
41+
- **``CTable`` grows a pandas-style API**: ``CTable.assign()`` plus an
42+
unbound ``blosc2.col()`` for chaining —
43+
``t.assign(profit=col("revenue") - col("cost"))[col("profit") > 0].sort_by("profit", ascending=False).head(10)``
44+
— and a real missing-data story: ``Column.fillna()``, ``CTable.dropna()``,
45+
and null-propagating arithmetic/comparisons on nullable columns (no more
46+
``t[t.x < 0]`` silently matching null rows). ``group_by().agg()`` accepts
47+
UDF aggregations, and ``CTable.apply()`` runs a UDF across live rows.
48+
49+
- **Faster indexed reads**: ``NDArray.iter_sorted()``/``argsort()`` on a
50+
``FULL``-indexed array reads the sidecar range directly instead of
51+
building the full permutation — ~52x faster and ~193x less memory for
52+
tail-style queries on a 20M-element array.
4153

4254
Install it with::
4355

RELEASE_NOTES.md

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,108 @@
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

Comments
 (0)