Skip to content
Draft
Show file tree
Hide file tree
Changes from 104 commits
Commits
Show all changes
452 commits
Select commit Hold shift + click to select a range
cfdacf2
feat: Impl `expand_columns`
dangotbanned Jun 8, 2025
04427db
feat: Impl `expand_indices`, `replace_dtype_or_index_with_column`
dangotbanned Jun 8, 2025
7b3641b
feat: Impl `replace_wildcard`, `replace_wildcard_with_column`
dangotbanned Jun 8, 2025
f76c9dd
docs(DRAFT): Add more notes on selectors todo
dangotbanned Jun 8, 2025
f42e202
feat: Impl `replace_selector`
dangotbanned Jun 8, 2025
9a627a7
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 8, 2025
cfe3229
revert: Remove unplanned dtypes stuff and comments
dangotbanned Jun 8, 2025
804ac3d
chore: Remove factored-out `Inplace` 🥳
dangotbanned Jun 8, 2025
353ef59
chore: use `Version.dtypes`
dangotbanned Jun 8, 2025
1d63326
feat: Impl `ExprIR.map_ir` for most nodes
dangotbanned Jun 9, 2025
0df2be6
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 9, 2025
d3ea987
fix: typo
dangotbanned Jun 9, 2025
4604d9a
test: add `assert_expr_ir_equal`
dangotbanned Jun 9, 2025
360caec
test: Add `test_replace_selector`
dangotbanned Jun 9, 2025
2550103
feat: Impl `WindowExpr.map_ir`
dangotbanned Jun 9, 2025
7dd7092
feat: Impl `FunctionExpr.map_ir`
dangotbanned Jun 9, 2025
a3b96ee
chore: Tidy up notes
dangotbanned Jun 9, 2025
b569b57
test: Add `test_prepare_projection`
dangotbanned Jun 10, 2025
534c902
test: Add repro for horizontal alias bug
dangotbanned Jun 10, 2025
23045dc
fix: Add missing `Exclude` iterators
dangotbanned Jun 10, 2025
4e65ebc
refactor(DRAFT): Start splitting out `WindowExpr`
dangotbanned Jun 10, 2025
99cb01a
refactor: Use a single `Over` with two builder methods
dangotbanned Jun 10, 2025
422bbc7
fix: Expand exprs/selectors in `over(order_by=...)`
dangotbanned Jun 10, 2025
ee1bdb8
chore: Update comments
dangotbanned Jun 10, 2025
41f4070
refactor: Simplify `with_order_by`
dangotbanned Jun 10, 2025
7d4543e
refactor: Factor out tuple boilerplate
dangotbanned Jun 10, 2025
9303338
perf: Prepare `FrozenSchema` for caching
dangotbanned Jun 10, 2025
090330c
feat: Validate expressions with schema
dangotbanned Jun 11, 2025
d8dcfa4
revert: Remove superseded `_ColumnSelection.expand_columns`
dangotbanned Jun 11, 2025
49a6fc9
refactor: Repurpose `col`
dangotbanned Jun 11, 2025
b244b34
refactor: Make `expr` a dependency of `expr_expansion`
dangotbanned Jun 11, 2025
09c01fe
revert: Remove unused `regex` expansion stuff
dangotbanned Jun 11, 2025
bd58867
refactor: Remove/rename things inherited from `rust`
dangotbanned Jun 11, 2025
cfcbbda
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 11, 2025
00c2cc1
fix: Add some missing `is_scalar` props
dangotbanned Jun 11, 2025
48e9f25
feat: Add `functions.Log`
dangotbanned Jun 12, 2025
fb3f407
feat: Add `Expr.filter`
dangotbanned Jun 12, 2025
52f7975
ci: Update `name-tests-test` exclude pattern
dangotbanned Jun 12, 2025
ba0271e
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 12, 2025
0ff24fe
refactor: Move types from `common` to `typing`
dangotbanned Jun 12, 2025
13caf8d
feat: Utilize `IntoDType`
dangotbanned Jun 12, 2025
8c86fea
perf: Add a two-level cache for selectors expansion
dangotbanned Jun 12, 2025
6414142
refactor: Replace 3x `replace_*` functions with 1
dangotbanned Jun 12, 2025
80cc1c0
Merge branch 'main' into oh-nodes
dangotbanned Jun 12, 2025
0d0d6a2
feat: Support `*args, **kwds` in `when`
dangotbanned Jun 12, 2025
534cf16
feat: Add `expr`, `sqrt`, `kurtosis`
dangotbanned Jun 12, 2025
c9cb596
feat: Ensure mutability stays within function boundaries
dangotbanned Jun 12, 2025
03af47e
feat: more consistent index error
dangotbanned Jun 12, 2025
446c082
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 12, 2025
43a1ee2
Merge branch 'main' into oh-nodes
dangotbanned Jun 13, 2025
708d6ac
refactor: Reduce schema to columns where possible
dangotbanned Jun 13, 2025
7dbc380
typo
dangotbanned Jun 13, 2025
e7e17a7
feat: Add `_repr_html_`
dangotbanned Jun 14, 2025
579f9bd
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 15, 2025
12d7c96
chore(ruff): Update for `3.9` typing
dangotbanned Jun 15, 2025
1de65d2
fix: More consistent `__str__`
dangotbanned Jun 18, 2025
11f1e1b
refactor: Move, document `GroupByKeys`
dangotbanned Jun 22, 2025
1134e10
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 22, 2025
e17ab21
refactor: Add some `is_*_expr` guards
dangotbanned Jun 22, 2025
12ebe0c
docs: lil note on `prepare_projection`
dangotbanned Jun 22, 2025
44f7602
feat(DRAFT): Add `rewrite_elementwise_over`
dangotbanned Jun 22, 2025
67451b1
Merge branch 'main' into oh-nodes
dangotbanned Jun 23, 2025
b3af144
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 23, 2025
576afa9
feat: Add a basic rewrite composer
dangotbanned Jun 23, 2025
2ceadc4
test: `test_rewrite_elementwise_over_(simple|multiple)`
dangotbanned Jun 23, 2025
357d419
perf: Add safe caching to `meta.output_name`
dangotbanned Jun 23, 2025
2b8aea5
prep for `NamedIR`
dangotbanned Jun 23, 2025
bcc071a
refactor: Update to rewrite w/ `NamedIR`
dangotbanned Jun 23, 2025
d973b83
fix: Make sure to call `function` on result
dangotbanned Jun 23, 2025
5ae792d
refactor: Add `map_ir` function, un special-case `NamedIR`
dangotbanned Jun 23, 2025
ed9d769
docs(typing): `IntoFrozenSchema` alias
dangotbanned Jun 24, 2025
7dcdf86
test: Move `meta.output_name` doctests, add failing one
dangotbanned Jun 24, 2025
54d0781
fix: Get the right name from `FunctionExpr`
dangotbanned Jun 24, 2025
e8106c4
test: Lots of `output_name` coverage
dangotbanned Jun 24, 2025
b79181a
test: Backcompat `len`, `nth`
dangotbanned Jun 24, 2025
26716c5
refactor: Handle `SortBy`, `WindowExpr` internally
dangotbanned Jun 24, 2025
3a1c375
refactor: Simplify `FunctionExpr` version
dangotbanned Jun 24, 2025
bd5c33f
fix: Ensure `output_name` matches upstream
dangotbanned Jun 24, 2025
f0d9ddc
Merge branch 'main' into oh-nodes
dangotbanned Jun 24, 2025
a84ae05
feat: Add `NamedIR.(__repr__|_repr_html_)`
dangotbanned Jun 24, 2025
a46b3e5
test: Add `test_rewrite_elementwise_over_complex`
dangotbanned Jun 24, 2025
83e2b58
fix: Handle `*args` in `rewrite_elementwise_over`
dangotbanned Jun 24, 2025
ac2b1ff
feat: Add `int_range`
dangotbanned Jun 25, 2025
cb8234e
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 25, 2025
7324753
feat: Add `NamedIR.is_elementwise_top_level`
dangotbanned Jun 25, 2025
5114d32
Merge branch 'main' into oh-nodes
dangotbanned Jun 25, 2025
afd0bc7
Merge branch 'main' into oh-nodes
dangotbanned Jun 25, 2025
1f6e1da
feat: Initial `rewrite_binary_agg_over` impl
dangotbanned Jun 26, 2025
f2f6141
Merge branch 'oh-nodes' of https://github.com/narwhals-dev/narwhals i…
dangotbanned Jun 26, 2025
f2ac1c6
fix: undo `TypeIs` import
dangotbanned Jun 26, 2025
46831ea
Merge branch 'main' into oh-nodes
dangotbanned Jun 26, 2025
e726865
fix: Ensure lhs gets leaf name used
dangotbanned Jun 26, 2025
6edc52c
test: Add some `rewrite_binary_agg_over`
dangotbanned Jun 26, 2025
5c80033
Merge branch 'main' into oh-nodes
dangotbanned Jun 29, 2025
4076d4d
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 2, 2025
463d75a
feat: `FrozenSchema` repr#
dangotbanned Jul 2, 2025
08dcfca
refactor: Split out `FrozenSchema`
dangotbanned Jul 2, 2025
17822e8
planning schema projection
dangotbanned Jul 2, 2025
8243433
revert: Drop unplanned `impl_arrow` bits
dangotbanned Jul 2, 2025
984c07b
fix(typing): `*Series` generic
dangotbanned Jul 2, 2025
1468662
ci: Ignore dtypes import
dangotbanned Jul 2, 2025
2985bd5
feat: Reimpl `pyarrow`, start on `select`
dangotbanned Jul 2, 2025
a85fc7e
feat(pyarrow): Impl `Cast`, `Sort`, `Filter`, `Len`
dangotbanned Jul 2, 2025
faa91ec
feat(pyarrow): Impl `SortBy`
dangotbanned Jul 2, 2025
e6fab3f
Merge branch 'main' into oh-nodes
dangotbanned Jul 3, 2025
25ba870
Merge branch 'main' into oh-nodes
dangotbanned Jul 3, 2025
925d601
feat(pyarrow): Impl `First`, `Last`
dangotbanned Jul 3, 2025
c823d78
feat(pyarrow): Impl all aggregations
dangotbanned Jul 3, 2025
b069348
docs: Note on broacasting
dangotbanned Jul 3, 2025
a4d90d8
feat(DRAFT): Prepare new broadcasting layer
dangotbanned Jul 3, 2025
0ba05eb
refactor: Move `flatten_hash_safe`
dangotbanned Jul 4, 2025
3a4776f
more giant refactors 😅
dangotbanned Jul 4, 2025
5ca704f
ignore-banned-import
dangotbanned Jul 4, 2025
8e40fea
fix: alias removed exception types
dangotbanned Jul 4, 2025
34355dc
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 4, 2025
e45118d
refactor: Handle expansion, projection at narwhals-level
dangotbanned Jul 4, 2025
86d56ad
feat: Add `NamedIR.from_name`
dangotbanned Jul 5, 2025
a193af0
test: Add `test_lit_series_roundtrip`
dangotbanned Jul 5, 2025
8e47788
fix(typing): Propagate `NativeSeriesT`
dangotbanned Jul 5, 2025
9e2e2b9
chore(typing): Link to pyright explainer
dangotbanned Jul 5, 2025
154a3a0
test: Update tests that shouldn't broadcast
dangotbanned Jul 5, 2025
6bf86d8
keep on iterating
dangotbanned Jul 5, 2025
f2c6566
fix: Fill in missing type params
dangotbanned Jul 6, 2025
423ea9a
refactor: Move `native` out of higher protocol
dangotbanned Jul 6, 2025
f47fed2
refactor: Impl dispatch only once?
dangotbanned Jul 6, 2025
a4b1a02
chore: planning `CompliantNamespace`
dangotbanned Jul 6, 2025
1aaca2a
feat: `col`, `lit` classmethods?
dangotbanned Jul 6, 2025
1060f0e
feat(DRAFT): Dispatch take ✌
dangotbanned Jul 6, 2025
d6ebf9b
Update narwhals/_plan/dummy.py
dangotbanned Jul 6, 2025
33ddb8d
maybe `pyarrow` backcompat?
dangotbanned Jul 6, 2025
7aa7d1d
is `len` the issue?
dangotbanned Jul 6, 2025
eebef7a
plz
dangotbanned Jul 6, 2025
acdbf5e
revert: remove typing check
dangotbanned Jul 7, 2025
4437861
fix: Unwrap scalar on old pyarrow
dangotbanned Jul 7, 2025
5b6e644
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 7, 2025
bcdca6e
fix: Use new `Interval` helper in `truncate`
dangotbanned Jul 7, 2025
96b0c9c
feat(pyarrow): Impl `len`
dangotbanned Jul 7, 2025
9583383
feat(pyarrow): More impls
dangotbanned Jul 7, 2025
5457b30
test(pyarrow): Coverage for most of the current impl
dangotbanned Jul 7, 2025
c0c6b7d
refactor(pyarrow): Migrate most of `evaluate` into `ArrowExpr`
dangotbanned Jul 7, 2025
5039a09
feat(pyarrow): Add `ArrowDataFrame.sort`
dangotbanned Jul 7, 2025
c26c862
feat(pyarrow): Impl `Expr.sort_by`
dangotbanned Jul 7, 2025
c720749
fix: unused-ignore
dangotbanned Jul 7, 2025
017aa57
Merge branch 'main' into oh-nodes
dangotbanned Jul 8, 2025
8dcc57b
refactor: Move `lit`, `col`, `len` to namespace
dangotbanned Jul 8, 2025
4d54ff4
refactor: Move `len` impl to `EagerNamespace`
dangotbanned Jul 8, 2025
25a248b
revert: remove unused
dangotbanned Jul 8, 2025
d682390
feat: Add the remaining top-level nodes
dangotbanned Jul 8, 2025
6fdbd34
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 9, 2025
14033ae
refactor: Use `Protocol`s, move to `protocols`
dangotbanned Jul 9, 2025
d72b200
feat(pyarrow): Impl `BinaryExpr`
dangotbanned Jul 9, 2025
d5ccd31
🧹🧹🧹
dangotbanned Jul 9, 2025
a40b044
fix: Align all binary ops with `polars`
dangotbanned Jul 10, 2025
1880cef
fix: Fill in some `DType` holes
dangotbanned Jul 10, 2025
4b5b9ab
test: use `assert_equal_data`
dangotbanned Jul 10, 2025
2c0c067
refactor: tighten up protocols
dangotbanned Jul 10, 2025
97b84fd
Merge branch 'main' into oh-nodes
dangotbanned Jul 11, 2025
cb2e247
refactor: rename `Agg` -> `AggExpr`
dangotbanned Jul 11, 2025
4947ec4
fix: Check for scalars in `int_range`
dangotbanned Jul 11, 2025
5c33fce
feat: Identify n-ary functions
dangotbanned Jul 11, 2025
3c5663a
feat(DRAFT): Stub out namespace functions
dangotbanned Jul 11, 2025
f48d868
fix(typing): Oops they return expr
dangotbanned Jul 11, 2025
08e2bd2
feat(pyarrow): Impl `int_range`
dangotbanned Jul 11, 2025
5635be2
fix: typo in error message
dangotbanned Jul 11, 2025
98fe85a
feat(pyarrow): Impl `pow`
dangotbanned Jul 12, 2025
63b63c8
feat(pyarrow): Impl `fill_null`, `is_between`
dangotbanned Jul 12, 2025
cccc323
feat(pyarrow): Impl 6x boolean unary functions
dangotbanned Jul 12, 2025
1ad79a1
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 12, 2025
638b585
chore: remove `to_compliant`/`_to_compliant`
dangotbanned Jul 12, 2025
ccfa7da
rename `to_compliant_test` -> `compliant_test`
dangotbanned Jul 12, 2025
df52814
test: Add failing `when` tests
dangotbanned Jul 13, 2025
3140a48
feat(pyarrow): Impl complex `when-then-otherwise`
dangotbanned Jul 13, 2025
d9d8e64
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 14, 2025
b5550b4
refactor: Remove `pyarrow<13` compat
dangotbanned Jul 14, 2025
97453bb
refactor: Moving around
dangotbanned Jul 14, 2025
fbb463b
feat(pyarrow): Impl `all_horizontal`
dangotbanned Jul 14, 2025
21bf3db
feat(pyarrow): Impl `{any,sum,min,max}_horizontal`
dangotbanned Jul 14, 2025
0ae9f5f
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 15, 2025
ec06a5e
test: Shorten some test ids
dangotbanned Jul 15, 2025
031d547
test(pyarrow): Add `any_horizontal` tests
dangotbanned Jul 15, 2025
51ed0fb
test: Add equiv to `test_sumh_broadcasting`
dangotbanned Jul 15, 2025
b065573
feat(pyarrow): Impl `mean_horizontal`
dangotbanned Jul 15, 2025
3732e5a
fix: fill nulls in `sum_horizontal`
dangotbanned Jul 15, 2025
2b44d63
test: Add detail to xfail message
dangotbanned Jul 15, 2025
17634cd
refactor: Split out `functions`, `typing`
dangotbanned Jul 16, 2025
02092b4
feat(pyarrow): Impl `concat_str`
dangotbanned Jul 16, 2025
5d57676
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 16, 2025
3db1e65
prep for complex nodes
dangotbanned Jul 16, 2025
a5f110f
feat(pyarrow): Impl `map_batches`
dangotbanned Jul 16, 2025
747a5ae
test: Add `map_batches` tests
dangotbanned Jul 17, 2025
e85af02
refactor(pyarrow): Split out `int_range`
dangotbanned Jul 17, 2025
088a48a
feat(DRAFT): Add `with_columns`
dangotbanned Jul 17, 2025
168e930
Merge branch 'main' into oh-nodes
dangotbanned Jul 18, 2025
cf2f96c
fix: Extend exprs in `Schema._with_columns`
dangotbanned Jul 18, 2025
cd6ade0
wip: add `concat`
dangotbanned Jul 18, 2025
76b9b9a
refactor: Split out eager, remove `from_series`
dangotbanned Jul 18, 2025
37d7709
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 18, 2025
250922f
revert: Leave out unused `schema_projected` for now
dangotbanned Jul 23, 2025
8729ebe
refactor(typing): Relax `_concat_horizontal`
dangotbanned Jul 23, 2025
6ef9375
feat(pyarrow): Impl `over_ordered`
dangotbanned Jul 23, 2025
d3bed76
oop
dangotbanned Jul 23, 2025
cf38656
Merge branch 'main' into oh-nodes
dangotbanned Jul 23, 2025
2c85701
add `_with_columns`
dangotbanned Jul 30, 2025
4de8579
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 30, 2025
94d533d
perf: Add early return path for `over_ordered`
dangotbanned Jul 30, 2025
c893b2f
Merge branch 'main' into oh-nodes
dangotbanned Aug 2, 2025
b03d31f
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 15, 2025
25549de
style(ruff): re-run updated config
dangotbanned Aug 15, 2025
3ec5f7b
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 19, 2025
a12a985
chore(ruff): re-run updated config x2
dangotbanned Aug 19, 2025
62030ae
refactor(expr-ir): Rename `Dummy*` everything (#3014)
dangotbanned Aug 19, 2025
25b744d
refactor(expr-ir): Heavily sugar `Function` defs (#3017)
dangotbanned Aug 21, 2025
fcfcabf
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 21, 2025
de884c5
refactor(typing): Align `NativeDataFrame`
dangotbanned Aug 21, 2025
dbe2a45
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 25, 2025
f5ecc6e
refactor(expr-ir): Add more builder sugar (#3040)
dangotbanned Aug 25, 2025
69eede5
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 28, 2025
c934d25
refactor(expr-ir): Clearing out the cobwebs (#3053)
dangotbanned Aug 29, 2025
dc3b9ea
Merge branch 'main' into oh-nodes
dangotbanned Aug 29, 2025
607ea5a
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 30, 2025
631d3a3
refactor(expr-ir): `copy.replace` most `with_*` methods (#3063)
dangotbanned Aug 31, 2025
192de64
Merge branch 'main' into oh-nodes
dangotbanned Sep 1, 2025
215386f
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Sep 5, 2025
973824c
refactor(expr-ir): Shrinking `ExprIR` (main) (#3066)
dangotbanned Sep 10, 2025
70744e0
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Sep 10, 2025
6bc5ff5
fix(typing): Make pyright mostly happy
dangotbanned Sep 10, 2025
2aa8a2e
chore(typing): Ignore the other one for now
dangotbanned Sep 10, 2025
53d314d
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Sep 11, 2025
967a150
refactor(expr-ir): Organize `_plan` package (#3122)
dangotbanned Sep 14, 2025
318261e
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Sep 15, 2025
7599fc4
revert(ruff): ignore (`RUF043`)
dangotbanned Sep 15, 2025
e502dcc
Merge branch 'main' into oh-nodes
dangotbanned Sep 18, 2025
65f9738
Merge branch 'main' into oh-nodes
dangotbanned Sep 21, 2025
8208d32
feat(expr-ir): Support `group_by`, utilize `pyarrow.acero` (#3143)
dangotbanned Oct 1, 2025
53f4041
Merge branch 'main' into oh-nodes
dangotbanned Oct 1, 2025
2b9dbf0
refactor(expr-ir): Split up and refine `protocols.py` (#3166)
dangotbanned Oct 1, 2025
2403f1b
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Oct 6, 2025
bde22ac
feat(expr-ir): Acero `order_by`, `hashjoin` , `DataFrame.{filter,join…
dangotbanned Oct 12, 2025
cce4289
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Oct 12, 2025
1a433a9
fix: Update alias import
dangotbanned Oct 12, 2025
bf544d4
fix(expr-ir): Ensure only `__slots__`, and not `__dict__` too (#3201)
dangotbanned Oct 13, 2025
63c85c8
Merge branch 'main' into oh-nodes
dangotbanned Oct 13, 2025
fa0899a
perf(expr-ir): Optimize `ExpansionFlags.from_ir` (#3206)
dangotbanned Oct 15, 2025
1ab1599
Merge branch 'main' into oh-nodes
dangotbanned Oct 15, 2025
34a1fd0
refactor(expr-ir): Improve function dispatch (#3215)
dangotbanned Oct 18, 2025
dbca5a5
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Oct 18, 2025
9acce69
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Oct 20, 2025
46abfe6
chore: re-sync imports following (#3086)
dangotbanned Oct 20, 2025
1ba04b1
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Nov 1, 2025
089bfb5
Merge branch 'main' into oh-nodes
dangotbanned Nov 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions narwhals/_plan/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Brainstorming an `Expr` internal representation.

Notes:
- Each `Expr` method should be representable by a single node
- But the node does not need to be unique to the method
- A chain of `Expr` methods should form a plan of operations
- We must be able to enforce rules on what plans are permitted:
- Must be flexible to both eager/lazy and individual backends
- Must be flexible to a given context (select, with_columns, filter, group_by)
- Nodes & plans are:
- Immutable, but
- Can be extended/re-written at both the Narwhals & Compliant levels
- Introspectable, but
- Store as little-as-needed for the common case
- Provide properties/methods for computing the less frequent metadata

References:
- https://github.com/pola-rs/polars/blob/dafd0a2d0e32b52bcfa4273bffdd6071a0d5977a/crates/polars-python/src/lazyframe/visitor/expr_nodes.rs
- https://github.com/pola-rs/polars/blob/dafd0a2d0e32b52bcfa4273bffdd6071a0d5977a/crates/polars-plan/src/dsl/expr.rs
- https://github.com/pola-rs/polars/blob/dafd0a2d0e32b52bcfa4273bffdd6071a0d5977a/crates/polars-plan/src/dsl/function_expr/mod.rs
- https://github.com/pola-rs/polars/blob/dafd0a2d0e32b52bcfa4273bffdd6071a0d5977a/crates/polars-plan/src/dsl/options/mod.rs#L137-L172
- https://github.com/pola-rs/polars/blob/3fd7ecc5f9de95f62b70ea718e7e5dbf951b6d1c/crates/polars-plan/src/plans/options.rs#L35-L106
- https://github.com/pola-rs/polars/blob/3fd7ecc5f9de95f62b70ea718e7e5dbf951b6d1c/crates/polars-plan/src/plans/options.rs#L131-L236
- https://github.com/pola-rs/polars/blob/3fd7ecc5f9de95f62b70ea718e7e5dbf951b6d1c/crates/polars-plan/src/plans/options.rs#L240-L267
- https://github.com/pola-rs/polars/blob/6df23a09a81c640c21788607611e09d9f43b1abc/crates/polars-plan/src/plans/aexpr/mod.rs

Related:
- https://github.com/narwhals-dev/narwhals/pull/2483#issuecomment-2866902903
- https://github.com/narwhals-dev/narwhals/pull/2483#issuecomment-2867331343
- https://github.com/narwhals-dev/narwhals/pull/2483#issuecomment-2867446959
- https://github.com/narwhals-dev/narwhals/pull/2483#issuecomment-2869070157
- (https://github.com/narwhals-dev/narwhals/pull/2538/commits/a7eeb0d23e67cb70e7cfa73cec2c7b69a15c8bef#r2083562677)
- https://github.com/narwhals-dev/narwhals/issues/2225
- https://github.com/narwhals-dev/narwhals/issues/1848
- https://github.com/narwhals-dev/narwhals/issues/2534#issuecomment-2875676729
- https://github.com/narwhals-dev/narwhals/issues/2291
- https://github.com/narwhals-dev/narwhals/issues/2522
- https://github.com/narwhals-dev/narwhals/pull/2555
"""

from __future__ import annotations
95 changes: 95 additions & 0 deletions narwhals/_plan/aggregation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from narwhals._plan.common import ExprIR

if TYPE_CHECKING:
from typing import Iterator

from narwhals.typing import RollingInterpolationMethod


class Agg(ExprIR):
__slots__ = ("expr",)

expr: ExprIR

@property
def is_scalar(self) -> bool:
return True

def __repr__(self) -> str:
tp = type(self)
if tp in {Agg, OrderableAgg}:
return tp.__name__
m = {ArgMin: "arg_min", ArgMax: "arg_max", NUnique: "n_unique"}
name = m.get(tp, tp.__name__.lower())
return f"{self.expr!r}.{name}()"

def iter_left(self) -> Iterator[ExprIR]:
yield from self.expr.iter_left()
yield self

def iter_right(self) -> Iterator[ExprIR]:
yield self
yield from self.expr.iter_right()


class Count(Agg): ...


class Max(Agg): ...


class Mean(Agg): ...


class Median(Agg): ...


class Min(Agg): ...


class NUnique(Agg): ...


class Quantile(Agg):
__slots__ = (*Agg.__slots__, "interpolation", "quantile")

quantile: float
interpolation: RollingInterpolationMethod


class Std(Agg):
__slots__ = (*Agg.__slots__, "ddof")

ddof: int
"""https://github.com/narwhals-dev/narwhals/pull/2555"""


class Sum(Agg): ...


class Var(Agg):
__slots__ = (*Agg.__slots__, "ddof")

ddof: int
"""https://github.com/narwhals-dev/narwhals/pull/2555"""


class OrderableAgg(Agg): ...


class First(OrderableAgg):
"""https://github.com/narwhals-dev/narwhals/issues/2526."""


class Last(OrderableAgg):
"""https://github.com/narwhals-dev/narwhals/issues/2526."""


class ArgMin(OrderableAgg): ...


class ArgMax(OrderableAgg): ...
142 changes: 142 additions & 0 deletions narwhals/_plan/boolean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
from __future__ import annotations

# NOTE: Needed to avoid naming collisions
# - Any
import typing as t

from narwhals._plan.common import Function
from narwhals._plan.options import FunctionFlags
from narwhals._plan.options import FunctionOptions

if t.TYPE_CHECKING:
from narwhals.typing import ClosedInterval


class BooleanFunction(Function):
def __repr__(self) -> str:
tp = type(self)
if tp is BooleanFunction:
return tp.__name__
m: dict[type[BooleanFunction], str] = {
All: "all",
Any: "any",
AllHorizontal: "all_horizontal",
AnyHorizontal: "any_horizontal",
IsBetween: "is_between",
IsDuplicated: "is_duplicated",
IsFinite: "is_finite",
IsNan: "is_nan",
IsNull: "is_null",
IsFirstDistinct: "is_first_distinct",
IsLastDistinct: "is_last_distinct",
IsUnique: "is_unique",
IsIn: "is_in",
Not: "not",
}
return m[tp]


class All(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.aggregation()


class AllHorizontal(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise().with_flags(
FunctionFlags.INPUT_WILDCARD_EXPANSION
)


class Any(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.aggregation()


class AnyHorizontal(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise().with_flags(
FunctionFlags.INPUT_WILDCARD_EXPANSION
)


class IsBetween(BooleanFunction):
"""`lower_bound`, `upper_bound` aren't spec'd in the function enum.

Assuming the `FunctionExpr.input` becomes `s` in the impl

https://github.com/pola-rs/polars/blob/62257860a43ec44a638e8492ed2cf98a49c05f2e/crates/polars-plan/src/dsl/function_expr/boolean.rs#L225-L237
"""

__slots__ = ("closed",)

closed: ClosedInterval

@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()


class IsDuplicated(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.length_preserving()


class IsFinite(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()


class IsFirstDistinct(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.length_preserving()


class IsIn(BooleanFunction):
"""``other` isn't spec'd in the function enum.

See `IsBetween` comment.
"""

@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()


class IsLastDistinct(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.length_preserving()


class IsNan(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()


class IsNull(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()


class IsUnique(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.length_preserving()


class Not(BooleanFunction):
"""`__invert__`."""

@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()
19 changes: 19 additions & 0 deletions narwhals/_plan/categorical.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from __future__ import annotations

from narwhals._plan.common import Function
from narwhals._plan.options import FunctionOptions


class CategoricalFunction(Function): ...


class GetCategories(CategoricalFunction):
"""https://github.com/pola-rs/polars/blob/62257860a43ec44a638e8492ed2cf98a49c05f2e/crates/polars-plan/src/dsl/function_expr/cat.rs#L7."""

@property
def function_options(self) -> FunctionOptions:
"""https://github.com/pola-rs/polars/blob/62257860a43ec44a638e8492ed2cf98a49c05f2e/crates/polars-plan/src/dsl/function_expr/cat.rs#L41."""
return FunctionOptions.groupwise()

def __repr__(self) -> str:
return "cat.get_categories"
Loading
Loading