|
1 | | -Announcing Python-Blosc2 4.9.0 |
| 1 | +Announcing Python-Blosc2 4.9.1 |
2 | 2 | ============================== |
3 | 3 |
|
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. |
12 | | - |
13 | | -The main highlights are: |
14 | | - |
15 | | -- **Arrow PyCapsule interchange**: ``CTable.__arrow_c_stream__`` lets |
16 | | - pyarrow, DuckDB, and Polars consume a ``CTable`` directly as a stream of |
17 | | - record batches, with bounded memory — no ``to_arrow()`` copy step |
18 | | - 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 | | - pandas >= 3.0 gets the same treatment via the new |
25 | | - ``pandas.DataFrame.from_arrow(t)`` classmethod (the plain ``pd.DataFrame(t)`` |
26 | | - constructor doesn't use this protocol). ``CTable.from_arrow()`` now |
27 | | - accepts any object implementing the same protocol on ingest too — a |
28 | | - pyarrow Table, a Polars DataFrame, a Parquet reader — in addition to the |
29 | | - existing ``(schema, batches)`` form, including Arrow's ``string_view`` |
30 | | - layout (Polars' default string export type). |
31 | | - |
32 | | -- **``blosc2.utf8()``: string columns that speak Arrow's layout**. Instead of |
33 | | - a blosc2-specific encoding, ``utf8()`` stores text exactly as Arrow does — |
34 | | - int64 row offsets plus a UTF-8 byte blob — and reads back as NumPy |
35 | | - ``StringDType``. It's the new recommended default for free text: 7-13x |
36 | | - smaller uncompressed than fixed-width ``string()`` on high-cardinality |
37 | | - text, with a full query surface (comparisons, ``where()``, ``sort_by``, |
38 | | - ``group_by`` keys, ``fillna``, Arrow round-trip). |
39 | | - |
40 | | -- **pandas engine, pandas 3 ready**: ``engine=blosc2.jit`` for |
41 | | - ``DataFrame.apply`` now returns a properly indexed ``DataFrame``/``Series`` |
42 | | - under pandas 3's default ``raw=False``, and ``Series.map(func, |
43 | | - engine=blosc2.jit)`` is implemented for the first time. |
44 | | - |
45 | | -- **``CTable`` grows a pandas-style API**: ``CTable.assign()`` plus an |
46 | | - unbound ``blosc2.col()`` for chaining — |
47 | | - ``t.assign(profit=col("revenue") - col("cost"))[col("profit") > 0].sort_by("profit", ascending=False).head(10)`` |
48 | | - — and a real missing-data story: ``Column.fillna()``, ``CTable.dropna()``, |
49 | | - and null-propagating arithmetic/comparisons on nullable columns (no more |
50 | | - ``t[t.x < 0]`` silently matching null rows). ``group_by().agg()`` accepts |
51 | | - UDF aggregations, and ``CTable.apply()`` runs a UDF across live rows. |
52 | | - |
53 | | -- **Faster indexed reads**: ``NDArray.iter_sorted()``/``argsort()`` on a |
54 | | - ``FULL``-indexed array reads the sidecar range directly instead of |
55 | | - building the full permutation — ~52x faster and ~193x less memory for |
56 | | - tail-style queries on a 20M-element array. |
| 4 | +This is a hot-fix release for the Arrow interop work introduced in 4.9.0 — |
| 5 | +one real performance regression and one clearer error message, both in |
| 6 | +``CTable``. |
| 7 | + |
| 8 | +- **Faster dictionary-column Arrow export**: ``CTable.iter_arrow_batches()`` |
| 9 | + (and therefore ``to_arrow()`` and the Arrow PyCapsule interchange, |
| 10 | + ``__arrow_c_stream__``) was recomputing the full live-row-position array |
| 11 | + from scratch on every batch, for every dictionary-encoded string column — |
| 12 | + an ``O(n_rows)`` scan repeated ``O(n_rows / batch_size)`` times. It's now |
| 13 | + computed once per export call instead. 6-14x faster export for |
| 14 | + dictionary columns on a 1M-row benchmark. |
| 15 | + |
| 16 | +- **Worth knowing regardless of this fix**: the Arrow PyCapsule protocol |
| 17 | + (``__arrow_c_stream__``) has no column-projection pushdown — a consumer |
| 18 | + that only needs two columns (DuckDB, pyarrow, Polars, pandas) still |
| 19 | + triggers export of *every* column in the table, since the raw Arrow C |
| 20 | + Stream interface has no way to say "I only need these." Use |
| 21 | + ``CTable.select([...])`` to project down to the columns you actually |
| 22 | + need before handing the table off, especially if any column holds an |
| 23 | + expensive nested/list type:: |
| 24 | + |
| 25 | + sub = t.select(["company", "fare"]) |
| 26 | + duckdb.sql("SELECT company, avg(sub.fare) FROM sub GROUP BY company").show() |
| 27 | + |
| 28 | +- **Clearer error on ``mode="a"``**: opening a ``CTable`` with |
| 29 | + ``mode="a"`` at a path that doesn't exist yet now raises a |
| 30 | + ``FileNotFoundError`` explaining that ``mode="a"`` opens an existing |
| 31 | + table (use ``mode="w"`` to create one), instead of silently creating a |
| 32 | + new, empty table. |
57 | 33 |
|
58 | 34 | Install it with:: |
59 | 35 |
|
|
0 commit comments