Skip to content

Commit

Permalink
feat: deprecate Expr.head, Expr.tail, Expr.sort, Expr.gather_every, E…
Browse files Browse the repository at this point in the history
…xpr.sample (but keep them in stable.v1) (#1791)
  • Loading branch information
MarcoGorelli authored Jan 16, 2025
1 parent 8564711 commit 21df661
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 272 deletions.
329 changes: 71 additions & 258 deletions narwhals/expr.py

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions narwhals/stable/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,91 @@ def rolling_std(
ddof=ddof,
)

def head(self, n: int = 10) -> Self:
r"""Get the first `n` rows.
Arguments:
n: Number of rows to return.
Returns:
A new expression.
"""
return self.__class__(lambda plx: self._to_compliant_expr(plx).head(n))

def tail(self, n: int = 10) -> Self:
r"""Get the last `n` rows.
Arguments:
n: Number of rows to return.
Returns:
A new expression.
"""
return self.__class__(lambda plx: self._to_compliant_expr(plx).tail(n))

def gather_every(self: Self, n: int, offset: int = 0) -> Self:
r"""Take every nth value in the Series and return as new Series.
Arguments:
n: Gather every *n*-th row.
offset: Starting index.
Returns:
A new expression.
"""
return self.__class__(
lambda plx: self._to_compliant_expr(plx).gather_every(n=n, offset=offset)
)

def sort(self, *, descending: bool = False, nulls_last: bool = False) -> Self:
"""Sort this column. Place null values first.
Arguments:
descending: Sort in descending order.
nulls_last: Place null values last instead of first.
Returns:
A new expression.
"""
return self.__class__(
lambda plx: self._to_compliant_expr(plx).sort(
descending=descending, nulls_last=nulls_last
)
)

def sample(
self: Self,
n: int | None = None,
*,
fraction: float | None = None,
with_replacement: bool = False,
seed: int | None = None,
) -> Self:
"""Sample randomly from this expression.
!!! warning
`Expr.sample` is deprecated and will be removed in a future version.
Hint: instead of `df.select(nw.col('a').sample())`, use
`df.select(nw.col('a')).sample()` instead.
Note: this will remain available in `narwhals.stable.v1`.
See [stable api](../backcompat.md/) for more information.
Arguments:
n: Number of items to return. Cannot be used with fraction.
fraction: Fraction of items to return. Cannot be used with n.
with_replacement: Allow values to be sampled more than once.
seed: Seed for the random number generator. If set to None (default), a random
seed is generated for each sample operation.
Returns:
A new expression.
"""
return self.__class__(
lambda plx: self._to_compliant_expr(plx).sample(
n, fraction=fraction, with_replacement=with_replacement, seed=seed
)
)


class Schema(NwSchema):
"""Ordered mapping of column names to their data type.
Expand Down
2 changes: 1 addition & 1 deletion narwhals/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ def validate_strict_and_pass_though(
msg = (
"`strict` in `from_native` is deprecated, please use `pass_through` instead.\n\n"
"Note: `strict` will remain available in `narwhals.stable.v1`.\n"
"See [stable api](../backcompat.md/) for more information.\n"
"See https://narwhals-dev.github.io/narwhals/backcompat/ for more information.\n"
)
issue_deprecation_warning(msg, _version="1.13.0")
pass_through = not strict
Expand Down
4 changes: 4 additions & 0 deletions tests/expr_and_series/gather_every_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest

import narwhals as nw_main
import narwhals.stable.v1 as nw
from tests.utils import ConstructorEager
from tests.utils import assert_equal_data
Expand All @@ -21,6 +22,9 @@ def test_gather_every_expr(

assert_equal_data(result, expected)

with pytest.deprecated_call():
df.select(nw_main.col("a").gather_every(n=n, offset=offset))


@pytest.mark.parametrize("n", [1, 2, 3])
@pytest.mark.parametrize("offset", [1, 2, 3])
Expand Down
6 changes: 5 additions & 1 deletion tests/expr_and_series/head_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import pytest

import narwhals as nw
import narwhals as nw_main
import narwhals.stable.v1 as nw
from tests.utils import ConstructorEager
from tests.utils import assert_equal_data

Expand All @@ -18,6 +19,9 @@ def test_head(
expected = {"a": [1, 2]}
assert_equal_data(result, expected)

with pytest.deprecated_call():
df.select(nw_main.col("a").head(5))


@pytest.mark.parametrize("n", [2, -1])
def test_head_series(constructor_eager: ConstructorEager, n: int) -> None:
Expand Down
18 changes: 9 additions & 9 deletions tests/expr_and_series/sample_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest

import narwhals as nw_main
import narwhals.stable.v1 as nw
from tests.utils import ConstructorEager
from tests.utils import assert_equal_data
Expand All @@ -18,21 +19,20 @@ def test_expr_sample(constructor_eager: ConstructorEager) -> None:
expected_series = (2,)
assert result_series == expected_series

with pytest.deprecated_call():
df.select(nw_main.col("a").sample(n=2))

def test_expr_sample_fraction(
constructor_eager: ConstructorEager, request: pytest.FixtureRequest
) -> None:
if "dask" in str(constructor_eager):
request.applymarker(pytest.mark.xfail)

def test_expr_sample_fraction(constructor_eager: ConstructorEager) -> None:
df = nw.from_native(
constructor_eager({"a": [1, 2, 3] * 10, "b": [4, 5, 6] * 10})
).lazy()
constructor_eager({"a": [1, 2, 3] * 10, "b": [4, 5, 6] * 10}), eager_only=True
)

result_expr = df.select(nw.col("a").sample(fraction=0.1)).collect().shape
result_expr = df.select(nw.col("a").sample(fraction=0.1)).shape
expected_expr = (3, 1)
assert result_expr == expected_expr

result_series = df.collect()["a"].sample(fraction=0.1).shape
result_series = df["a"].sample(fraction=0.1).shape
expected_series = (3,)
assert result_series == expected_series

Expand Down
6 changes: 6 additions & 0 deletions tests/expr_and_series/sort_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pytest

import narwhals as nw_main
import narwhals.stable.v1 as nw
from tests.utils import ConstructorEager
from tests.utils import assert_equal_data
Expand All @@ -29,6 +30,11 @@ def test_sort_expr(
nw.col("b").sort(descending=descending, nulls_last=nulls_last),
)
assert_equal_data(result, expected)
with pytest.deprecated_call():
df.select(
"a",
nw_main.col("b").sort(descending=descending, nulls_last=nulls_last),
)


@pytest.mark.parametrize(
Expand Down
10 changes: 7 additions & 3 deletions tests/expr_and_series/tail_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

import pytest

import narwhals as nw
import narwhals as nw_main
import narwhals.stable.v1 as nw
from tests.utils import ConstructorEager
from tests.utils import assert_equal_data


@pytest.mark.parametrize("n", [2, -1])
def test_head(
def test_tail(
constructor_eager: ConstructorEager, n: int, request: pytest.FixtureRequest
) -> None:
if "polars" in str(constructor_eager) and n < 0:
Expand All @@ -18,9 +19,12 @@ def test_head(
expected = {"a": [2, 3]}
assert_equal_data(result, expected)

with pytest.deprecated_call():
df.select(nw_main.col("a").tail(5))


@pytest.mark.parametrize("n", [2, -1])
def test_head_series(constructor_eager: ConstructorEager, n: int) -> None:
def test_tail_series(constructor_eager: ConstructorEager, n: int) -> None:
df = nw.from_native(constructor_eager({"a": [1, 2, 3]}), eager_only=True)
result = df.select(df["a"].tail(n))
expected = {"a": [2, 3]}
Expand Down

0 comments on commit 21df661

Please sign in to comment.