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 |
None
|
span
|
float | None
|
Specify decay in terms of span, \(\theta\), with |
None
|
half_life
|
float | None
|
Specify decay in terms of half-life, \(\tau\), with |
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
|
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.
|
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 |
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
|
required |
new
|
Sequence[Any] | None
|
Sequence of values to replace by. Length must match the length of |
None
|
return_dtype
|
DType | type[DType] | None
|
The data type of the resulting expression. If set to |
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
|
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
|
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 |
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 |
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