Skip to content

Commit c6742b1

Browse files
committed
Support pl.Expr.extend_constant in cudf_polars
1 parent 15eb369 commit c6742b1

2 files changed

Lines changed: 68 additions & 0 deletions

File tree

python/cudf_polars/cudf_polars/dsl/expressions/unary.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class UnaryFunction(Expr):
105105
{
106106
"as_struct",
107107
"drop_nulls",
108+
"extend_constant",
108109
"fill_null",
109110
"fill_null_with_strategy",
110111
"mask_nans",
@@ -538,6 +539,33 @@ def do_evaluate(
538539
plc.copying.shift(column.obj, offset, fill_scalar, stream=df.stream),
539540
dtype=self.dtype,
540541
)
542+
elif self.name == "extend_constant":
543+
column = self.children[0].evaluate(df, context=context)
544+
value_expr = self.children[1]
545+
n_expr = self.children[2]
546+
if isinstance(n_expr, Literal):
547+
count = n_expr.value
548+
else:
549+
count = (
550+
n_expr.evaluate(df, context=context)
551+
.obj_scalar(stream=df.stream)
552+
.to_py(stream=df.stream)
553+
)
554+
if count == 0:
555+
return column
556+
if isinstance(value_expr, Literal):
557+
fill = plc.Scalar.from_py(
558+
value_expr.value, self.dtype.plc_type, stream=df.stream
559+
)
560+
else:
561+
fill = value_expr.evaluate(df, context=context).obj_scalar(
562+
stream=df.stream
563+
)
564+
extension = plc.Column.from_scalar(fill, count, stream=df.stream)
565+
return Column(
566+
plc.concatenate.concatenate([column.obj, extension], stream=df.stream),
567+
dtype=self.dtype,
568+
)
541569
elif self.name in self._OP_MAPPING:
542570
column = self.children[0].evaluate(df, context=context)
543571
if column.dtype.plc_type.id() != self.dtype.id():
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
from __future__ import annotations
4+
5+
import pytest
6+
7+
import polars as pl
8+
9+
from cudf_polars.testing.asserts import assert_gpu_result_equal
10+
11+
12+
@pytest.mark.parametrize(
13+
"series, value",
14+
[
15+
(pl.Series([1, 2, 3]), 0),
16+
(pl.Series([1, 2, None]), None),
17+
(pl.Series([1.5, float("nan"), -3.0]), 2.5),
18+
(pl.Series([1.5, 2.5, -3.0]), None),
19+
(pl.Series(["a", "bb", None]), "zz"),
20+
(pl.Series(["a", "bb", "c"]), None),
21+
(pl.Series([], dtype=pl.Int64), 0),
22+
(pl.Series([None, None, None], dtype=pl.Int64), 7),
23+
(pl.Series([None, None, None], dtype=pl.Int64), None),
24+
(pl.Series([42], dtype=pl.Int64), 7),
25+
(pl.Series([None], dtype=pl.Int64), 7),
26+
],
27+
)
28+
@pytest.mark.parametrize("n", [0, 1, 3])
29+
def test_extend_constant(
30+
engine: pl.GPUEngine, series: pl.Series, value: int | float | str | None, n: int
31+
) -> None:
32+
lf = pl.LazyFrame({"a": series})
33+
q = lf.select(pl.col("a").extend_constant(value, n))
34+
assert_gpu_result_equal(q, engine=engine)
35+
36+
37+
def test_extend_constant_non_literal_value_and_n(engine: pl.GPUEngine) -> None:
38+
lf = pl.LazyFrame({"a": [1, 2, 3], "v": [9, 9, 9], "n": [2, 2, 2]})
39+
q = lf.select(pl.col("a").extend_constant(pl.col("v").first(), pl.col("n").first()))
40+
assert_gpu_result_equal(q, engine=engine)

0 commit comments

Comments
 (0)