Skip to content

narwhals.Series

Narwhals Series, backed by a native series.

The native series might be pandas.Series, polars.Series, ...

This class is not meant to be instantiated directly - instead, use narwhals.from_native, making sure to pass allow_series=True or series_only=True.

dtype: DType property

Get the data type of the Series.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> nw.dtypes.DType:
...     s = nw.from_native(s_native, series_only=True)
...     return s.dtype

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
Int64
>>> my_library_agnostic_function(s_pl)
Int64

name: str property

Get the name of the Series.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s, name="foo")
>>> s_pl = pl.Series("foo", s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries) -> str:
...     s = nw.from_native(s_native, series_only=True)
...     return s.name

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
'foo'
>>> my_library_agnostic_function(s_pl)
'foo'

shape: tuple[int] property

Get the shape of the Series.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries) -> tuple[int]:
...     s = nw.from_native(s_native, series_only=True)
...     return s.shape

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
(3,)
>>> my_library_agnostic_function(s_pl)
(3,)

__arrow_c_stream__(requested_schema=None)

Export a Series via the Arrow PyCapsule Interface.

Narwhals doesn't implement anything itself here:

  • if the underlying series implements the interface, it'll return that
  • else, it'll call to_arrow and then defer to PyArrow's implementation

See PyCapsule Interface for more.

__getitem__(idx)

__getitem__(idx: int) -> Any
__getitem__(idx: slice | Sequence[int]) -> Self

__iter__()

abs()

Calculate the absolute value of each element.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [2, -4, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.abs().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    2
1    4
2    3
dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   2
   4
   3
]

alias(name)

Rename the Series.

Notes

This method is very cheap, but does not guarantee that data will be copied. For example:

s1: nw.Series
s2 = s1.alias("foo")
arr = s2.to_numpy()
arr[0] = 999

may (depending on the backend, and on the version) result in s1's data being modified. We recommend:

- if you need to alias an object and don't need the original
  one around any more, just use `alias` without worrying about it.
- if you were expecting `alias` to copy data, then explicily call
  `.clone` before calling `alias`.

Parameters:

Name Type Description Default
name str

The new name.

required

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s, name="foo")
>>> s_pl = pl.Series("foo", s)
>>> s_pa = pa.chunked_array([s])

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.alias("bar").to_native()

We can then pass any supported library such as pandas, Polars, or PyArrow:

>>> my_library_agnostic_function(s_pd)
0    1
1    2
2    3
Name: bar, dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: 'bar' [i64]
[
   1
   2
   3
]
>>> my_library_agnostic_function(s_pa)
<pyarrow.lib.ChunkedArray object at 0x...>
[
  [
    1,
    2,
    3
  ]
]

all()

Return whether all values in the Series are True.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [True, False, True]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.all()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
np.False_
>>> my_library_agnostic_function(s_pl)
False

any()

Return whether any of the values in the Series are True.

Notes

Only works on Series of data type Boolean.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [False, True, False]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.any()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
np.True_
>>> my_library_agnostic_function(s_pl)
True

arg_true()

Find elements where boolean Series is True.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> data = [1, None, None, 2]
>>> s_pd = pd.Series(data, name="a")
>>> s_pl = pl.Series("a", data)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.is_null().arg_true().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
1    1
2    2
Name: a, dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (2,)
Series: 'a' [u32]
[
   1
   2
]

cast(dtype)

Cast between data types.

Parameters:

Name Type Description Default
dtype DType | type[DType]

Data type that the object will be cast into.

required

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [True, False, True]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.cast(nw.Int64).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    1
1    0
2    1
dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   1
   0
   1
]

clip(lower_bound=None, upper_bound=None)

Clip values in the Series.

Parameters:

Name Type Description Default
lower_bound Any | None

Lower bound value.

None
upper_bound Any | None

Upper bound value.

None

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>>
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def clip_lower(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.clip(2).to_native()

We can then pass either pandas or Polars to clip_lower:

>>> clip_lower(s_pd)
0    2
1    2
2    3
dtype: int64
>>> clip_lower(s_pl)
shape: (3,)
Series: '' [i64]
[
   2
   2
   3
]

We define another library agnostic function:

>>> def clip_upper(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.clip(upper_bound=2).to_native()

We can then pass either pandas or Polars to clip_upper:

>>> clip_upper(s_pd)
0    1
1    2
2    2
dtype: int64
>>> clip_upper(s_pl)
shape: (3,)
Series: '' [i64]
[
   1
   2
   2
]

We can have both at the same time

>>> s = [-1, 1, -3, 3, -5, 5]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.clip(-1, 3).to_native()

We can pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0   -1
1    1
2   -1
3    3
4   -1
5    3
dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (6,)
Series: '' [i64]
[
   -1
    1
   -1
    3
   -1
    3
]

count()

Returns the number of non-null elements in the Series.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.count()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
np.int64(3)
>>> my_library_agnostic_function(s_pl)
3

cum_count(*, reverse=False)

Return the cumulative count of the non-null values in the series.

Parameters:

Name Type Description Default
reverse bool

reverse the operation

False

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> data = ["x", "k", None, "d"]

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.cum_count(reverse=True).to_native()

We can then pass any supported library such as Pandas, Polars, or PyArrow to func:

>>> my_library_agnostic_function(pd.Series(data))
0    3
1    2
2    1
3    1
dtype: int64
>>> my_library_agnostic_function(pl.Series(data))
shape: (4,)
Series: '' [u32]
[
    3
    2
    1
    1
]
>>> my_library_agnostic_function(pa.chunked_array([data]))
<pyarrow.lib.ChunkedArray object at ...>
[
  [
    3,
    2,
    1,
    1
  ]
]

cum_max(*, reverse=False)

Return the cumulative max of the non-null values in the series.

Parameters:

Name Type Description Default
reverse bool

reverse the operation

False

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> data = [1, 3, None, 2]

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.cum_max().to_native()

We can then pass any supported library such as Pandas, Polars, or PyArrow to func:

>>> my_library_agnostic_function(pd.Series(data))
0    1.0
1    3.0
2    NaN
3    3.0
dtype: float64
>>> my_library_agnostic_function(pl.Series(data))
shape: (4,)
Series: '' [i64]
[
   1
   3
   null
   3
]
>>> my_library_agnostic_function(pa.chunked_array([data]))
<pyarrow.lib.ChunkedArray object at ...>
[
  [
    1,
    3,
    null,
    3
  ]
]

cum_min(*, reverse=False)

Return the cumulative min of the non-null values in the series.

Parameters:

Name Type Description Default
reverse bool

reverse the operation

False

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> data = [3, 1, None, 2]

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.cum_min().to_native()

We can then pass any supported library such as Pandas, Polars, or PyArrow to func:

>>> my_library_agnostic_function(pd.Series(data))
0    3.0
1    1.0
2    NaN
3    1.0
dtype: float64
>>> my_library_agnostic_function(pl.Series(data))
shape: (4,)
Series: '' [i64]
[
   3
   1
   null
   1
]
>>> my_library_agnostic_function(pa.chunked_array([data]))
<pyarrow.lib.ChunkedArray object at ...>
[
  [
    3,
    1,
    null,
    1
  ]
]

cum_prod(*, reverse=False)

Return the cumulative product of the non-null values in the series.

Parameters:

Name Type Description Default
reverse bool

reverse the operation

False

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> data = [1, 3, None, 2]

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.cum_prod().to_native()

We can then pass any supported library such as Pandas, Polars, or PyArrow to func:

>>> my_library_agnostic_function(pd.Series(data))
0    1.0
1    3.0
2    NaN
3    6.0
dtype: float64
>>> my_library_agnostic_function(pl.Series(data))
shape: (4,)
Series: '' [i64]
[
   1
   3
   null
   6
]
>>> my_library_agnostic_function(pa.chunked_array([data]))
<pyarrow.lib.ChunkedArray object at ...>
[
  [
    1,
    3,
    null,
    6
  ]
]

cum_sum(*, reverse=False)

Calculate the cumulative sum.

Parameters:

Name Type Description Default
reverse bool

reverse the operation

False

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [2, 4, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.cum_sum().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    2
1    6
2    9
dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   2
   6
   9
]

diff()

Calculate the difference with the previous element, for each element.

Notes

pandas may change the dtype here, for example when introducing missing values in an integer column. To ensure, that the dtype doesn't change, you may want to use fill_null and cast. For example, to calculate the diff and fill missing values with 0 in a Int64 column, you could do:

s.diff().fill_null(0).cast(nw.Int64)

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [2, 4, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.diff().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    NaN
1    2.0
2   -1.0
dtype: float64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   null
   2
   -1
]

drop_nulls()

Drop all null values.

Notes

pandas and Polars handle null values differently. Polars distinguishes between NaN and Null, whereas pandas doesn't.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import numpy as np
>>> import narwhals as nw
>>> s_pd = pd.Series([2, 4, None, 3, 5])
>>> s_pl = pl.Series("a", [2, 4, None, 3, 5])

Now define a dataframe-agnostic function with a column argument for the column to evaluate :

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.drop_nulls().to_native()

Then we can pass either Series (polars or pandas) to func:

>>> my_library_agnostic_function(s_pd)
0    2.0
1    4.0
3    3.0
4    5.0
dtype: float64
>>> my_library_agnostic_function(s_pl)
shape: (4,)
Series: 'a' [i64]
[
   2
   4
   3
   5
]

ewm_mean(*, com=None, span=None, half_life=None, alpha=None, adjust=True, min_periods=1, ignore_nulls=False)

Compute exponentially-weighted moving average.

Warning

This functionality is considered unstable. It may be changed at any point without it being considered a breaking change.

Parameters:

Name Type Description Default
com float | None

Specify decay in terms of center of mass, \(\gamma\), with
\(\alpha = \frac{1}{1+\gamma}\forall\gamma\geq0\)

None
span float | None

Specify decay in terms of span, \(\theta\), with
\(\alpha = \frac{2}{\theta + 1} \forall \theta \geq 1\)

None
half_life float | None

Specify decay in terms of half-life, \(\tau\), with
\(\alpha = 1 - \exp \left\{ \frac{ -\ln(2) }{ \tau } \right\} \forall \tau > 0\)

None
alpha float | None

Specify smoothing factor alpha directly, \(0 < \alpha \leq 1\).

None
adjust bool

Divide by decaying adjustment factor in beginning periods to account for imbalance in relative weightings

  • When adjust=True (the default) the EW function is calculated using weights \(w_i = (1 - \alpha)^i\)
  • When adjust=False the EW function is calculated recursively by $$ y_0=x_0 $$ $$ y_t = (1 - \alpha)y_{t - 1} + \alpha x_t $$
True
min_periods int

Minimum number of observations in window required to have a value (otherwise result is null).

1
ignore_nulls bool

Ignore missing values when calculating weights.

  • When ignore_nulls=False (default), weights are based on absolute positions. For example, the weights of \(x_0\) and \(x_2\) used in calculating the final weighted average of \([x_0, None, x_2]\) are \((1-\alpha)^2\) and \(1\) if adjust=True, and \((1-\alpha)^2\) and \(\alpha\) if adjust=False.
  • When ignore_nulls=True, weights are based on relative positions. For example, the weights of \(x_0\) and \(x_2\) used in calculating the final weighted average of \([x_0, None, x_2]\) are \(1-\alpha\) and \(1\) if adjust=True, and \(1-\alpha\) and \(\alpha\) if adjust=False.
False

Returns:

Type Description
Self

Series

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> data = [1, 2, 3]
>>> s_pd = pd.Series(name="a", data=data)
>>> s_pl = pl.Series(name="a", values=data)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.ewm_mean(com=1, ignore_nulls=False).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    1.000000
1    1.666667
2    2.428571
Name: a, dtype: float64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: 'a' [f64]
[
   1.0
   1.666667
   2.428571
]

fill_null(value=None, strategy=None, limit=None)

Fill null values using the specified value.

Parameters:

Name Type Description Default
value Any | None

Value used to fill null values.

None
strategy Literal['forward', 'backward'] | None

Strategy used to fill null values.

None
limit int | None

Number of consecutive null values to fill when using the 'forward' or 'backward' strategy.

None
Notes

pandas and Polars handle null values differently. Polars distinguishes between NaN and Null, whereas pandas doesn't.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [1, 2, None]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.fill_null(5).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    1.0
1    2.0
2    5.0
dtype: float64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   1
   2
   5
]

Using a strategy:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.fill_null(strategy="forward", limit=1).to_native()
>>> my_library_agnostic_function(s_pd)
0    1.0
1    2.0
2    2.0
dtype: float64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   1
   2
   2
]

filter(other)

Filter elements in the Series based on a condition.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [4, 10, 15, 34, 50]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.filter(s > 10).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
2    15
3    34
4    50
dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   15
   34
   50
]

gather_every(n, offset=0)

Take every nth value in the Series and return as new Series.

Parameters:

Name Type Description Default
n int

Gather every n-th row.

required
offset int

Starting index.

0

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> data = [1, 2, 3, 4]
>>> s_pd = pd.Series(name="a", data=data)
>>> s_pl = pl.Series(name="a", values=data)

Let's define a dataframe-agnostic function in which gather every 2 rows, starting from a offset of 1:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.gather_every(n=2, offset=1).to_native()
>>> my_library_agnostic_function(s_pd)
1    2
3    4
Name: a, dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (2,)
Series: 'a' [i64]
[
   2
   4
]

head(n=10)

Get the first n rows.

Parameters:

Name Type Description Default
n int

Number of rows to return.

10

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> data = list(range(10))
>>> s_pd = pd.Series(data)
>>> s_pl = pl.Series(data)

Let's define a dataframe-agnostic function that returns the first 3 rows:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.head(3).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    0
1    1
2    2
dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   0
   1
   2
]

is_between(lower_bound, upper_bound, closed='both')

Get a boolean mask of the values that are between the given lower/upper bounds.

Parameters:

Name Type Description Default
lower_bound Any

Lower bound value.

required
upper_bound Any

Upper bound value.

required
closed str

Define which sides of the interval are closed (inclusive).

'both'
Notes

If the value of the lower_bound is greater than that of the upper_bound, then the values will be False, as no value can satisfy the condition.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s_pd = pd.Series([1, 2, 3, 4, 5])
>>> s_pl = pl.Series([1, 2, 3, 4, 5])

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.is_between(2, 4, "right").to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    False
1    False
2     True
3     True
4    False
dtype: bool
>>> my_library_agnostic_function(s_pl)
shape: (5,)
Series: '' [bool]
[
   false
   false
   true
   true
   false
]

is_duplicated()

Get a mask of all duplicated rows in the Series.

Returns:

Type Description
Self

A new Series.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> s_pd = pd.Series([1, 2, 3, 1])
>>> s_pl = pl.Series([1, 2, 3, 1])

Let's define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.is_duplicated().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0     True
1    False
2    False
3     True
dtype: bool
>>> my_library_agnostic_function(s_pl)
shape: (4,)
Series: '' [bool]
[
    true
    false
    false
    true
]

is_empty()

Check if the series is empty.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> import pandas as pd
>>> import polars as pl

Let's define a dataframe-agnostic function that filters rows in which "foo" values are greater than 10, and then checks if the result is empty or not:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.filter(s > 10).is_empty()

We can then pass either pandas or Polars to func:

>>> s_pd = pd.Series([1, 2, 3])
>>> s_pl = pl.Series([1, 2, 3])
>>> my_library_agnostic_function(s_pd), my_library_agnostic_function(s_pl)
(True, True)
>>> s_pd = pd.Series([100, 2, 3])
>>> s_pl = pl.Series([100, 2, 3])
>>> my_library_agnostic_function(s_pd), my_library_agnostic_function(s_pl)
(False, False)

is_finite()

Returns a boolean Series indicating which values are finite.

Warning

Different backend handle null values differently. is_finite will return False for NaN and Null's in the Dask and pandas non-nullable backend, while for Polars, PyArrow and pandas nullable backends null values are kept as such.

Returns:

Type Description
Self

Expression of Boolean data type.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> data = [float("nan"), float("inf"), 2.0, None]

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.is_finite().to_native()

We can then pass any supported library such as Pandas, Polars, or PyArrow to func:

>>> my_library_agnostic_function(pd.Series(data))
0    False
1    False
2     True
3    False
dtype: bool
>>> my_library_agnostic_function(
...     pl.Series(data)
... )
shape: (4,)
Series: '' [bool]
[
   false
   false
   true
   null
]
>>> my_library_agnostic_function(pa.chunked_array([data]))
<pyarrow.lib.ChunkedArray object at ...>
[
  [
    false,
    false,
    true,
    null
  ]
]

is_first_distinct()

Return a boolean mask indicating the first occurrence of each distinct value.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> s_pd = pd.Series([1, 1, 2, 3, 2])
>>> s_pl = pl.Series([1, 1, 2, 3, 2])

Let's define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.is_first_distinct().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0     True
1    False
2     True
3     True
4    False
dtype: bool
>>> my_library_agnostic_function(s_pl)
shape: (5,)
Series: '' [bool]
[
    true
    false
    true
    true
    false
]

is_in(other)

Check if the elements of this Series are in the other sequence.

Parameters:

Name Type Description Default
other Any

Sequence of primitive type.

required

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s_pd = pd.Series([1, 2, 3])
>>> s_pl = pl.Series([1, 2, 3])

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.is_in([3, 2, 8]).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    False
1     True
2     True
dtype: bool
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [bool]
[
   false
   true
   true
]

is_last_distinct()

Return a boolean mask indicating the last occurrence of each distinct value.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> s_pd = pd.Series([1, 1, 2, 3, 2])
>>> s_pl = pl.Series([1, 1, 2, 3, 2])

Let's define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.is_last_distinct().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    False
1     True
2    False
3     True
4     True
dtype: bool
>>> my_library_agnostic_function(s_pl)
shape: (5,)
Series: '' [bool]
[
    false
    true
    false
    true
    true
]

is_null()

Returns a boolean Series indicating which values are null.

Notes

pandas and Polars handle null values differently. Polars distinguishes between NaN and Null, whereas pandas doesn't.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [1, 2, None]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.is_null().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    False
1    False
2     True
dtype: bool
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [bool]
[
   false
   false
   true
]

is_sorted(*, descending=False)

Check if the Series is sorted.

Parameters:

Name Type Description Default
descending bool

Check if the Series is sorted in descending order.

False

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> import pandas as pd
>>> import polars as pl
>>> unsorted_data = [1, 3, 2]
>>> sorted_data = [3, 2, 1]

Let's define a dataframe-agnostic function:

>>> def my_library_agnostic_function(
...     s_native: IntoSeries, descending: bool = False
... ):
...     s = nw.from_native(s_native, series_only=True)
...     return s.is_sorted(descending=descending)

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(pl.Series(unsorted_data))
False
>>> my_library_agnostic_function(pl.Series(sorted_data), descending=True)
True
>>> my_library_agnostic_function(pd.Series(unsorted_data))
False
>>> my_library_agnostic_function(pd.Series(sorted_data), descending=True)
True

is_unique()

Get a mask of all unique rows in the Series.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> s_pd = pd.Series([1, 2, 3, 1])
>>> s_pl = pl.Series([1, 2, 3, 1])

Let's define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.is_unique().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    False
1     True
2     True
3    False
dtype: bool
>>> my_library_agnostic_function(s_pl)
shape: (4,)
Series: '' [bool]
[
    false
     true
     true
    false
]

item(index=None)

Return the Series as a scalar, or return the element at the given index.

If no index is provided, this is equivalent to s[0], with a check that the shape is (1,). With an index, this is equivalent to s[index].

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl

Let's define a dataframe-agnostic function that returns item at given index

>>> def my_library_agnostic_function(s_native: IntoSeries, index=None):
...     s = nw.from_native(s_native, series_only=True)
...     return s.item(index)

We can then pass either pandas or Polars to func:

>>> (
...     my_library_agnostic_function(pl.Series("a", [1]), None),
...     my_library_agnostic_function(pd.Series([1]), None),
... )
(1, np.int64(1))
>>> (
...     my_library_agnostic_function(pl.Series("a", [9, 8, 7]), -1),
...     my_library_agnostic_function(pl.Series([9, 8, 7]), -2),
... )
(7, 8)

len()

Return the number of elements in the Series.

Null values count towards the total.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> import pandas as pd
>>> import polars as pl
>>> data = [1, 2, None]
>>> s_pd = pd.Series(data)
>>> s_pl = pl.Series(data)

Let's define a dataframe-agnostic function that computes the len of the series:

>>> def my_library_agnostic_function(s_native: IntoSeries) -> int:
...     s = nw.from_native(s_native, series_only=True)
...     return s.len()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
3
>>> my_library_agnostic_function(s_pl)
3

max()

Get the maximum value in this Series.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.max()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
np.int64(3)
>>> my_library_agnostic_function(s_pl)
3

mean()

Reduce this Series to the mean value.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.mean()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
np.float64(2.0)
>>> my_library_agnostic_function(s_pl)
2.0

median()

Reduce this Series to the median value.

Notes

Results might slightly differ across backends due to differences in the underlying algorithms used to compute the median.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [5, 3, 8]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)
>>> s_pa = pa.chunked_array([s])

Let's define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.median()

We can then pass any supported library such as pandas, Polars, or PyArrow to func:

>>> my_library_agnostic_function(s_pd)
np.float64(5.0)
>>> my_library_agnostic_function(s_pl)
5.0
>>> my_library_agnostic_function(s_pa)
<pyarrow.DoubleScalar: 5.0>

min()

Get the minimal value in this Series.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.min()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
np.int64(1)
>>> my_library_agnostic_function(s_pl)
1

mode()

Compute the most occurring value(s).

Can return multiple values.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> data = [1, 1, 2, 2, 3]
>>> s_pd = pd.Series(name="a", data=data)
>>> s_pl = pl.Series(name="a", values=data)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.mode().sort().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    1
1    2
Name: a, dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (2,)
Series: 'a' [i64]
[
   1
   2
]

n_unique()

Count the number of unique values.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.n_unique()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
3
>>> my_library_agnostic_function(s_pl)
3

null_count()

Create a new Series that shows the null counts per column.

Notes

pandas and Polars handle null values differently. Polars distinguishes between NaN and Null, whereas pandas doesn't.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> import pandas as pd
>>> import polars as pl
>>> s_pd = pd.Series([1, None, 3])
>>> s_pl = pl.Series([1, None, None])

Let's define a dataframe-agnostic function that returns the null count of the series:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.null_count()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
np.int64(1)
>>> my_library_agnostic_function(s_pl)
2

pipe(function, *args, **kwargs)

Pipe function call.

Examples:

>>> import polars as pl
>>> import pandas as pd
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s_pd = pd.Series([1, 2, 3, 4])
>>> s_pl = pl.Series([1, 2, 3, 4])

Lets define a function to pipe into

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.pipe(lambda x: x + 2).to_native()

Now apply it to the series

>>> my_library_agnostic_function(s_pd)
0    3
1    4
2    5
3    6
dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (4,)
Series: '' [i64]
[
   3
   4
   5
   6
]

quantile(quantile, interpolation)

Get quantile value of the series.

Note

pandas and Polars may have implementation differences for a given interpolation method.

Parameters:

Name Type Description Default
quantile float

Quantile between 0.0 and 1.0.

required
interpolation Literal['nearest', 'higher', 'lower', 'midpoint', 'linear']

Interpolation method.

required

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> import pandas as pd
>>> import polars as pl
>>> data = list(range(50))
>>> s_pd = pd.Series(data)
>>> s_pl = pl.Series(data)

Let's define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return [
...         s.quantile(quantile=q, interpolation="nearest")
...         for q in (0.1, 0.25, 0.5, 0.75, 0.9)
...     ]

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
[np.int64(5), np.int64(12), np.int64(24), np.int64(37), np.int64(44)]
>>> my_library_agnostic_function(s_pl)
[5.0, 12.0, 25.0, 37.0, 44.0]

rename(name)

Rename the Series.

Alias for Series.alias().

Notes

This method is very cheap, but does not guarantee that data will be copied. For example:

s1: nw.Series
s2 = s1.rename("foo")
arr = s2.to_numpy()
arr[0] = 999

may (depending on the backend, and on the version) result in s1's data being modified. We recommend:

- if you need to rename an object and don't need the original
  one around any more, just use `rename` without worrying about it.
- if you were expecting `rename` to copy data, then explicily call
  `.clone` before calling `rename`.

Parameters:

Name Type Description Default
name str

The new name.

required

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s, name="foo")
>>> s_pl = pl.Series("foo", s)
>>> s_pa = pa.chunked_array([s])

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.rename("bar").to_native()

We can then pass any supported library such as pandas, Polars, or PyArrow:

>>> my_library_agnostic_function(s_pd)
0    1
1    2
2    3
Name: bar, dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: 'bar' [i64]
[
   1
   2
   3
]
>>> my_library_agnostic_function(s_pa)
<pyarrow.lib.ChunkedArray object at 0x...>
[
  [
    1,
    2,
    3
  ]
]

replace_strict(old, new=None, *, return_dtype=None)

Replace all values by different values.

This function must replace all non-null input values (else it raises an error).

Parameters:

Name Type Description Default
old Sequence[Any] | Mapping[Any, Any]

Sequence of values to replace. It also accepts a mapping of values to their replacement as syntactic sugar for replace_all(old=list(mapping.keys()), new=list(mapping.values())).

required
new Sequence[Any] | None

Sequence of values to replace by. Length must match the length of old.

None
return_dtype DType | type[DType] | None

The data type of the resulting expression. If set to None (default), the data type is determined automatically based on the other inputs.

None

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> df_pd = pd.DataFrame({"a": [3, 0, 1, 2]})
>>> df_pl = pl.DataFrame({"a": [3, 0, 1, 2]})
>>> df_pa = pa.table({"a": [3, 0, 1, 2]})

Let's define dataframe-agnostic functions:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.replace_strict(
...         [0, 1, 2, 3], ["zero", "one", "two", "three"], return_dtype=nw.String
...     ).to_native()

We can then pass any supported library such as Pandas, Polars, or PyArrow to func:

>>> my_library_agnostic_function(df_pd["a"])
0    three
1     zero
2      one
3      two
Name: a, dtype: object
>>> my_library_agnostic_function(df_pl["a"])
shape: (4,)
Series: 'a' [str]
[
    "three"
    "zero"
    "one"
    "two"
]
>>> my_library_agnostic_function(df_pa["a"])
<pyarrow.lib.ChunkedArray object at ...>
[
  [
    "three",
    "zero",
    "one",
    "two"
  ]
]

rolling_mean(window_size, *, min_periods=None, center=False)

Apply a rolling mean (moving mean) over the values.

Warning

This functionality is considered unstable. It may be changed at any point without it being considered a breaking change.

A window of length window_size will traverse the values. The resulting values will be aggregated to their mean.

The window at a given row will include the row itself and the window_size - 1 elements before it.

Parameters:

Name Type Description Default
window_size int

The length of the window in number of elements. It must be a strictly positive integer.

required
min_periods int | None

The number of values in the window that should be non-null before computing a result. If set to None (default), it will be set equal to window_size. If provided, it must be a strictly positive integer, and less than or equal to window_size

None
center bool

Set the labels at the center of the window.

False

Returns:

Type Description
Self

A new expression.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> data = [1.0, 2.0, 3.0, 4.0]
>>> s_pd = pd.Series(data)
>>> s_pl = pl.Series(data)
>>> s_pa = pa.chunked_array([data])

We define a library agnostic function:

>>> def agnostic_rolling_mean(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.rolling_mean(window_size=2).to_native()

We can then pass any supported library such as Pandas, Polars, or PyArrow to func:

>>> agnostic_rolling_mean(s_pd)
0    NaN
1    1.5
2    2.5
3    3.5
dtype: float64
>>> agnostic_rolling_mean(s_pl)
shape: (4,)
Series: '' [f64]
[
   null
   1.5
   2.5
   3.5
]
>>> agnostic_rolling_mean(s_pa)
<pyarrow.lib.ChunkedArray object at ...>
[
  [
    null,
    1.5,
    2.5,
    3.5
  ]
]

rolling_sum(window_size, *, min_periods=None, center=False)

Apply a rolling sum (moving sum) over the values.

Warning

This functionality is considered unstable. It may be changed at any point without it being considered a breaking change.

A window of length window_size will traverse the values. The resulting values will be aggregated to their sum.

The window at a given row will include the row itself and the window_size - 1 elements before it.

Parameters:

Name Type Description Default
window_size int

The length of the window in number of elements. It must be a strictly positive integer.

required
min_periods int | None

The number of values in the window that should be non-null before computing a result. If set to None (default), it will be set equal to window_size. If provided, it must be a strictly positive integer, and less than or equal to window_size

None
center bool

Set the labels at the center of the window.

False

Returns:

Type Description
Self

A new expression.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> import pyarrow as pa
>>> data = [1.0, 2.0, 3.0, 4.0]
>>> s_pd = pd.Series(data)
>>> s_pl = pl.Series(data)
>>> s_pa = pa.chunked_array([data])

We define a library agnostic function:

>>> def agnostic_rolling_sum(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.rolling_sum(window_size=2).to_native()

We can then pass any supported library such as Pandas, Polars, or PyArrow to func:

>>> agnostic_rolling_sum(s_pd)
0    NaN
1    3.0
2    5.0
3    7.0
dtype: float64
>>> agnostic_rolling_sum(s_pl)
shape: (4,)
Series: '' [f64]
[
   null
   3.0
   5.0
   7.0
]
>>> agnostic_rolling_sum(s_pa)
<pyarrow.lib.ChunkedArray object at ...>
[
  [
    null,
    3,
    5,
    7
  ]
]

round(decimals=0)

Round underlying floating point data by decimals digits.

Parameters:

Name Type Description Default
decimals int

Number of decimals to round by.

0
Notes

For values exactly halfway between rounded decimal values pandas behaves differently than Polars and Arrow.

pandas rounds to the nearest even value (e.g. -0.5 and 0.5 round to 0.0, 1.5 and 2.5 round to 2.0, 3.5 and 4.5 to 4.0, etc..).

Polars and Arrow round away from 0 (e.g. -0.5 to -1.0, 0.5 to 1.0, 1.5 to 2.0, 2.5 to 3.0, etc..).

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> data = [1.12345, 2.56789, 3.901234]
>>> s_pd = pd.Series(data)
>>> s_pl = pl.Series(data)

Let's define a dataframe-agnostic function that rounds to the first decimal:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.round(1).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    1.1
1    2.6
2    3.9
dtype: float64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [f64]
[
   1.1
   2.6
   3.9
]

sample(n=None, *, fraction=None, with_replacement=False, seed=None)

Sample randomly from this Series.

Parameters:

Name Type Description Default
n int | None

Number of items to return. Cannot be used with fraction.

None
fraction float | None

Fraction of items to return. Cannot be used with n.

None
with_replacement bool

Allow values to be sampled more than once.

False
seed int | None

Seed for the random number generator. If set to None (default), a random seed is generated for each sample operation.

None
Notes

The sample method returns a Series with a specified number of randomly selected items chosen from this Series. The results are not consistent across libraries.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> s_pd = pd.Series([1, 2, 3, 4])
>>> s_pl = pl.Series([1, 2, 3, 4])

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.sample(fraction=1.0, with_replacement=True).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
   a
2  3
1  2
3  4
3  4
>>> my_library_agnostic_function(s_pl)
shape: (4,)
Series: '' [i64]
[
   1
   4
   3
   4
]

scatter(indices, values)

Set value(s) at given position(s).

Parameters:

Name Type Description Default
indices int | Sequence[int]

Position(s) to set items at.

required
values Any

Values to set.

required
Note

This method always returns a new Series, without modifying the original one. Using this function in a for-loop is an anti-pattern, we recommend building up your positions and values beforehand and doing an update in one go.

For example, instead of

for i in [1, 3, 2]:
    value = some_function(i)
    s = s.scatter(i, value)

prefer

positions = [1, 3, 2]
values = [some_function(x) for x in positions]
s = s.scatter(positions, values)

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoFrameT
>>> data = {"a": [1, 2, 3], "b": [4, 5, 6]}
>>> df_pd = pd.DataFrame(data)
>>> df_pl = pl.DataFrame(data)

We define a library agnostic function:

>>> def my_library_agnostic_function(df_native: IntoFrameT) -> IntoFrameT:
...     df = nw.from_native(df_native)
...     return df.with_columns(df["a"].scatter([0, 1], [999, 888])).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(df_pd)
     a  b
0  999  4
1  888  5
2    3  6
>>> my_library_agnostic_function(df_pl)
shape: (3, 2)
┌─────┬─────┐
│ a   ┆ b   │
│ --- ┆ --- │
│ i64 ┆ i64 │
╞═════╪═════╡
│ 999 ┆ 4   │
│ 888 ┆ 5   │
│ 3   ┆ 6   │
└─────┴─────┘

shift(n)

Shift values by n positions.

Parameters:

Name Type Description Default
n int

Number of indices to shift forward. If a negative value is passed, values are shifted in the opposite direction instead.

required
Notes

pandas may change the dtype here, for example when introducing missing values in an integer column. To ensure, that the dtype doesn't change, you may want to use fill_null and cast. For example, to shift and fill missing values with 0 in a Int64 column, you could do:

s.shift(1).fill_null(0).cast(nw.Int64)

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [2, 4, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.shift(1).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    NaN
1    2.0
2    4.0
dtype: float64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   null
   2
   4
]

sort(*, descending=False, nulls_last=False)

Sort this Series. Place null values first.

Parameters:

Name Type Description Default
descending bool

Sort in descending order.

False
nulls_last bool

Place null values last instead of first.

False

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [5, None, 1, 2]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define library agnostic functions:

>>> def agnostic_sort(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.sort().to_native()
>>> def agnostic_sort_descending(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.sort(descending=True).to_native()

We can then pass either pandas or Polars to agnostic_sort:

>>> agnostic_sort(s_pd)
1    NaN
2    1.0
3    2.0
0    5.0
dtype: float64
>>> agnostic_sort(s_pl)
shape: (4,)
Series: '' [i64]
[
   null
   1
   2
   5
]
>>> agnostic_sort_descending(s_pd)
1    NaN
0    5.0
3    2.0
2    1.0
dtype: float64
>>> agnostic_sort_descending(s_pl)
shape: (4,)
Series: '' [i64]
[
   null
   5
   2
   1
]

std(*, ddof=1)

Get the standard deviation of this Series.

Parameters:

Name Type Description Default
ddof int

“Delta Degrees of Freedom”: the divisor used in the calculation is N - ddof, where N represents the number of elements.

1

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.std()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
np.float64(1.0)
>>> my_library_agnostic_function(s_pl)
1.0

sum()

Reduce this Series to the sum value.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.sum()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
np.int64(6)
>>> my_library_agnostic_function(s_pl)
6

tail(n=10)

Get the last n rows.

Parameters:

Name Type Description Default
n int

Number of rows to return.

10

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> data = list(range(10))
>>> s_pd = pd.Series(data)
>>> s_pl = pl.Series(data)

Let's define a dataframe-agnostic function that returns the last 3 rows:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.tail(3).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
7    7
8    8
9    9
dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   7
   8
   9
]

to_arrow()

Convert to arrow.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> import pandas as pd
>>> import polars as pl
>>> data = [1, 2, 3, 4]
>>> s_pd = pd.Series(name="a", data=data)
>>> s_pl = pl.Series(name="a", values=data)

Let's define a dataframe-agnostic function that converts to arrow:

>>> def my_library_agnostic_function(s_native: IntoSeries) -> pa.Array:
...     s = nw.from_native(s_native, series_only=True)
...     return s.to_arrow()
>>> my_library_agnostic_function(s_pd)
<pyarrow.lib.Int64Array object at ...>
[
    1,
    2,
    3,
    4
]
>>> my_library_agnostic_function(s_pl)
<pyarrow.lib.Int64Array object at ...>
[
    1,
    2,
    3,
    4
]

to_dummies(*, separator='_', drop_first=False)

Get dummy/indicator variables.

Parameters:

Name Type Description Default
separator str

Separator/delimiter used when generating column names.

'_'
drop_first bool

Remove the first category from the variable being encoded.

False
Notes

pandas and Polars handle null values differently. Polars distinguishes between NaN and Null, whereas pandas doesn't.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries, IntoDataFrame
>>> import pandas as pd
>>> import polars as pl
>>> data = [1, 2, 3]
>>> s_pd = pd.Series(data, name="a")
>>> s_pl = pl.Series("a", data)

Let's define a dataframe-agnostic function that rounds to the first decimal:

>>> def my_library_agnostic_function(
...     s_native: IntoSeries, drop_first: bool = False
... ) -> IntoDataFrame:
...     s = nw.from_native(s_native, series_only=True)
...     return s.to_dummies(drop_first=drop_first).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
   a_1  a_2  a_3
0    1    0    0
1    0    1    0
2    0    0    1
>>> my_library_agnostic_function(s_pd, drop_first=True)
   a_2  a_3
0    0    0
1    1    0
2    0    1
>>> my_library_agnostic_function(s_pl)
shape: (3, 3)
┌─────┬─────┬─────┐
│ a_1 ┆ a_2 ┆ a_3 │
│ --- ┆ --- ┆ --- │
│ i8  ┆ i8  ┆ i8  │
╞═════╪═════╪═════╡
│ 1   ┆ 0   ┆ 0   │
│ 0   ┆ 1   ┆ 0   │
│ 0   ┆ 0   ┆ 1   │
└─────┴─────┴─────┘
>>> my_library_agnostic_function(s_pl, drop_first=True)
shape: (3, 2)
┌─────┬─────┐
│ a_2 ┆ a_3 │
│ --- ┆ --- │
│ i8  ┆ i8  │
╞═════╪═════╡
│ 0   ┆ 0   │
│ 1   ┆ 0   │
│ 0   ┆ 1   │
└─────┴─────┘

to_frame()

Convert to dataframe.

Returns:

Type Description
DataFrame[Any]

A new DataFrame.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries, IntoDataFrame
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s, name="a")
>>> s_pl = pl.Series("a", s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries) -> IntoDataFrame:
...     s = nw.from_native(s_native, series_only=True)
...     return s.to_frame().to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
   a
0  1
1  2
2  3
>>> my_library_agnostic_function(s_pl)
shape: (3, 1)
┌─────┐
│ a   │
│ --- │
│ i64 │
╞═════╡
│ 1   │
│ 2   │
│ 3   │
└─────┘

to_list()

Convert to list.

Notes

This function converts to Python scalars. It's typically more efficient to keep your data in the format native to your original dataframe, so we recommend only calling this when you absolutely need to.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s, name="a")
>>> s_pl = pl.Series("a", s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries):
...     s = nw.from_native(s_native, series_only=True)
...     return s.to_list()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
[1, 2, 3]
>>> my_library_agnostic_function(s_pl)
[1, 2, 3]

to_numpy()

Convert to numpy.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s, name="a")
>>> s_pl = pl.Series("a", s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries) -> np.ndarray:
...     s = nw.from_native(s_native, series_only=True)
...     return s.to_numpy()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
array([1, 2, 3]...)
>>> my_library_agnostic_function(s_pl)
array([1, 2, 3]...)

to_pandas()

Convert to pandas.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s, name="a")
>>> s_pl = pl.Series("a", s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries) -> pd.Series:
...     s = nw.from_native(s_native, series_only=True)
...     return s.to_pandas()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    1
1    2
2    3
Name: a, dtype: int64
>>> my_library_agnostic_function(s_pl)
0    1
1    2
2    3
Name: a, dtype: int64

to_native()

Convert Narwhals series to native series.

Returns:

Type Description
Any

Series of class that user started with.

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [1, 2, 3]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

We define a library agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    1
1    2
2    3
dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
    1
    2
    3
]

unique(*, maintain_order=False)

Returns unique values of the series.

Parameters:

Name Type Description Default
maintain_order bool

Keep the same order as the original series. This may be more expensive to compute. Settings this to True blocks the possibility to run on the streaming engine for Polars.

False

Examples:

>>> import pandas as pd
>>> import polars as pl
>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> s = [2, 4, 4, 6]
>>> s_pd = pd.Series(s)
>>> s_pl = pl.Series(s)

Let's define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeriesT) -> IntoSeriesT:
...     s = nw.from_native(s_native, series_only=True)
...     return s.unique(maintain_order=True).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
0    2
1    4
2    6
dtype: int64
>>> my_library_agnostic_function(s_pl)
shape: (3,)
Series: '' [i64]
[
   2
   4
   6
]

value_counts(*, sort=False, parallel=False, name=None, normalize=False)

Count the occurrences of unique values.

Parameters:

Name Type Description Default
sort bool

Sort the output by count in descending order. If set to False (default), the order of the output is random.

False
parallel bool

Execute the computation in parallel. Used for Polars only.

False
name str | None

Give the resulting count column a specific name; if normalize is True defaults to "proportion", otherwise defaults to "count".

None
normalize bool

If true gives relative frequencies of the unique values

False

Returns:

Type Description
DataFrame[Any]

A new DataFrame.

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeries, IntoDataFrame
>>> import pandas as pd
>>> import polars as pl
>>> s_pd = pd.Series([1, 1, 2, 3, 2], name="s")
>>> s_pl = pl.Series(values=[1, 1, 2, 3, 2], name="s")

Let's define a dataframe-agnostic function:

>>> def my_library_agnostic_function(s_native: IntoSeries) -> IntoDataFrame:
...     s = nw.from_native(s_native, series_only=True)
...     return s.value_counts(sort=True).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(s_pd)
   s  count
0  1      2
1  2      2
2  3      1
>>> my_library_agnostic_function(s_pl)
shape: (3, 2)
┌─────┬───────┐
│ s   ┆ count │
│ --- ┆ ---   │
│ i64 ┆ u32   │
╞═════╪═══════╡
│ 1   ┆ 2     │
│ 2   ┆ 2     │
│ 3   ┆ 1     │
└─────┴───────┘

zip_with(mask, other)

Take values from self or other based on the given mask.

Where mask evaluates true, take values from self. Where mask evaluates false, take values from other.

Parameters:

Name Type Description Default
mask Self

Boolean Series

required
other Self

Series of same type.

required

Examples:

>>> import narwhals as nw
>>> from narwhals.typing import IntoSeriesT
>>> import pandas as pd
>>> import polars as pl
>>> s1_pl = pl.Series([1, 2, 3, 4, 5])
>>> s2_pl = pl.Series([5, 4, 3, 2, 1])
>>> mask_pl = pl.Series([True, False, True, False, True])
>>> s1_pd = pd.Series([1, 2, 3, 4, 5])
>>> s2_pd = pd.Series([5, 4, 3, 2, 1])
>>> mask_pd = pd.Series([True, False, True, False, True])

Let's define a dataframe-agnostic function:

>>> def my_library_agnostic_function(
...     s1_native: IntoSeriesT, mask_native: IntoSeriesT, s2_native: IntoSeriesT
... ) -> IntoSeriesT:
...     s1 = nw.from_native(s1_native, series_only=True)
...     mask = nw.from_native(mask_native, series_only=True)
...     s2 = nw.from_native(s2_native, series_only=True)
...     return s1.zip_with(mask, s2).to_native()

We can then pass either pandas or Polars to func:

>>> my_library_agnostic_function(
...     s1_pl, mask_pl, s2_pl
... )
shape: (5,)
Series: '' [i64]
[
   1
   4
   3
   2
   5
]
>>> my_library_agnostic_function(s1_pd, mask_pd, s2_pd)
0    1
1    4
2    3
3    2
4    5
dtype: int64