Skip to content

Commit cbc1ee1

Browse files
committed
Merge remote-tracking branch 'upstream/main' into more-dedup-1
2 parents b4f6a51 + 9387ecd commit cbc1ee1

File tree

6 files changed

+57
-26
lines changed

6 files changed

+57
-26
lines changed

narwhals/_arrow/series.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,10 +667,10 @@ def fill_aux(
667667
)
668668

669669
ser = self._native_series
670-
dtype = ser.type
671670

672671
if value is not None:
673-
res_ser = self._from_native_series(pc.fill_null(ser, lit(value, dtype))) # type: ignore[attr-defined]
672+
_, value = extract_native(self, value)
673+
res_ser = self._from_native_series(pc.fill_null(ser, value)) # type: ignore[attr-defined]
674674
elif limit is None:
675675
fill_func = (
676676
pc.fill_null_forward if strategy == "forward" else pc.fill_null_backward

narwhals/_duckdb/expr.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,9 +450,10 @@ def fill_null(self: Self, value: Any, strategy: Any, limit: int | None) -> Self:
450450
msg = "todo"
451451
raise NotImplementedError(msg)
452452

453-
return self._from_call(
454-
lambda _input: CoalesceOperator(_input, lit(value)), "fill_null"
455-
)
453+
def func(_input: duckdb.Expression, value: Any) -> duckdb.Expression:
454+
return CoalesceOperator(_input, value)
455+
456+
return self._from_call(func, "fill_null", value=value)
456457

457458
def cast(self: Self, dtype: DType | type[DType]) -> Self:
458459
def func(_input: duckdb.Expression) -> duckdb.Expression:

narwhals/_pandas_like/series.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ def fill_null(
531531
) -> Self:
532532
ser = self._native_series
533533
if value is not None:
534+
_, value = align_and_extract_native(self, value)
534535
res_ser = self._from_native_series(ser.fillna(value=value))
535536
else:
536537
res_ser = self._from_native_series(

narwhals/expr.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from narwhals._expression_parsing import ExprMetadata
1313
from narwhals._expression_parsing import apply_n_ary_operation
1414
from narwhals._expression_parsing import combine_metadata
15+
from narwhals._expression_parsing import extract_compliant
1516
from narwhals.dtypes import _validate_dtype
1617
from narwhals.exceptions import LengthChangingExprError
1718
from narwhals.expr_cat import ExprCatNamespace
@@ -1333,7 +1334,7 @@ def fill_null(
13331334
"""Fill null values with given value.
13341335
13351336
Arguments:
1336-
value: Value used to fill null values.
1337+
value: Value or expression used to fill null values.
13371338
strategy: Strategy used to fill null values.
13381339
limit: Number of consecutive null values to fill when using the 'forward' or 'backward' strategy.
13391340
@@ -1352,34 +1353,37 @@ def fill_null(
13521353
... {
13531354
... "a": [2, None, None, 3],
13541355
... "b": [2.0, float("nan"), float("nan"), 3.0],
1356+
... "c": [1, 2, 3, 4],
13551357
... }
13561358
... )
13571359
>>> df = nw.from_native(df_native)
13581360
>>> df.with_columns(
1359-
... nw.col("a", "b").fill_null(0).name.suffix("_nulls_filled")
1361+
... nw.col("a", "b").fill_null(0).name.suffix("_filled"),
1362+
... nw.col("a").fill_null(nw.col("c")).name.suffix("_filled_with_c"),
13601363
... )
1361-
┌────────────────────────────────────────────────┐
1362-
| Narwhals DataFrame |
1363-
|------------------------------------------------|
1364-
|shape: (4, 4) |
1365-
|┌──────┬─────┬────────────────┬────────────────┐|
1366-
|│ a ┆ b ┆ a_nulls_filled ┆ b_nulls_filled │|
1367-
|│ --- ┆ --- ┆ --- ┆ --- │|
1368-
|│ i64 ┆ f64 ┆ i64 ┆ f64 │|
1369-
|╞══════╪═════╪════════════════╪════════════════╡|
1370-
|│ 2 ┆ 2.0 ┆ 2 ┆ 2.0 │|
1371-
|│ null ┆ NaN ┆ 0 ┆ NaN │|
1372-
|│ null ┆ NaN ┆ 0 ┆ NaN │|
1373-
|│ 3 ┆ 3.0 ┆ 3 ┆ 3.0 │|
1374-
|└──────┴─────┴────────────────┴────────────────┘|
1375-
└────────────────────────────────────────────────┘
1364+
┌────────────────────────────────────────────────────────────
1365+
| Narwhals DataFrame |
1366+
|------------------------------------------------------------|
1367+
|shape: (4, 6) |
1368+
|┌──────┬─────┬───────────────┬──────────┬─────────────────┐|
1369+
|│ a ┆ b ┆ c ┆ a_filled ┆ b_filled ┆ a_filled_with_c │|
1370+
|│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │|
1371+
|│ i64 ┆ f64 ┆ i64 ┆ i64 ┆ f64 ┆ i64 │|
1372+
|╞══════╪═════╪═══════════════╪══════════╪═════════════════╡|
1373+
|│ 2 ┆ 2.0 ┆ 1 ┆ 2 ┆ 2.0 ┆ 2 │|
1374+
|│ null ┆ NaN ┆ 2 ┆ 0 ┆ NaN ┆ 2 │|
1375+
|│ null ┆ NaN ┆ 3 ┆ 0 ┆ NaN ┆ 3 │|
1376+
|│ 3 ┆ 3.0 ┆ 4 ┆ 3 ┆ 3.0 ┆ 3 │|
1377+
|└──────┴─────┴───────────────┴──────────┴─────────────────┘|
1378+
└────────────────────────────────────────────────────────────
13761379
13771380
Using a strategy:
13781381
1379-
>>> df.with_columns(
1382+
>>> df.select(
1383+
... nw.col("a", "b"),
13801384
... nw.col("a", "b")
13811385
... .fill_null(strategy="forward", limit=1)
1382-
... .name.suffix("_nulls_forward_filled")
1386+
... .name.suffix("_nulls_forward_filled"),
13831387
... )
13841388
┌────────────────────────────────────────────────────────────────┐
13851389
| Narwhals DataFrame |
@@ -1408,7 +1412,9 @@ def fill_null(
14081412
raise ValueError(msg)
14091413
return self._from_callable(
14101414
lambda plx: self._to_compliant_expr(plx).fill_null(
1411-
value=value, strategy=strategy, limit=limit
1415+
value=extract_compliant(plx, value, str_as_lit=True),
1416+
strategy=strategy,
1417+
limit=limit,
14121418
)
14131419
)
14141420

narwhals/series.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1328,7 +1328,9 @@ def fill_null(
13281328
msg = f"strategy not supported: {strategy}"
13291329
raise ValueError(msg)
13301330
return self._from_compliant_series(
1331-
self._compliant_series.fill_null(value=value, strategy=strategy, limit=limit)
1331+
self._compliant_series.fill_null(
1332+
value=self._extract_native(value), strategy=strategy, limit=limit
1333+
)
13321334
)
13331335

13341336
def is_between(

tests/expr_and_series/fill_null_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ def test_fill_null(request: pytest.FixtureRequest, constructor: Constructor) ->
3131
assert_equal_data(result, expected)
3232

3333

34+
def test_fill_null_series_expression(
35+
request: pytest.FixtureRequest, constructor: Constructor
36+
) -> None:
37+
if "pyspark" in str(constructor):
38+
request.applymarker(pytest.mark.xfail)
39+
data = {
40+
"a": [0.0, None, 2.0, 3.0, 4.0],
41+
"b": [1.0, None, None, 5.0, 3.0],
42+
"c": [5.0, 2.0, None, 2.0, 1.0],
43+
}
44+
df = nw.from_native(constructor(data))
45+
46+
result = df.with_columns(nw.col("a", "b").fill_null(nw.col("c")))
47+
expected = {
48+
"a": [0.0, 2, 2, 3, 4],
49+
"b": [1.0, 2, None, 5, 3],
50+
"c": [5.0, 2, None, 2, 1],
51+
}
52+
assert_equal_data(result, expected)
53+
54+
3455
def test_fill_null_exceptions(constructor: Constructor) -> None:
3556
data = {
3657
"a": [0.0, None, 2.0, 3.0, 4.0],

0 commit comments

Comments
 (0)