Ë
    Ôªg•b ã                  óœ  — d dl mZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlm	Z	 d d	lm
Z
 d d
lmZ d dlmZ d dlmZ d dlmZ d dlmZ er$d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ 	 	 	 	 	 	 d>d„Z G d„ d«      Z ede¬«      Z G d„ dee   «      Z G d„ dee   «      Z G d„ dee   «      Z  G d „ d!ee   «      Z! G d"„ d#ee   «      Z"d?d$„Z#d@d%„Z$dAd&„Z%dAd'„Z&dBd(„Z'dBd)„Z(dBd*„Z)dBd+„Z*dBd,„Z+dCd-„Z,dCd.„Z-dCd/„Z. G d0„ d1«      Z/ G d2„ d3e«      Z0dDd4„Z1dCd5„Z2dEdFd7„Z3dCd8„Z4dCd9„Z5d:d;d<œ	 	 	 	 	 	 	 	 	 dGd=„Z6dgZ7y6)Hé    )Úannotations)ÚTYPE_CHECKING)ÚAny)ÚCallable)ÚGeneric)ÚIterable)ÚLiteral)ÚMapping)ÚSequence)ÚTypeVar)Úis_numpy_array)Ú_validate_dtype)Ú_validate_rolling_arguments)Úflatten)ÚSelf)ÚDType)ÚCompliantExpr)ÚCompliantNamespace)ÚCompliantSeriesT_co)ÚIntoExprc                ó„   — ddl m} t        |t        «      r|j	                  | «      S t        ||«      r|j
                  S |S )Nr   )ÚSeries)Únarwhals.seriesr   Ú
isinstanceÚExprÚ_to_compliant_exprÚ_compliant_series)ÚplxÚotherr   s      úB/var/www/openai/venv/lib/python3.12/site-packages/narwhals/expr.pyÚextract_compliantr!      s=   € õ 'ä%œÔØ×'Ñ'¨Ó,Ð,Ü%˜Ô Ø×&Ñ&Ð&Ø€Ló    c                  ó¤  — e Zd Zdjd„Zdkd„Zdld„Zdmd„Zdnd„Zdod„Zdod„Z	dpd„Z
dpd	„Zdpd
„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdpd„Zdkd„Z dkd„Z!dkd „Z"d!d!d!d!d"d#d$d%œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dqd&„Z#dkd'„Z$dkd(„Z%d#d)œdrd*„Z&d#d)œdrd+„Z'	 ds	 	 	 	 	 dtd,„Z(dud-„Z)dvd.„Z*dkd/„Z+dkd0„Z,dkd1„Z-dkd2„Z.dkd3„Z/dkd4„Z0d$d5œdwd6„Z1dkd7„Z2d$d8œdxd9„Z3dkd:„Z4dyd;„Z5	 dsd!d<œ	 	 	 	 	 	 	 dzd=„Z6d$d$d>œd{d?„Z7	 d|	 	 	 	 	 	 	 d}d@„Z8dpdA„Z9d~dB„Z:dkdC„Z;dkdD„Z<dkdE„Z=	 	 	 d	 	 	 	 	 	 	 d€dF„Z>dkdG„Z?	 dsd!d$d!dHœ	 	 	 	 	 	 	 	 	 	 	 ddI„Z@d‚dJ„ZAdkdK„ZBdkdL„ZCdkdM„ZDdkdN„ZEdkdO„ZF	 	 	 	 	 	 dƒdP„ZGd„dydQ„ZHd„dydR„ZId…d†dS„ZJdkdT„ZKd…d‡dU„ZL	 	 dˆ	 	 	 	 	 d‰dV„ZMdudW„ZNdudX„ZOd$d8œdxdY„ZPd$d8œdxdZ„ZQd$d8œdxd[„ZRd$d8œdxd\„ZSd!d$d]œ	 	 	 	 	 	 	 	 	 dŠd^„ZTd!d$d]œ	 	 	 	 	 	 	 	 	 dŠd_„ZUd!d$d#d`œ	 	 	 	 	 	 	 	 	 	 	 d‹da„ZVd!d$d#d`œ	 	 	 	 	 	 	 	 	 	 	 d‹db„ZW	 dŒd$dcœ	 	 	 	 	 	 	 ddd„ZXeYdŽde„«       ZZeYddf„«       Z[eYddg„«       Z\eYd‘dh„«       Z]eYd’di„«       Z^y!)“r   c                ó   — || _         y ©N)r   )ÚselfÚto_compliant_exprs     r    Ú__init__zExpr.__init__)   s
   € à"3ˆÕr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )Nc                ó^   •— ‰j                  | «      j                  «       j                  «       S r%   )r   ÚabsÚsum©r   r&   s    €r    Ú<lambda>z$Expr._taxicab_norm.<locals>.<lambda>0   s"   ø€ ¨$×*AÑ*AÀ#Ó*F×*JÑ*JÓ*L×*PÑ*PÔ*Rr"   ©Ú	__class__©r&   s   `r    Ú_taxicab_normzExpr._taxicab_norm-   s   ø€ ð ~‰~ÓRÓSÐSr"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )uš  Rename the expression.

        Arguments:
            name: The new name.

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_alias(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select((nw.col("b") + 10).alias("c")).to_native()

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

            >>> agnostic_alias(df_pd)
                c
            0  14
            1  15

            >>> agnostic_alias(df_pl)
            shape: (2, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ c   â”‚
            â”‚ --- â”‚
            â”‚ i64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 14  â”‚
            â”‚ 15  â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_alias(df_pa)
            pyarrow.Table
            c: int64
            ----
            c: [[14,15]]

        c                óD   •— ‰j                  | «      j                  ‰«      S r%   )r   Úalias)r   Únamer&   s    €€r    r.   zExpr.alias.<locals>.<lambda>h   s   ø€ ¨$×*AÑ*AÀ#Ó*F×*LÑ*LÈTÔ*Rr"   r/   )r&   r6   s   ``r    r5   z
Expr.alias3   s   ù€ ðj ~‰~ÔRÓSÐSr"   c                ó   —  || g|¢­i |¤ŽS )u\  Pipe function call.

        Arguments:
            function: Function to apply.
            args: Positional arguments to pass to function.
            kwargs: Keyword arguments to pass to function.

        Returns:
            A new expression.

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

            Lets define a library-agnostic function:

            >>> def agnostic_pipe(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").pipe(lambda x: x + 1)).to_native()

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

            >>> agnostic_pipe(df_pd)
               a
            0  2
            1  3
            2  4
            3  5

            >>> agnostic_pipe(df_pl)
            shape: (4, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ i64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 2   â”‚
            â”‚ 3   â”‚
            â”‚ 4   â”‚
            â”‚ 5   â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_pipe(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[2,3,4,5]]
        © )r&   ÚfunctionÚargsÚkwargss       r    Úpipez	Expr.pipej   s   € ñt ˜Ð.˜tÒ. vÑ.Ð.r"   c                óF   ‡ ‡— t        ‰«       ‰ j                  ˆˆ fd„«      S )uö  Redefine an object's data type.

        Arguments:
            dtype: Data type that the object will be cast into.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_cast(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("foo").cast(nw.Float32), nw.col("bar").cast(nw.UInt8)
            ...     ).to_native()

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

            >>> agnostic_cast(df_pd)
               foo  bar
            0  1.0    6
            1  2.0    7
            2  3.0    8
            >>> agnostic_cast(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ foo â”† bar â”‚
            â”‚ --- â”† --- â”‚
            â”‚ f32 â”† u8  â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 1.0 â”† 6   â”‚
            â”‚ 2.0 â”† 7   â”‚
            â”‚ 3.0 â”† 8   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜
            >>> agnostic_cast(df_pa)
            pyarrow.Table
            foo: float
            bar: uint8
            ----
            foo: [[1,2,3]]
            bar: [[6,7,8]]
        c                óD   •— ‰j                  | «      j                  ‰«      S r%   )r   Úcast)r   Údtyper&   s    €€r    r.   zExpr.cast.<locals>.<lambda>à   s   ø€ ˜×/Ñ/°Ó4×9Ñ9¸%Ô@r"   )r   r0   )r&   r@   s   ``r    r?   z	Expr.cast¦   s"   ù€ ôp 	˜ÔØ~‰~Ü@ó
ð 	
r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__eq__r!   ©r   r   r&   s    €€r    r.   zExpr.__eq__.<locals>.<lambda>æ   ó$   ø€ ˜×/Ñ/°Ó4×;Ñ;Ô<MÈcÐSXÓ<YÔZr"   r/   ©r&   r   s   ``r    rC   zExpr.__eq__ä   ó   ù€ Ø~‰~ÜZó
ð 	
r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__ne__r!   rD   s    €€r    r.   zExpr.__ne__.<locals>.<lambda>ë   rE   r"   r/   rF   s   ``r    rJ   zExpr.__ne__é   rG   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__and__r!   rD   s    €€r    r.   zExpr.__and__.<locals>.<lambda>ð   ó%   ø€ ˜×/Ñ/°Ó4×<Ñ<Ü! # uÓ-ôr"   r/   rF   s   ``r    rM   zExpr.__and__î   ó   ù€ Ø~‰~ôó
ð 	
r"   c                ó6   ‡ ‡— dˆˆ fd„}‰ j                  |«      S )Nc                óp   •— | j                  t        | ‰«      d ¬«      j                  t        | ‰«      «      S ©N)r@   )Úlitr!   rM   rD   s    €€r    ÚfunczExpr.__rand__.<locals>.funcö   ó5   ø€ Ø—7‘7Ô,¨S°%Ó8À7ÓE×MÑMÜ! # tÓ,óð r"   ©r   zCompliantNamespace[Any]ÚreturnzCompliantExpr[Any]r/   ©r&   r   rT   s   `` r    Ú__rand__zExpr.__rand__õ   ó   ù€ ö	ð
 ~‰~˜dÓ#Ð#r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__or__r!   rD   s    €€r    r.   zExpr.__or__.<locals>.<lambda>ÿ   rE   r"   r/   rF   s   ``r    r]   zExpr.__or__ý   rG   r"   c                ó6   ‡ ‡— dˆˆ fd„}‰ j                  |«      S )Nc                óp   •— | j                  t        | ‰«      d ¬«      j                  t        | ‰«      «      S rR   )rS   r!   r]   rD   s    €€r    rT   zExpr.__ror__.<locals>.func  s5   ø€ Ø—7‘7Ô,¨S°%Ó8À7ÓE×LÑLÜ! # tÓ,óð r"   rV   r/   rX   s   `` r    Ú__ror__zExpr.__ror__  rZ   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__add__r!   rD   s    €€r    r.   zExpr.__add__.<locals>.<lambda>  rN   r"   r/   rF   s   ``r    rc   zExpr.__add__
  rO   r"   c                ó6   ‡ ‡— dˆˆ fd„}‰ j                  |«      S )Nc                óp   •— | j                  t        | ‰«      d ¬«      j                  t        | ‰«      «      S rR   )rS   r!   rc   rD   s    €€r    rT   zExpr.__radd__.<locals>.func  rU   r"   rV   r/   rX   s   `` r    Ú__radd__zExpr.__radd__  rZ   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__sub__r!   rD   s    €€r    r.   zExpr.__sub__.<locals>.<lambda>  rN   r"   r/   rF   s   ``r    ri   zExpr.__sub__  rO   r"   c                ó6   ‡ ‡— dˆˆ fd„}‰ j                  |«      S )Nc                óp   •— | j                  t        | ‰«      d ¬«      j                  t        | ‰«      «      S rR   )rS   r!   ri   rD   s    €€r    rT   zExpr.__rsub__.<locals>.func!  rU   r"   rV   r/   rX   s   `` r    Ú__rsub__zExpr.__rsub__   rZ   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__truediv__r!   rD   s    €€r    r.   z"Expr.__truediv__.<locals>.<lambda>*  s%   ø€ ˜×/Ñ/°Ó4×@Ñ@Ü! # uÓ-ôr"   r/   rF   s   ``r    ro   zExpr.__truediv__(  rO   r"   c                ó6   ‡ ‡— dˆˆ fd„}‰ j                  |«      S )Nc                óp   •— | j                  t        | ‰«      d ¬«      j                  t        | ‰«      «      S rR   )rS   r!   ro   rD   s    €€r    rT   zExpr.__rtruediv__.<locals>.func0  s5   ø€ Ø—7‘7Ô,¨S°%Ó8À7ÓE×QÑQÜ! # tÓ,óð r"   rV   r/   rX   s   `` r    Ú__rtruediv__zExpr.__rtruediv__/  rZ   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__mul__r!   rD   s    €€r    r.   zExpr.__mul__.<locals>.<lambda>9  rN   r"   r/   rF   s   ``r    ru   zExpr.__mul__7  rO   r"   c                ó6   ‡ ‡— dˆˆ fd„}‰ j                  |«      S )Nc                óp   •— | j                  t        | ‰«      d ¬«      j                  t        | ‰«      «      S rR   )rS   r!   ru   rD   s    €€r    rT   zExpr.__rmul__.<locals>.func?  rU   r"   rV   r/   rX   s   `` r    Ú__rmul__zExpr.__rmul__>  rZ   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__le__r!   rD   s    €€r    r.   zExpr.__le__.<locals>.<lambda>H  rE   r"   r/   rF   s   ``r    r{   zExpr.__le__F  rG   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__lt__r!   rD   s    €€r    r.   zExpr.__lt__.<locals>.<lambda>M  rE   r"   r/   rF   s   ``r    r~   zExpr.__lt__K  rG   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__gt__r!   rD   s    €€r    r.   zExpr.__gt__.<locals>.<lambda>R  rE   r"   r/   rF   s   ``r    r   zExpr.__gt__P  rG   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__ge__r!   rD   s    €€r    r.   zExpr.__ge__.<locals>.<lambda>W  rE   r"   r/   rF   s   ``r    r„   zExpr.__ge__U  rG   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__pow__r!   rD   s    €€r    r.   zExpr.__pow__.<locals>.<lambda>\  rN   r"   r/   rF   s   ``r    r‡   zExpr.__pow__Z  rO   r"   c                ó6   ‡ ‡— dˆˆ fd„}‰ j                  |«      S )Nc                óp   •— | j                  t        | ‰«      d ¬«      j                  t        | ‰«      «      S rR   )rS   r!   r‡   rD   s    €€r    rT   zExpr.__rpow__.<locals>.funcb  rU   r"   rV   r/   rX   s   `` r    Ú__rpow__zExpr.__rpow__a  rZ   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__floordiv__r!   rD   s    €€r    r.   z#Expr.__floordiv__.<locals>.<lambda>k  s%   ø€ ˜×/Ñ/°Ó4×AÑAÜ! # uÓ-ôr"   r/   rF   s   ``r    r   zExpr.__floordiv__i  rO   r"   c                ó6   ‡ ‡— dˆˆ fd„}‰ j                  |«      S )Nc                óp   •— | j                  t        | ‰«      d ¬«      j                  t        | ‰«      «      S rR   )rS   r!   r   rD   s    €€r    rT   z Expr.__rfloordiv__.<locals>.funcq  s5   ø€ Ø—7‘7Ô,¨S°%Ó8À7ÓE×RÑRÜ! # tÓ,óð r"   rV   r/   rX   s   `` r    Ú__rfloordiv__zExpr.__rfloordiv__p  rZ   r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú__mod__r!   rD   s    €€r    r.   zExpr.__mod__.<locals>.<lambda>z  rN   r"   r/   rF   s   ``r    r“   zExpr.__mod__x  rO   r"   c                ó6   ‡ ‡— dˆˆ fd„}‰ j                  |«      S )Nc                óp   •— | j                  t        | ‰«      d ¬«      j                  t        | ‰«      «      S rR   )rS   r!   r“   rD   s    €€r    rT   zExpr.__rmod__.<locals>.func€  rU   r"   rV   r/   rX   s   `` r    Ú__rmod__zExpr.__rmod__  rZ   r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )Nc                óB   •— ‰j                  | «      j                  «       S r%   )r   Ú
__invert__r-   s    €r    r.   z!Expr.__invert__.<locals>.<lambda>‰  ó   ø€ ¨$×*AÑ*AÀ#Ó*F×*QÑ*QÔ*Sr"   r/   r1   s   `r    r™   zExpr.__invert__ˆ  s   ø€ Ø~‰~ÓSÓTÐTr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u  Return whether any of the values in the column are `True`.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [True, False], "b": [True, True]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_any(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").any()).to_native()

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

            >>> agnostic_any(df_pd)
                  a     b
            0  True  True

            >>> agnostic_any(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”† b    â”‚
            â”‚ ---  â”† ---  â”‚
            â”‚ bool â”† bool â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•¡
            â”‚ true â”† true â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_any(df_pa)
            pyarrow.Table
            a: bool
            b: bool
            ----
            a: [[true]]
            b: [[true]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úanyr-   s    €r    r.   zExpr.any.<locals>.<lambda>¼  ó   ø€ ¨$×*AÑ*AÀ#Ó*F×*JÑ*JÔ*Lr"   r/   r1   s   `r    r   zExpr.any‹  ó   ø€ ðb ~‰~ÓLÓMÐMr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u  Return whether all values in the column are `True`.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [True, False], "b": [True, True]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_all(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").all()).to_native()

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

            >>> agnostic_all(df_pd)
                   a     b
            0  False  True

            >>> agnostic_all(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”
            â”‚ a     â”† b    â”‚
            â”‚ ---   â”† ---  â”‚
            â”‚ bool  â”† bool â”‚
            â•žâ•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•¡
            â”‚ false â”† true â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_all(df_pa)
            pyarrow.Table
            a: bool
            b: bool
            ----
            a: [[false]]
            b: [[true]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úallr-   s    €r    r.   zExpr.all.<locals>.<lambda>ï  rž   r"   r/   r1   s   `r    r¢   zExpr.all¾  rŸ   r"   NTé   F©ÚcomÚspanÚ	half_lifeÚalphaÚadjustÚmin_periodsÚignore_nullsc          
     óH   ‡ ‡‡‡‡‡‡‡— ‰ j                  ˆˆˆˆˆˆˆ ˆfd„«      S )uÓ  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.

        Arguments:
            com: Specify decay in terms of center of mass, $\gamma$, with <br> $\alpha = \frac{1}{1+\gamma}\forall\gamma\geq0$
            span: Specify decay in terms of span, $\theta$, with <br> $\alpha = \frac{2}{\theta + 1} \forall \theta \geq 1$
            half_life: Specify decay in terms of half-life, $\tau$, with <br> $\alpha = 1 - \exp \left\{ \frac{ -\ln(2) }{ \tau } \right\} \forall \tau > 0$
            alpha: Specify smoothing factor alpha directly, $0 < \alpha \leq 1$.
            adjust: 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
                  $$
            min_periods: Minimum number of observations in window required to have a value, (otherwise result is null).
            ignore_nulls: 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`.

        Returns:
            Expr

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

            We define a library agnostic function:

            >>> def agnostic_ewm_mean(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a").ewm_mean(com=1, ignore_nulls=False)
            ...     ).to_native()

            We can then pass either pandas or Polars to `agnostic_ewm_mean`:

            >>> agnostic_ewm_mean(df_pd)
                      a
            0  1.000000
            1  1.666667
            2  2.428571

            >>> agnostic_ewm_mean(df_pl)  # doctest: +NORMALIZE_WHITESPACE
            shape: (3, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a        â”‚
            â”‚ ---      â”‚
            â”‚ f64      â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 1.0      â”‚
            â”‚ 1.666667 â”‚
            â”‚ 2.428571 â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
        c           	     óR   •— ‰j                  | «      j                  ‰‰‰‰‰‰‰¬«      S )Nr¤   )r   Úewm_mean)	r   r©   r¨   r¥   r§   r«   rª   r&   r¦   s	    €€€€€€€€r    r.   zExpr.ewm_mean.<locals>.<lambda>L  s5   ø€ ˜×/Ñ/°Ó4×=Ñ=ØØØ#ØØØ'Ø)ð >ô r"   r/   )r&   r¥   r¦   r§   r¨   r©   rª   r«   s   ````````r    r®   zExpr.ewm_meanñ  s!   ÿ€ ðt ~‰~÷ò ó

ð 
	
r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )uº  Get mean value.

        Returns:
            A new expression.

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [-1, 0, 1], "b": [2, 4, 6]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_mean(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").mean()).to_native()

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

            >>> agnostic_mean(df_pd)
                 a    b
            0  0.0  4.0

            >>> agnostic_mean(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ f64 â”† f64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 0.0 â”† 4.0 â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_mean(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[0]]
            b: [[4]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úmeanr-   s    €r    r.   zExpr.mean.<locals>.<lambda>ˆ  ó   ø€ ¨$×*AÑ*AÀ#Ó*F×*KÑ*KÔ*Mr"   r/   r1   s   `r    r±   z	Expr.meanW  ó   ø€ ðb ~‰~ÓMÓNÐNr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u]  Get median value.

        Returns:
            A new expression.

        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 IntoFrameT
            >>>
            >>> data = {"a": [1, 8, 3], "b": [4, 5, 2]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_median(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").median()).to_native()

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

            >>> agnostic_median(df_pd)
                 a    b
            0  3.0  4.0

            >>> agnostic_median(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ f64 â”† f64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 3.0 â”† 4.0 â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_median(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[3]]
            b: [[4]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úmedianr-   s    €r    r.   zExpr.median.<locals>.<lambda>¾  ó   ø€ ¨$×*AÑ*AÀ#Ó*F×*MÑ*MÔ*Or"   r/   r1   s   `r    r¶   zExpr.medianŠ  s   ø€ ðh ~‰~ÓOÓPÐPr"   ©Úddofc               ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )uD  Get standard deviation.

        Arguments:
            ddof: "Delta Degrees of Freedom": the divisor used in the calculation is N - ddof,
                where N represents the number of elements. By default ddof is 1.

        Returns:
            A new expression.

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [20, 25, 60], "b": [1.5, 1, -1.4]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_std(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").std(ddof=0)).to_native()

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

            >>> agnostic_std(df_pd)
                      a         b
            0  17.79513  1.265789
            >>> agnostic_std(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a        â”† b        â”‚
            â”‚ ---      â”† ---      â”‚
            â”‚ f64      â”† f64      â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 17.79513 â”† 1.265789 â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
            >>> agnostic_std(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[17.795130420052185]]
            b: [[1.2657891697365016]]

        c                óF   •— ‰j                  | «      j                  ‰¬«      S ©Nr¸   )r   Ústd©r   r¹   r&   s    €€r    r.   zExpr.std.<locals>.<lambda>ô  ó   ø€ ¨$×*AÑ*AÀ#Ó*F×*JÑ*JÐPTÐ*JÔ*Ur"   r/   ©r&   r¹   s   ``r    r½   zExpr.stdÀ  s   ù€ ðh ~‰~ÔUÓVÐVr"   c               ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )u]  Get variance.

        Arguments:
            ddof: "Delta Degrees of Freedom": the divisor used in the calculation is N - ddof,
                     where N represents the number of elements. By default ddof is 1.

        Returns:
            A new expression.

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [20, 25, 60], "b": [1.5, 1, -1.4]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_var(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").var(ddof=0)).to_native()

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

            >>> agnostic_var(df_pd)
                        a         b
            0  316.666667  1.602222

            >>> agnostic_var(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a          â”† b        â”‚
            â”‚ ---        â”† ---      â”‚
            â”‚ f64        â”† f64      â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 316.666667 â”† 1.602222 â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_var(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[316.6666666666667]]
            b: [[1.6022222222222222]]
        c                óF   •— ‰j                  | «      j                  ‰¬«      S r¼   )r   Úvarr¾   s    €€r    r.   zExpr.var.<locals>.<lambda>+  r¿   r"   r/   rÀ   s   ``r    rÃ   zExpr.varö  s   ù€ ðj ~‰~ÔUÓVÐVr"   c                ó4   ‡ ‡‡— ‰ j                  ˆˆˆ fd„«      S )uâ  Apply a custom python function to a whole Series or sequence of Series.

        The output of this custom function is presumed to be either a Series,
        or a NumPy array (in which case it will be automatically converted into
        a Series).

        Arguments:
            function: Function to apply to Series.
            return_dtype: Dtype of the output Series.
                If not set, the dtype will be inferred based on the first non-null value
                that is returned by the function.

        Returns:
            A new expression.

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import pyarrow as pa
            >>> 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)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_map_batches(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a", "b").map_batches(
            ...             lambda s: s.to_numpy() + 1, return_dtype=nw.Float64
            ...         )
            ...     ).to_native()

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

            >>> agnostic_map_batches(df_pd)
                 a    b
            0  2.0  5.0
            1  3.0  6.0
            2  4.0  7.0
            >>> agnostic_map_batches(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ f64 â”† f64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 2.0 â”† 5.0 â”‚
            â”‚ 3.0 â”† 6.0 â”‚
            â”‚ 4.0 â”† 7.0 â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜
            >>> agnostic_map_batches(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[2,3,4]]
            b: [[5,6,7]]
        c                óH   •— ‰j                  | «      j                  ‰‰¬«      S )N)r9   Úreturn_dtype)r   Úmap_batches)r   r9   rÆ   r&   s    €€€r    r.   z"Expr.map_batches.<locals>.<lambda>s  s&   ø€ ˜×/Ñ/°Ó4×@Ñ@Ø!°ð Aô r"   r/   )r&   r9   rÆ   s   ```r    rÇ   zExpr.map_batches-  s   ú€ ðJ ~‰~õó
ð 	
r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )uk  Calculate the sample skewness of a column.

        Returns:
            An expression representing the sample skewness of the column.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_skew(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").skew()).to_native()

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

            >>> agnostic_skew(df_pd)
                 a         b
            0  0.0  1.472427

            >>> agnostic_skew(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b        â”‚
            â”‚ --- â”† ---      â”‚
            â”‚ f64 â”† f64      â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 0.0 â”† 1.472427 â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_skew(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[0]]
            b: [[1.4724267269058975]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úskewr-   s    €r    r.   zExpr.skew.<locals>.<lambda>©  r²   r"   r/   r1   s   `r    rÊ   z	Expr.skewx  r³   r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u´  Return the sum value.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [5, 10], "b": [50, 100]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_sum(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").sum()).to_native()

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

            >>> agnostic_sum(df_pd)
                a    b
            0  15  150
            >>> agnostic_sum(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ i64 â”† i64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 15  â”† 150 â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜
            >>> agnostic_sum(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[15]]
            b: [[150]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   r,   r-   s    €r    r.   zExpr.sum.<locals>.<lambda>Ú  rž   r"   r/   r1   s   `r    r,   zExpr.sum«  s   ø€ ð^ ~‰~ÓLÓMÐMr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u¼  Returns the minimum value(s) from a column(s).

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_min(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.min("a", "b")).to_native()

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

            >>> agnostic_min(df_pd)
               a  b
            0  1  3

            >>> agnostic_min(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ i64 â”† i64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 1   â”† 3   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_min(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[1]]
            b: [[3]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úminr-   s    €r    r.   zExpr.min.<locals>.<lambda>  rž   r"   r/   r1   s   `r    rÏ   zExpr.minÜ  rŸ   r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )uÊ  Returns the maximum value(s) from a column(s).

        Returns:
            A new expression.

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [10, 20], "b": [50, 100]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_max(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.max("a", "b")).to_native()

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

            >>> agnostic_max(df_pd)
                a    b
            0  20  100

            >>> agnostic_max(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ i64 â”† i64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 20  â”† 100 â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_max(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[20]]
            b: [[100]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úmaxr-   s    €r    r.   zExpr.max.<locals>.<lambda>@  rž   r"   r/   r1   s   `r    rÒ   zExpr.max  rŸ   r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )uû  Returns the index of the minimum value.

        Returns:
            A new expression.

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [10, 20], "b": [150, 100]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_arg_min(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a", "b").arg_min().name.suffix("_arg_min")
            ...     ).to_native()

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

            >>> agnostic_arg_min(df_pd)
               a_arg_min  b_arg_min
            0          0          1

            >>> agnostic_arg_min(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a_arg_min â”† b_arg_min â”‚
            â”‚ ---       â”† ---       â”‚
            â”‚ u32       â”† u32       â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 0         â”† 1         â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_arg_min(df_pa)
            pyarrow.Table
            a_arg_min: int64
            b_arg_min: int64
            ----
            a_arg_min: [[0]]
            b_arg_min: [[1]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úarg_minr-   s    €r    r.   zExpr.arg_min.<locals>.<lambda>u  ó   ø€ ¨$×*AÑ*AÀ#Ó*F×*NÑ*NÔ*Pr"   r/   r1   s   `r    rÕ   zExpr.arg_minB  ó   ø€ ðf ~‰~ÓPÓQÐQr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )uû  Returns the index of the maximum value.

        Returns:
            A new expression.

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [10, 20], "b": [150, 100]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_arg_max(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a", "b").arg_max().name.suffix("_arg_max")
            ...     ).to_native()

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

            >>> agnostic_arg_max(df_pd)
               a_arg_max  b_arg_max
            0          1          0

            >>> agnostic_arg_max(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a_arg_max â”† b_arg_max â”‚
            â”‚ ---       â”† ---       â”‚
            â”‚ u32       â”† u32       â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 1         â”† 0         â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_arg_max(df_pa)
            pyarrow.Table
            a_arg_max: int64
            b_arg_max: int64
            ----
            a_arg_max: [[1]]
            b_arg_max: [[0]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úarg_maxr-   s    €r    r.   zExpr.arg_max.<locals>.<lambda>ª  rÖ   r"   r/   r1   s   `r    rÚ   zExpr.arg_maxw  r×   r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u×  Returns the number of non-null elements in the column.

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_count(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.all().count()).to_native()

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

            >>> agnostic_count(df_pd)
               a  b
            0  3  2

            >>> agnostic_count(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ u32 â”† u32 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 3   â”† 2   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_count(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[3]]
            b: [[2]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úcountr-   s    €r    r.   zExpr.count.<locals>.<lambda>Ý  s   ø€ ¨$×*AÑ*AÀ#Ó*F×*LÑ*LÔ*Nr"   r/   r1   s   `r    rÝ   z
Expr.count¬  s   ø€ ðb ~‰~ÓNÓOÐOr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )uá  Returns count of unique values.

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_n_unique(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").n_unique()).to_native()

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

            >>> agnostic_n_unique(df_pd)
               a  b
            0  5  3
            >>> agnostic_n_unique(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ u32 â”† u32 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 5   â”† 3   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜
            >>> agnostic_n_unique(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[5]]
            b: [[3]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Ún_uniquer-   s    €r    r.   zExpr.n_unique.<locals>.<lambda>  ó   ø€ ¨$×*AÑ*AÀ#Ó*F×*OÑ*OÔ*Qr"   r/   r1   s   `r    rà   zExpr.n_uniqueß  s   ø€ ð^ ~‰~ÓQÓRÐRr"   ©Úmaintain_orderc               ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )ud  Return unique values of this expression.

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

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_unique(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").unique(maintain_order=True)).to_native()

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

            >>> agnostic_unique(df_pd)
               a  b
            0  1  2
            1  3  4
            2  5  6

            >>> agnostic_unique(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ i64 â”† i64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 1   â”† 2   â”‚
            â”‚ 3   â”† 4   â”‚
            â”‚ 5   â”† 6   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_unique(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[1,3,5]]
            b: [[2,4,6]]
        c                óF   •— ‰j                  | «      j                  ‰¬«      S )Nrâ   )r   Úunique)r   rã   r&   s    €€r    r.   zExpr.unique.<locals>.<lambda>K  s   ø€ ˜×/Ñ/°Ó4×;Ñ;È>Ð;ÔZr"   r/   )r&   rã   s   ``r    ræ   zExpr.unique  s   ù€ ðt ~‰~ÜZó
ð 	
r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )uô  Return absolute value of each element.

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_abs(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").abs()).to_native()

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

            >>> agnostic_abs(df_pd)
               a  b
            0  1  3
            1  2  4

            >>> agnostic_abs(df_pl)
            shape: (2, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ i64 â”† i64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 1   â”† 3   â”‚
            â”‚ 2   â”† 4   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_abs(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[1,2]]
            b: [[3,4]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   r+   r-   s    €r    r.   zExpr.abs.<locals>.<lambda>  rž   r"   r/   r1   s   `r    r+   zExpr.absN  s   ø€ ðf ~‰~ÓLÓMÐMr"   ©Úreversec               ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )u  Return cumulative sum.

        Arguments:
            reverse: reverse the operation

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_cum_sum(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a", "b").cum_sum()).to_native()

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

            >>> agnostic_cum_sum(df_pd)
                a   b
            0   1   2
            1   2   6
            2   5  10
            3  10  16
            4  15  22
            >>> agnostic_cum_sum(df_pl)
            shape: (5, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ i64 â”† i64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 1   â”† 2   â”‚
            â”‚ 2   â”† 6   â”‚
            â”‚ 5   â”† 10  â”‚
            â”‚ 10  â”† 16  â”‚
            â”‚ 15  â”† 22  â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜
            >>> agnostic_cum_sum(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[1,2,5,10,15]]
            b: [[2,6,10,16,22]]
        c                óF   •— ‰j                  | «      j                  ‰¬«      S ©Nré   )r   Úcum_sum©r   rê   r&   s    €€r    r.   zExpr.cum_sum.<locals>.<lambda>¾  ó   ø€ ˜×/Ñ/°Ó4×<Ñ<ÀWÐ<ÔMr"   r/   ©r&   rê   s   ``r    rî   zExpr.cum_sumƒ  s   ù€ ðt ~‰~ÜMó
ð 	
r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u  Returns the difference between each element and the previous one.

        Returns:
            A new expression.

        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:

                nw.col("a").diff().fill_null(0).cast(nw.Int64)

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_diff(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(a_diff=nw.col("a").diff()).to_native()

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

            >>> agnostic_diff(df_pd)
               a_diff
            0     NaN
            1     0.0
            2     2.0
            3     2.0
            4     0.0

            >>> agnostic_diff(df_pl)
            shape: (5, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a_diff â”‚
            â”‚ ---    â”‚
            â”‚ i64    â”‚
            â•žâ•â•â•â•â•â•â•â•â•¡
            â”‚ null   â”‚
            â”‚ 0      â”‚
            â”‚ 2      â”‚
            â”‚ 2      â”‚
            â”‚ 0      â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_diff(df_pa)
            pyarrow.Table
            a_diff: int64
            ----
            a_diff: [[null,0,2,2,0]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Údiffr-   s    €r    r.   zExpr.diff.<locals>.<lambda>  r²   r"   r/   r1   s   `r    rô   z	Expr.diffÁ  s   ø€ ð@ ~‰~ÓMÓNÐNr"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )uH  Shift values by `n` positions.

        Arguments:
            n: Number of positions to shift values by.

        Returns:
            A new expression.

        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:

                nw.col("a").shift(1).fill_null(0).cast(nw.Int64)

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_shift(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(a_shift=nw.col("a").shift(n=1)).to_native()

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

            >>> agnostic_shift(df_pd)
               a_shift
            0      NaN
            1      1.0
            2      1.0
            3      3.0
            4      5.0

            >>> agnostic_shift(df_pl)
            shape: (5, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a_shift â”‚
            â”‚ ---     â”‚
            â”‚ i64     â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•¡
            â”‚ null    â”‚
            â”‚ 1       â”‚
            â”‚ 1       â”‚
            â”‚ 3       â”‚
            â”‚ 5       â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_shift(df_pa)
            pyarrow.Table
            a_shift: int64
            ----
            a_shift: [[null,1,1,3,5]]
        c                óD   •— ‰j                  | «      j                  ‰«      S r%   )r   Úshift©r   Únr&   s    €€r    r.   zExpr.shift.<locals>.<lambda>F  s   ø€ ¨$×*AÑ*AÀ#Ó*F×*LÑ*LÈQÔ*Or"   r/   ©r&   rù   s   ``r    r÷   z
Expr.shift  s   ù€ ðF ~‰~ÔOÓPÐPr"   ©rÆ   c               óÚ   ‡ ‡‡‡— ‰€Ot        ‰t        «      sd}t        |«      ‚t        ‰j	                  «       «      Št        ‰j                  «       «      Š‰ j                  ˆˆˆˆ fd„«      S )u<
  Replace all values by different values.

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

        Arguments:
            old: 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()))`.
            new: Sequence of values to replace by. Length must match the length of `old`.
            return_dtype: The data type of the resulting expression. If set to `None`
                (default), the data type is determined automatically based on the other
                inputs.

        Returns:
            A new expression.

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

            Let's define dataframe-agnostic functions:

            >>> def agnostic_replace_strict(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         b=nw.col("a").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 `agnostic_replace_strict`:

            >>> agnostic_replace_strict(df_pd)
               a      b
            0  3  three
            1  0   zero
            2  1    one
            3  2    two

            >>> agnostic_replace_strict(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b     â”‚
            â”‚ --- â”† ---   â”‚
            â”‚ i64 â”† str   â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•¡
            â”‚ 3   â”† three â”‚
            â”‚ 0   â”† zero  â”‚
            â”‚ 1   â”† one   â”‚
            â”‚ 2   â”† two   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_replace_strict(df_pa)
            pyarrow.Table
            a: int64
            b: string
            ----
            a: [[3,0,1,2]]
            b: [["three","zero","one","two"]]
        zB`new` argument is required if `old` argument is not a Mapping typec                óJ   •— ‰j                  | «      j                  ‰‰‰¬«      S )Nrû   )r   Úreplace_strict)r   ÚnewÚoldrÆ   r&   s    €€€€r    r.   z%Expr.replace_strict.<locals>.<lambda>Ÿ  s(   ø€ ˜×/Ñ/°Ó4×CÑCØS |ð Dô r"   )r   r
   Ú	TypeErrorÚlistÚvaluesÚkeysr0   )r&   r   rÿ   rÆ   Úmsgs   ```` r    rþ   zExpr.replace_strictH  sZ   û€ ð\ ˆ;Ü˜c¤7Ô+ØZÜ “nÐ$äs—z‘z“|Ó$ˆCÜs—x‘x“zÓ"ˆCà~‰~öó
ð 	
r"   ©Ú
descendingÚ
nulls_lastc               ó4   ‡ ‡‡— ‰ j                  ˆˆˆ fd„«      S )uÛ	  Sort this column. Place null values first.

        Arguments:
            descending: Sort in descending order.
            nulls_last: Place null values last instead of first.

        Returns:
            A new expression.

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

            Let's define dataframe-agnostic functions:

            >>> def agnostic_sort(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").sort()).to_native()

            >>> def agnostic_sort_descending(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").sort(descending=True)).to_native()

            We can then pass any supported library such as pandas, Polars, or
            PyArrow to `agnostic_sort` and `agnostic_sort_descending`:

            >>> agnostic_sort(df_pd)
                 a
            1  NaN
            2  1.0
            3  2.0
            0  5.0

            >>> agnostic_sort(df_pl)
            shape: (4, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”‚
            â”‚ ---  â”‚
            â”‚ i64  â”‚
            â•žâ•â•â•â•â•â•â•¡
            â”‚ null â”‚
            â”‚ 1    â”‚
            â”‚ 2    â”‚
            â”‚ 5    â”‚
            â””â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_sort(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[null,1,2,5]]

            >>> agnostic_sort_descending(df_pd)
                 a
            1  NaN
            0  5.0
            3  2.0
            2  1.0

            >>> agnostic_sort_descending(df_pl)
            shape: (4, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”‚
            â”‚ ---  â”‚
            â”‚ i64  â”‚
            â•žâ•â•â•â•â•â•â•¡
            â”‚ null â”‚
            â”‚ 5    â”‚
            â”‚ 2    â”‚
            â”‚ 1    â”‚
            â””â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_sort_descending(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[null,5,2,1]]
        c                óH   •— ‰j                  | «      j                  ‰‰¬«      S )Nr  )r   Úsort)r   r  r  r&   s    €€€r    r.   zExpr.sort.<locals>.<lambda>ü  s%   ø€ ˜×/Ñ/°Ó4×9Ñ9Ø%°*ð :ô r"   r/   )r&   r  r  s   ```r    r  z	Expr.sort¤  s   ú€ ðn ~‰~õó
ð 	
r"   c                ó8   ‡ ‡‡‡— ‰ j                  ˆˆˆ ˆfd„«      S )u3  Check if this expression is between the given lower and upper bounds.

        Arguments:
            lower_bound: Lower bound value.
            upper_bound: Upper bound value.
            closed: Define which sides of the interval are closed (inclusive).

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_is_between(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").is_between(2, 4, "right")).to_native()

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

            >>> agnostic_is_between(df_pd)
                   a
            0  False
            1  False
            2   True
            3   True
            4  False

            >>> agnostic_is_between(df_pl)
            shape: (5, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a     â”‚
            â”‚ ---   â”‚
            â”‚ bool  â”‚
            â•žâ•â•â•â•â•â•â•â•¡
            â”‚ false â”‚
            â”‚ false â”‚
            â”‚ true  â”‚
            â”‚ true  â”‚
            â”‚ false â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_is_between(df_pa)
            pyarrow.Table
            a: bool
            ----
            a: [[false,false,true,true,false]]
        c                óp   •— ‰j                  | «      j                  t        | ‰«      t        | ‰«      ‰«      S r%   )r   Ú
is_betweenr!   )r   ÚclosedÚlower_boundr&   Úupper_bounds    €€€€r    r.   z!Expr.is_between.<locals>.<lambda>D  s2   ø€ ˜×/Ñ/°Ó4×?Ñ?Ü! # {Ó3Ü! # {Ó3Øôr"   r/   )r&   r  r  r  s   ````r    r  zExpr.is_between  s   û€ ðB ~‰~öó
ð 	
r"   c                ó–   ‡ ‡— t        ‰t        «      r+t        ‰t        t        f«      s‰ j	                  ˆˆ fd„«      S d}t        |«      ‚)u	  Check if elements of this expression are present in the other iterable.

        Arguments:
            other: iterable

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_is_in(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(b=nw.col("a").is_in([1, 2])).to_native()

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

            >>> agnostic_is_in(df_pd)
                a      b
            0   1   True
            1   2   True
            2   9  False
            3  10  False

            >>> agnostic_is_in(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b     â”‚
            â”‚ --- â”† ---   â”‚
            â”‚ i64 â”† bool  â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•¡
            â”‚ 1   â”† true  â”‚
            â”‚ 2   â”† true  â”‚
            â”‚ 9   â”† false â”‚
            â”‚ 10  â”† false â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_is_in(df_pa)
            pyarrow.Table
            a: int64
            b: bool
            ----
            a: [[1,2,9,10]]
            b: [[true,true,false,false]]
        c                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Úis_inr!   rD   s    €€r    r.   zExpr.is_in.<locals>.<lambda>‡  s%   ø€ ˜D×3Ñ3°CÓ8×>Ñ>Ü% c¨5Ó1ôr"   zyNarwhals `is_in` doesn't accept expressions as an argument, as opposed to Polars. You should provide an iterable instead.)r   r   ÚstrÚbytesr0   ÚNotImplementedError)r&   r   r  s   `` r    r  z
Expr.is_inK  sF   ù€ ôt eœXÔ&¬z¸%Ä#ÄuÀÔ/NØ—>‘>ôóð ð NˆCÜ% cÓ*Ð*r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )uT  Filters elements based on a condition, returning a new expression.

        Arguments:
            predicates: Conditions to filter by (which get ANDed together).

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [2, 3, 4, 5, 6, 7], "b": [10, 11, 12, 13, 14, 15]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_filter(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a").filter(nw.col("a") > 4),
            ...         nw.col("b").filter(nw.col("b") < 13),
            ...     ).to_native()

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

            >>> agnostic_filter(df_pd)
               a   b
            3  5  10
            4  6  11
            5  7  12

            >>> agnostic_filter(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ i64 â”† i64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 5   â”† 10  â”‚
            â”‚ 6   â”† 11  â”‚
            â”‚ 7   â”† 12  â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_filter(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[5,6,7]]
            b: [[10,11,12]]
        c           	     óŒ   •—  ‰j                  | «      j                  t        ‰«      D cg c]  }t        | |«      ‘Œ c}Ž S c c}w r%   )r   Úfilterr   r!   )r   ÚpredÚ
predicatesr&   s     €€r    r.   zExpr.filter.<locals>.<lambda>Ë  sB   ø€ Ð;˜×/Ñ/°Ó4×;Ñ;Ü;BÀ:Ô;NÓOÑ;N°4Ô# C¨Õ.Ð;NÑOñùÚOs   ªAr/   ©r&   r  s   ``r    r  zExpr.filter  s   ù€ ðv ~‰~ôó
ð 	
r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u  Returns a boolean Series indicating which values are null.

        Returns:
            A new expression.

        Notes:
            pandas handles null values differently from Polars and PyArrow.
            See [null_handling](../pandas_like_concepts/null_handling.md/)
            for reference.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> df_pd = pd.DataFrame(
            ...     {
            ...         "a": [2, 4, None, 3, 5],
            ...         "b": [2.0, 4.0, float("nan"), 3.0, 5.0],
            ...     }
            ... )
            >>> data = {
            ...     "a": [2, 4, None, 3, 5],
            ...     "b": [2.0, 4.0, None, 3.0, 5.0],
            ... }
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_is_null(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         a_is_null=nw.col("a").is_null(), b_is_null=nw.col("b").is_null()
            ...     ).to_native()

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

            >>> agnostic_is_null(df_pd)
                 a    b  a_is_null  b_is_null
            0  2.0  2.0      False      False
            1  4.0  4.0      False      False
            2  NaN  NaN       True       True
            3  3.0  3.0      False      False
            4  5.0  5.0      False      False

            >>> agnostic_is_null(df_pl)
            shape: (5, 4)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”† b    â”† a_is_null â”† b_is_null â”‚
            â”‚ ---  â”† ---  â”† ---       â”† ---       â”‚
            â”‚ i64  â”† f64  â”† bool      â”† bool      â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2    â”† 2.0  â”† false     â”† false     â”‚
            â”‚ 4    â”† 4.0  â”† false     â”† false     â”‚
            â”‚ null â”† null â”† true      â”† true      â”‚
            â”‚ 3    â”† 3.0  â”† false     â”† false     â”‚
            â”‚ 5    â”† 5.0  â”† false     â”† false     â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_is_null(df_pa)
            pyarrow.Table
            a: int64
            b: double
            a_is_null: bool
            b_is_null: bool
            ----
            a: [[2,4,null,3,5]]
            b: [[2,4,null,3,5]]
            a_is_null: [[false,false,true,false,false]]
            b_is_null: [[false,false,true,false,false]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úis_nullr-   s    €r    r.   zExpr.is_null.<locals>.<lambda>  rÖ   r"   r/   r1   s   `r    r   zExpr.is_nullÐ  s   ø€ ðX ~‰~ÓPÓQÐQr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u
  Indicate which values are NaN.

        Returns:
            A new expression.

        Notes:
            pandas handles null values differently from Polars and PyArrow.
            See [null_handling](../pandas_like_concepts/null_handling.md/)
            for reference.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"orig": [0.0, None, 2.0]}
            >>> df_pd = pd.DataFrame(data).astype({"orig": "Float64"})
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_self_div_is_nan(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         divided=nw.col("orig") / nw.col("orig"),
            ...         divided_is_nan=(nw.col("orig") / nw.col("orig")).is_nan(),
            ...     ).to_native()

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

            >>> print(agnostic_self_div_is_nan(df_pd))
               orig  divided  divided_is_nan
            0   0.0      NaN            True
            1  <NA>     <NA>            <NA>
            2   2.0      1.0           False

            >>> print(agnostic_self_div_is_nan(df_pl))
            shape: (3, 3)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ orig â”† divided â”† divided_is_nan â”‚
            â”‚ ---  â”† ---     â”† ---            â”‚
            â”‚ f64  â”† f64     â”† bool           â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 0.0  â”† NaN     â”† true           â”‚
            â”‚ null â”† null    â”† null           â”‚
            â”‚ 2.0  â”† 1.0     â”† false          â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> print(agnostic_self_div_is_nan(df_pa))
            pyarrow.Table
            orig: double
            divided: double
            divided_is_nan: bool
            ----
            orig: [[0,null,2]]
            divided: [[nan,null,1]]
            divided_is_nan: [[true,null,false]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úis_nanr-   s    €r    r.   zExpr.is_nan.<locals>.<lambda>]  r·   r"   r/   r1   s   `r    r#  zExpr.is_nan  s   ø€ ð~ ~‰~ÓOÓPÐPr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u…  Find elements where boolean expression is True.

        Returns:
            A new expression.

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

            We define a library agnostic function:

            >>> def agnostic_arg_true(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").is_null().arg_true()).to_native()

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

            >>> agnostic_arg_true(df_pd)
               a
            1  1
            2  2

            >>> agnostic_arg_true(df_pl)
            shape: (2, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ u32 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 1   â”‚
            â”‚ 2   â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_arg_true(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[1,2]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úarg_truer-   s    €r    r.   zExpr.arg_true.<locals>.<lambda>  rá   r"   r/   r1   s   `r    r&  zExpr.arg_true_  s   ø€ ðb ~‰~ÓQÓRÐRr"   c                ó¨   ‡ ‡‡‡— ‰‰d}t        |«      ‚‰€‰€d}t        |«      ‚‰‰dvrd‰› }t        |«      ‚‰ j                  ˆˆ ˆˆfd„«      S )u5  Fill null values with given value.

        Arguments:
            value: Value used to fill null values.
            strategy: Strategy used to fill null values.
            limit: Number of consecutive null values to fill when using the 'forward' or 'backward' strategy.

        Returns:
            A new expression.

        Notes:
            pandas handles null values differently from Polars and PyArrow.
            See [null_handling](../pandas_like_concepts/null_handling.md/)
            for reference.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> df_pd = pd.DataFrame(
            ...     {
            ...         "a": [2, 4, None, None, 3, 5],
            ...         "b": [2.0, 4.0, float("nan"), float("nan"), 3.0, 5.0],
            ...     }
            ... )
            >>> data = {
            ...     "a": [2, 4, None, None, 3, 5],
            ...     "b": [2.0, 4.0, None, None, 3.0, 5.0],
            ... }
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_fill_null(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(nw.col("a", "b").fill_null(0)).to_native()

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

            >>> agnostic_fill_null(df_pd)
                 a    b
            0  2.0  2.0
            1  4.0  4.0
            2  0.0  0.0
            3  0.0  0.0
            4  3.0  3.0
            5  5.0  5.0

            >>> agnostic_fill_null(df_pl)
            shape: (6, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ i64 â”† f64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 2   â”† 2.0 â”‚
            â”‚ 4   â”† 4.0 â”‚
            â”‚ 0   â”† 0.0 â”‚
            â”‚ 0   â”† 0.0 â”‚
            â”‚ 3   â”† 3.0 â”‚
            â”‚ 5   â”† 5.0 â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_fill_null(df_pa)
            pyarrow.Table
            a: int64
            b: double
            ----
            a: [[2,4,0,0,3,5]]
            b: [[2,4,0,0,3,5]]

            Using a strategy:

            >>> def agnostic_fill_null_with_strategy(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("a", "b")
            ...         .fill_null(strategy="forward", limit=1)
            ...         .name.suffix("_filled")
            ...     ).to_native()

            >>> agnostic_fill_null_with_strategy(df_pd)
                 a    b  a_filled  b_filled
            0  2.0  2.0       2.0       2.0
            1  4.0  4.0       4.0       4.0
            2  NaN  NaN       4.0       4.0
            3  NaN  NaN       NaN       NaN
            4  3.0  3.0       3.0       3.0
            5  5.0  5.0       5.0       5.0

            >>> agnostic_fill_null_with_strategy(df_pl)
            shape: (6, 4)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”† b    â”† a_filled â”† b_filled â”‚
            â”‚ ---  â”† ---  â”† ---      â”† ---      â”‚
            â”‚ i64  â”† f64  â”† i64      â”† f64      â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2    â”† 2.0  â”† 2        â”† 2.0      â”‚
            â”‚ 4    â”† 4.0  â”† 4        â”† 4.0      â”‚
            â”‚ null â”† null â”† 4        â”† 4.0      â”‚
            â”‚ null â”† null â”† null     â”† null     â”‚
            â”‚ 3    â”† 3.0  â”† 3        â”† 3.0      â”‚
            â”‚ 5    â”† 5.0  â”† 5        â”† 5.0      â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_fill_null_with_strategy(df_pa)
            pyarrow.Table
            a: int64
            b: double
            a_filled: int64
            b_filled: double
            ----
            a: [[2,4,null,null,3,5]]
            b: [[2,4,null,null,3,5]]
            a_filled: [[2,4,4,null,3,5]]
            b_filled: [[2,4,4,null,3,5]]
        z*cannot specify both `value` and `strategy`z0must specify either a fill `value` or `strategy`>   ÚforwardÚbackwardzstrategy not supported: c                óJ   •— ‰j                  | «      j                  ‰‰‰¬«      S )N)ÚvalueÚstrategyÚlimit)r   Ú	fill_null)r   r-  r&   r,  r+  s    €€€€r    r.   z Expr.fill_null.<locals>.<lambda>	  s'   ø€ ˜×/Ñ/°Ó4×>Ñ>Ø h°eð ?ô r"   ©Ú
ValueErrorr0   )r&   r+  r,  r-  r  s   ```` r    r.  zExpr.fill_null’  ss   û€ ð@ Ð Ð!5Ø>ˆCÜ˜S“/Ð!Øˆ=˜XÐ-ØDˆCÜ˜S“/Ð!ØÐ HÐ4KÑ$KØ,¨X¨JÐ7ˆCÜ˜S“/Ð!Ø~‰~öó
ð 	
r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )uÑ  Drop null values.

        Returns:
            A new expression.

        Notes:
            pandas handles null values differently from Polars and PyArrow.
            See [null_handling](../pandas_like_concepts/null_handling.md/)
            for reference.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> df_pd = pd.DataFrame({"a": [2.0, 4.0, float("nan"), 3.0, None, 5.0]})
            >>> df_pl = pl.DataFrame({"a": [2.0, 4.0, None, 3.0, None, 5.0]})
            >>> df_pa = pa.table({"a": [2.0, 4.0, None, 3.0, None, 5.0]})

            Let's define a dataframe-agnostic function:

            >>> def agnostic_drop_nulls(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").drop_nulls()).to_native()

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

            >>> agnostic_drop_nulls(df_pd)
                 a
            0  2.0
            1  4.0
            3  3.0
            5  5.0

            >>> agnostic_drop_nulls(df_pl)
            shape: (4, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ f64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 2.0 â”‚
            â”‚ 4.0 â”‚
            â”‚ 3.0 â”‚
            â”‚ 5.0 â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_drop_nulls(df_pa)
            pyarrow.Table
            a: double
            ----
            a: [[2,4,3,5]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Ú
drop_nullsr-   s    €r    r.   z!Expr.drop_nulls.<locals>.<lambda>[	  rš   r"   r/   r1   s   `r    r3  zExpr.drop_nulls"	  s   ø€ ðr ~‰~ÓSÓTÐTr"   ©ÚfractionÚwith_replacementÚseedc               ó<   ‡ ‡‡‡‡— ‰ j                  ˆˆˆˆ ˆfd„«      S )uš  Sample randomly from this expression.

        Arguments:
            n: Number of items to return. Cannot be used with fraction.
            fraction: Fraction of items to return. Cannot be used with n.
            with_replacement: Allow values to be sampled more than once.
            seed: Seed for the random number generator. If set to None (default), a random
                seed is generated for each sample operation.

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_sample(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a").sample(fraction=1.0, with_replacement=True)
            ...     ).to_native()

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

            >>> agnostic_sample(df_pd)  # doctest: +SKIP
               a
            2  3
            0  1
            2  3

            >>> agnostic_sample(df_pl)  # doctest: +SKIP
            shape: (3, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ f64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 2   â”‚
            â”‚ 3   â”‚
            â”‚ 3   â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_sample(df_pa)  # doctest: +SKIP
            pyarrow.Table
            a: int64
            ----
            a: [[1,3,3]]
        c                óL   •— ‰j                  | «      j                  ‰‰‰‰¬«      S )Nr4  )r   Úsample)r   r5  rù   r7  r&   r6  s    €€€€€r    r.   zExpr.sample.<locals>.<lambda>¡	  s*   ø€ ˜×/Ñ/°Ó4×;Ñ;Ø˜HÐ7GÈdð <ô r"   r/   )r&   rù   r5  r6  r7  s   `````r    r:  zExpr.sample]	  s   ü€ ðF ~‰~÷ó
ð 	
r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )u  Compute expressions over the given groups.

        Arguments:
            keys: Names of columns to compute window expression over.
                  Must be names of columns, as opposed to expressions -
                  so, this is a bit less flexible than Polars' `Expr.over`.

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_min_over_b(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         a_min_per_group=nw.col("a").min().over("b")
            ...     ).to_native()

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

            >>> agnostic_min_over_b(df_pd)
               a  b  a_min_per_group
            0  1  1                1
            1  2  1                1
            2  3  2                3

            >>> agnostic_min_over_b(df_pl)
            shape: (3, 3)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”† a_min_per_group â”‚
            â”‚ --- â”† --- â”† ---             â”‚
            â”‚ i64 â”† i64 â”† i64             â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 1   â”† 1   â”† 1               â”‚
            â”‚ 2   â”† 1   â”† 1               â”‚
            â”‚ 3   â”† 2   â”† 3               â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_min_over_b(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            a_min_per_group: int64
            ----
            a: [[1,2,3]]
            b: [[1,1,2]]
            a_min_per_group: [[1,1,3]]

            Cumulative operations are also supported, but (currently) only for
            pandas and Polars:

            >>> def agnostic_cum_sum(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(c=nw.col("a").cum_sum().over("b")).to_native()

            >>> agnostic_cum_sum(df_pd)
               a  b  c
            0  1  1  1
            1  2  1  3
            2  3  2  3

            >>> agnostic_cum_sum(df_pl)
            shape: (3, 3)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”† c   â”‚
            â”‚ --- â”† --- â”† --- â”‚
            â”‚ i64 â”† i64 â”† i64 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 1   â”† 1   â”† 1   â”‚
            â”‚ 2   â”† 1   â”† 3   â”‚
            â”‚ 3   â”† 2   â”† 3   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜
        c                óV   •— ‰j                  | «      j                  t        ‰«      «      S r%   )r   Úoverr   )r   r  r&   s    €€r    r.   zExpr.over.<locals>.<lambda>þ	  s   ø€ ˜×/Ñ/°Ó4×9Ñ9¼'À$»-ÔHr"   r/   )r&   r  s   ``r    r=  z	Expr.over¦	  s   ù€ ðn ~‰~ÜHó
ð 	
r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u9  Return a boolean mask indicating duplicated values.

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_is_duplicated(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.all().is_duplicated()).to_native()

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

            >>> agnostic_is_duplicated(df_pd)
                   a      b
            0   True   True
            1  False   True
            2  False  False
            3   True  False

            >>> agnostic_is_duplicated(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a     â”† b     â”‚
            â”‚ ---   â”† ---   â”‚
            â”‚ bool  â”† bool  â”‚
            â•žâ•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•¡
            â”‚ true  â”† true  â”‚
            â”‚ false â”† true  â”‚
            â”‚ false â”† false â”‚
            â”‚ true  â”† false â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_is_duplicated(df_pa)
            pyarrow.Table
            a: bool
            b: bool
            ----
            a: [[true,false,false,true]]
            b: [[true,true,false,false]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úis_duplicatedr-   s    €r    r.   z$Expr.is_duplicated.<locals>.<lambda>8
  s   ø€ ¨$×*AÑ*AÀ#Ó*F×*TÑ*TÔ*Vr"   r/   r1   s   `r    r@  zExpr.is_duplicated
  s   ø€ ðn ~‰~ÓVÓWÐWr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u  Return a boolean mask indicating unique values.

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_is_unique(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.all().is_unique()).to_native()

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

            >>> agnostic_is_unique(df_pd)
                   a      b
            0  False  False
            1   True  False
            2   True   True
            3  False   True

            >>> agnostic_is_unique(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a     â”† b     â”‚
            â”‚ ---   â”† ---   â”‚
            â”‚ bool  â”† bool  â”‚
            â•žâ•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•¡
            â”‚ false â”† false â”‚
            â”‚ true  â”† false â”‚
            â”‚ true  â”† true  â”‚
            â”‚ false â”† true  â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_is_unique(df_pa)
            pyarrow.Table
            a: bool
            b: bool
            ----
            a: [[false,true,true,false]]
            b: [[false,false,true,true]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Ú	is_uniquer-   s    €r    r.   z Expr.is_unique.<locals>.<lambda>q
  ó   ø€ ¨$×*AÑ*AÀ#Ó*F×*PÑ*PÔ*Rr"   r/   r1   s   `r    rC  zExpr.is_unique:
  s   ø€ ðn ~‰~ÓRÓSÐSr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u£  Count null values.

        Returns:
            A new expression.

        Notes:
            pandas handles null values differently from Polars and PyArrow.
            See [null_handling](../pandas_like_concepts/null_handling.md/)
            for reference.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_null_count(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.all().null_count()).to_native()

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

            >>> agnostic_null_count(df_pd)
               a  b
            0  1  2

            >>> agnostic_null_count(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† b   â”‚
            â”‚ --- â”† --- â”‚
            â”‚ u32 â”† u32 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 1   â”† 2   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_null_count(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[1]]
            b: [[2]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Ú
null_countr-   s    €r    r.   z!Expr.null_count.<locals>.<lambda>©
  rš   r"   r/   r1   s   `r    rG  zExpr.null_counts
  s   ø€ ðl ~‰~ÓSÓTÐTr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )ui  Return a boolean mask indicating the first occurrence of each distinct value.

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_is_first_distinct(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.all().is_first_distinct()).to_native()

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

            >>> agnostic_is_first_distinct(df_pd)
                   a      b
            0   True   True
            1   True  False
            2   True   True
            3  False   True

            >>> agnostic_is_first_distinct(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a     â”† b     â”‚
            â”‚ ---   â”† ---   â”‚
            â”‚ bool  â”† bool  â”‚
            â•žâ•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•¡
            â”‚ true  â”† true  â”‚
            â”‚ true  â”† false â”‚
            â”‚ true  â”† true  â”‚
            â”‚ false â”† true  â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_is_first_distinct(df_pa)
            pyarrow.Table
            a: bool
            b: bool
            ----
            a: [[true,true,true,false]]
            b: [[true,false,true,true]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úis_first_distinctr-   s    €r    r.   z(Expr.is_first_distinct.<locals>.<lambda>ã
  s   ø€ ˜×/Ñ/°Ó4×FÑFÔHr"   r/   r1   s   `r    rJ  zExpr.is_first_distinct«
  s   ø€ ðn ~‰~ÛHó
ð 	
r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )ub  Return a boolean mask indicating the last occurrence of each distinct value.

        Returns:
            A new expression.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_is_last_distinct(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.all().is_last_distinct()).to_native()

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

            >>> agnostic_is_last_distinct(df_pd)
                   a      b
            0  False  False
            1   True   True
            2   True   True
            3   True   True

            >>> agnostic_is_last_distinct(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a     â”† b     â”‚
            â”‚ ---   â”† ---   â”‚
            â”‚ bool  â”† bool  â”‚
            â•žâ•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•¡
            â”‚ false â”† false â”‚
            â”‚ true  â”† true  â”‚
            â”‚ true  â”† true  â”‚
            â”‚ true  â”† true  â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_is_last_distinct(df_pa)
            pyarrow.Table
            a: bool
            b: bool
            ----
            a: [[false,true,true,true]]
            b: [[false,true,true,true]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úis_last_distinctr-   s    €r    r.   z'Expr.is_last_distinct.<locals>.<lambda>  s   ø€ ¨$×*AÑ*AÀ#Ó*F×*WÑ*WÔ*Yr"   r/   r1   s   `r    rM  zExpr.is_last_distinctæ
  s   ø€ ðn ~‰~ÓYÓZÐZr"   c                ó4   ‡ ‡‡— ‰ j                  ˆˆˆ fd„«      S )u™  Get quantile value.

        Arguments:
            quantile: Quantile between 0.0 and 1.0.
            interpolation: Interpolation method.

        Returns:
            A new expression.

        Note:
            - pandas and Polars may have implementation differences for a given interpolation method.
            - [dask](https://docs.dask.org/en/stable/generated/dask.dataframe.Series.quantile.html) has
                its own method to approximate quantile and it doesn't implement 'nearest', 'higher',
                'lower', 'midpoint' as interpolation method - use 'linear' which is closest to the
                native 'dask' - method.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": list(range(50)), "b": list(range(50, 100))}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_quantile(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a", "b").quantile(0.5, interpolation="linear")
            ...     ).to_native()

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

            >>> agnostic_quantile(df_pd)
                  a     b
            0  24.5  74.5

            >>> agnostic_quantile(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”† b    â”‚
            â”‚ ---  â”† ---  â”‚
            â”‚ f64  â”† f64  â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•¡
            â”‚ 24.5 â”† 74.5 â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_quantile(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[24.5]]
            b: [[74.5]]
        c                óF   •— ‰j                  | «      j                  ‰‰«      S r%   )r   Úquantile)r   ÚinterpolationrP  r&   s    €€€r    r.   zExpr.quantile.<locals>.<lambda>b  s   ø€ ˜×/Ñ/°Ó4×=Ñ=¸hÈÔVr"   r/   )r&   rP  rQ  s   ```r    rP  zExpr.quantile  s   ú€ ðD ~‰~ÝVó
ð 	
r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )uÔ  Get the first `n` rows.

        Arguments:
            n: Number of rows to return.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": list(range(10))}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

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

            >>> def agnostic_head(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").head(3)).to_native()

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

            >>> agnostic_head(df_pd)
               a
            0  0
            1  1
            2  2

            >>> agnostic_head(df_pl)
            shape: (3, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ i64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 0   â”‚
            â”‚ 1   â”‚
            â”‚ 2   â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_head(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[0,1,2]]
        c                óD   •— ‰j                  | «      j                  ‰«      S r%   )r   Úheadrø   s    €€r    r.   zExpr.head.<locals>.<lambda>›  ó   ø€ ¨$×*AÑ*AÀ#Ó*F×*KÑ*KÈAÔ*Nr"   r/   rú   s   ``r    rT  z	Expr.heade  ó   ù€ ðl ~‰~ÔNÓOÐOr"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )uÒ  Get the last `n` rows.

        Arguments:
            n: Number of rows to return.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": list(range(10))}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

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

            >>> def agnostic_tail(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").tail(3)).to_native()

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

            >>> agnostic_tail(df_pd)
               a
            7  7
            8  8
            9  9

            >>> agnostic_tail(df_pl)
            shape: (3, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ i64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 7   â”‚
            â”‚ 8   â”‚
            â”‚ 9   â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_tail(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[7,8,9]]
        c                óD   •— ‰j                  | «      j                  ‰«      S r%   )r   Útailrø   s    €€r    r.   zExpr.tail.<locals>.<lambda>Ó  rU  r"   r/   rú   s   ``r    rY  z	Expr.tail  rV  r"   c                ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )uº  Round underlying floating point data by `decimals` digits.

        Arguments:
            decimals: Number of decimals to round by.

        Returns:
            A new expression.


        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 pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1.12345, 2.56789, 3.901234]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

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

            >>> def agnostic_round(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").round(1)).to_native()

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

            >>> agnostic_round(df_pd)
                 a
            0  1.1
            1  2.6
            2  3.9

            >>> agnostic_round(df_pl)
            shape: (3, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ f64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 1.1 â”‚
            â”‚ 2.6 â”‚
            â”‚ 3.9 â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_round(df_pa)
            pyarrow.Table
            a: double
            ----
            a: [[1.1,2.6,3.9]]
        c                óD   •— ‰j                  | «      j                  ‰«      S r%   )r   Úround)r   Údecimalsr&   s    €€r    r.   zExpr.round.<locals>.<lambda>  s   ø€ ¨$×*AÑ*AÀ#Ó*F×*LÑ*LÈXÔ*Vr"   r/   )r&   r]  s   ``r    r\  z
Expr.roundÕ  s   ù€ ð~ ~‰~ÔVÓWÐWr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )uç  Return the number of elements in the column.

        Null values count towards the total.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": ["x", "y", "z"], "b": [1, 2, 1]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function that computes the len over
            different values of "b" column:

            >>> def agnostic_len(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a").filter(nw.col("b") == 1).len().alias("a1"),
            ...         nw.col("a").filter(nw.col("b") == 2).len().alias("a2"),
            ...     ).to_native()

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

            >>> agnostic_len(df_pd)
               a1  a2
            0   2   1

            >>> agnostic_len(df_pl)
            shape: (1, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a1  â”† a2  â”‚
            â”‚ --- â”† --- â”‚
            â”‚ u32 â”† u32 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 2   â”† 1   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_len(df_pa)
            pyarrow.Table
            a1: int64
            a2: int64
            ----
            a1: [[2]]
            a2: [[1]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úlenr-   s    €r    r.   zExpr.len.<locals>.<lambda>M  rž   r"   r/   r1   s   `r    r`  zExpr.len  s   ø€ ðn ~‰~ÓLÓMÐMr"   c                ó4   ‡ ‡‡— ‰ j                  ˆˆˆ fd„«      S )ue  Take every nth value in the Series and return as new Series.

        Arguments:
            n: Gather every *n*-th row.
            offset: Starting index.

        Returns:
            A new expression.

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

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

            >>> def agnostic_gather_every(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").gather_every(n=2, offset=1)).to_native()

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

            >>> agnostic_gather_every(df_pd)
               a
            1  2
            3  4

            >>> agnostic_gather_every(df_pl)
            shape: (2, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ i64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 2   â”‚
            â”‚ 4   â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_gather_every(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[2,4]]
        c                óH   •— ‰j                  | «      j                  ‰‰¬«      S )N)rù   Úoffset)r   Úgather_every)r   rù   rc  r&   s    €€€r    r.   z#Expr.gather_every.<locals>.<lambda>†  s    ø€ ˜×/Ñ/°Ó4×AÑAÀAÈfÐAÔUr"   r/   )r&   rù   rc  s   ```r    rd  zExpr.gather_everyO  s   ú€ ðl ~‰~ÝUó
ð 	
r"   c                ó4   ‡ ‡‡— ‰ j                  ˆˆ ˆfd„«      S )u§  Clip values in the Series.

        Arguments:
            lower_bound: Lower bound value.
            upper_bound: Upper bound value.

        Returns:
            A new expression.

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

            We define a library agnostic function:

            >>> def agnostic_clip_lower(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").clip(2)).to_native()

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

            >>> agnostic_clip_lower(df_pd)
               a
            0  2
            1  2
            2  3

            >>> agnostic_clip_lower(df_pl)
            shape: (3, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ i64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 2   â”‚
            â”‚ 2   â”‚
            â”‚ 3   â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_clip_lower(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[2,2,3]]

            We define another library agnostic function:

            >>> def agnostic_clip_upper(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").clip(upper_bound=2)).to_native()

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

            >>> agnostic_clip_upper(df_pd)
               a
            0  1
            1  2
            2  2

            >>> agnostic_clip_upper(df_pl)
            shape: (3, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ i64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 1   â”‚
            â”‚ 2   â”‚
            â”‚ 2   â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_clip_upper(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[1,2,2]]

            We can have both at the same time

            >>> data = {"a": [-1, 1, -3, 3, -5, 5]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a library agnostic function:

            >>> def agnostic_clip(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").clip(-1, 3)).to_native()

            We can pass any supported library such as Pandas, Polars, or
            PyArrow to `agnostic_clip`:

            >>> agnostic_clip(df_pd)
               a
            0 -1
            1  1
            2 -1
            3  3
            4 -1
            5  3

            >>> agnostic_clip(df_pl)
            shape: (6, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ i64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ -1  â”‚
            â”‚ 1   â”‚
            â”‚ -1  â”‚
            â”‚ 3   â”‚
            â”‚ -1  â”‚
            â”‚ 3   â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_clip(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[-1,1,-1,3,-1,3]]
        c                ón   •— ‰j                  | «      j                  t        | ‰«      t        | ‰«      «      S r%   )r   Úclipr!   )r   r  r&   r  s    €€€r    r.   zExpr.clip.<locals>.<lambda>  s/   ø€ ˜×/Ñ/°Ó4×9Ñ9Ü! # {Ó3Ü! # {Ó3ôr"   r/   )r&   r  r  s   ```r    rg  z	Expr.clip‹  s   ú€ ðT ~‰~õó
ð 	
r"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )u¤  Compute the most occurring value(s).

        Can return multiple values.

        Returns:
            A new expression.

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

            We define a library agnostic function:

            >>> def agnostic_mode(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").mode()).sort("a").to_native()

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

            >>> agnostic_mode(df_pd)
               a
            0  1

            >>> agnostic_mode(df_pl)
            shape: (1, 1)
            â”Œâ”€â”€â”€â”€â”€â”
            â”‚ a   â”‚
            â”‚ --- â”‚
            â”‚ i64 â”‚
            â•žâ•â•â•â•â•â•¡
            â”‚ 1   â”‚
            â””â”€â”€â”€â”€â”€â”˜

            >>> agnostic_mode(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[1]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Úmoder-   s    €r    r.   zExpr.mode.<locals>.<lambda>P  r²   r"   r/   r1   s   `r    rj  z	Expr.mode  s   ø€ ðh ~‰~ÓMÓNÐNr"   c                ó,   ‡ — ‰ j                  ˆ fd„«      S )um  Returns boolean values indicating which original 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:
            Expression of `Boolean` data type.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [float("nan"), float("inf"), 2.0, None]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a library agnostic function:

            >>> def agnostic_is_finite(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").is_finite()).to_native()

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

            >>> agnostic_is_finite(df_pd)
                   a
            0  False
            1  False
            2   True
            3  False

            >>> agnostic_is_finite(df_pl)
            shape: (4, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a     â”‚
            â”‚ ---   â”‚
            â”‚ bool  â”‚
            â•žâ•â•â•â•â•â•â•â•¡
            â”‚ false â”‚
            â”‚ false â”‚
            â”‚ true  â”‚
            â”‚ null  â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_is_finite(df_pa)
            pyarrow.Table
            a: bool
            ----
            a: [[false,false,true,null]]
        c                óB   •— ‰j                  | «      j                  «       S r%   )r   Ú	is_finiter-   s    €r    r.   z Expr.is_finite.<locals>.<lambda>Œ  rD  r"   r/   r1   s   `r    rm  zExpr.is_finiteR  s   ø€ ðt ~‰~ÓRÓSÐSr"   c               ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )u8
  Return the cumulative count of the non-null values in the column.

        Arguments:
            reverse: reverse the operation

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": ["x", "k", None, "d"]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a library agnostic function:

            >>> def agnostic_cum_count(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("a").cum_count().alias("cum_count"),
            ...         nw.col("a").cum_count(reverse=True).alias("cum_count_reverse"),
            ...     ).to_native()

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

            >>> agnostic_cum_count(df_pd)
                  a  cum_count  cum_count_reverse
            0     x          1                  3
            1     k          2                  2
            2  None          2                  1
            3     d          3                  1

            >>> agnostic_cum_count(df_pl)
            shape: (4, 3)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”† cum_count â”† cum_count_reverse â”‚
            â”‚ ---  â”† ---       â”† ---               â”‚
            â”‚ str  â”† u32       â”† u32               â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ x    â”† 1         â”† 3                 â”‚
            â”‚ k    â”† 2         â”† 2                 â”‚
            â”‚ null â”† 2         â”† 1                 â”‚
            â”‚ d    â”† 3         â”† 1                 â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_cum_count(df_pa)
            pyarrow.Table
            a: string
            cum_count: uint32
            cum_count_reverse: uint32
            ----
            a: [["x","k",null,"d"]]
            cum_count: [[1,2,2,3]]
            cum_count_reverse: [[3,2,1,1]]
        c                óF   •— ‰j                  | «      j                  ‰¬«      S rí   )r   Ú	cum_countrï   s    €€r    r.   z Expr.cum_count.<locals>.<lambda>Î  s   ø€ ˜×/Ñ/°Ó4×>Ñ>ÀwÐ>ÔOr"   r/   rñ   s   ``r    rp  zExpr.cum_countŽ  s   ù€ ð~ ~‰~ÜOó
ð 	
r"   c               ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )uº	  Return the cumulative min of the non-null values in the column.

        Arguments:
            reverse: reverse the operation

        Returns:
            A new expression.

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

            We define a library agnostic function:

            >>> def agnostic_cum_min(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("a").cum_min().alias("cum_min"),
            ...         nw.col("a").cum_min(reverse=True).alias("cum_min_reverse"),
            ...     ).to_native()

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

            >>> agnostic_cum_min(df_pd)
                 a  cum_min  cum_min_reverse
            0  3.0      3.0              1.0
            1  1.0      1.0              1.0
            2  NaN      NaN              NaN
            3  2.0      1.0              2.0

            >>> agnostic_cum_min(df_pl)
            shape: (4, 3)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”† cum_min â”† cum_min_reverse â”‚
            â”‚ ---  â”† ---     â”† ---             â”‚
            â”‚ i64  â”† i64     â”† i64             â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 3    â”† 3       â”† 1               â”‚
            â”‚ 1    â”† 1       â”† 1               â”‚
            â”‚ null â”† null    â”† null            â”‚
            â”‚ 2    â”† 1       â”† 2               â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_cum_min(df_pa)
            pyarrow.Table
            a: int64
            cum_min: int64
            cum_min_reverse: int64
            ----
            a: [[3,1,null,2]]
            cum_min: [[3,1,null,1]]
            cum_min_reverse: [[1,1,null,2]]
        c                óF   •— ‰j                  | «      j                  ‰¬«      S rí   )r   Úcum_minrï   s    €€r    r.   zExpr.cum_min.<locals>.<lambda>  rð   r"   r/   rñ   s   ``r    rs  zExpr.cum_minÑ  ó   ù€ ð~ ~‰~ÜMó
ð 	
r"   c               ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )u³	  Return the cumulative max of the non-null values in the column.

        Arguments:
            reverse: reverse the operation

        Returns:
            A new expression.

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

            We define a library agnostic function:

            >>> def agnostic_cum_max(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("a").cum_max().alias("cum_max"),
            ...         nw.col("a").cum_max(reverse=True).alias("cum_max_reverse"),
            ...     ).to_native()

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

            >>> agnostic_cum_max(df_pd)
                 a  cum_max  cum_max_reverse
            0  1.0      1.0              3.0
            1  3.0      3.0              3.0
            2  NaN      NaN              NaN
            3  2.0      3.0              2.0

            >>> agnostic_cum_max(df_pl)
            shape: (4, 3)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”† cum_max â”† cum_max_reverse â”‚
            â”‚ ---  â”† ---     â”† ---             â”‚
            â”‚ i64  â”† i64     â”† i64             â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 1    â”† 1       â”† 3               â”‚
            â”‚ 3    â”† 3       â”† 3               â”‚
            â”‚ null â”† null    â”† null            â”‚
            â”‚ 2    â”† 3       â”† 2               â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_cum_max(df_pa)
            pyarrow.Table
            a: int64
            cum_max: int64
            cum_max_reverse: int64
            ----
            a: [[1,3,null,2]]
            cum_max: [[1,3,null,3]]
            cum_max_reverse: [[3,3,null,2]]
        c                óF   •— ‰j                  | «      j                  ‰¬«      S rí   )r   Úcum_maxrï   s    €€r    r.   zExpr.cum_max.<locals>.<lambda>T  rð   r"   r/   rñ   s   ``r    rw  zExpr.cum_max  rt  r"   c               ó0   ‡ ‡— ‰ j                  ˆˆ fd„«      S )uõ	  Return the cumulative product of the non-null values in the column.

        Arguments:
            reverse: reverse the operation

        Returns:
            A new expression.

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

            We define a library agnostic function:

            >>> def agnostic_cum_prod(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("a").cum_prod().alias("cum_prod"),
            ...         nw.col("a").cum_prod(reverse=True).alias("cum_prod_reverse"),
            ...     ).to_native()

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

            >>> agnostic_cum_prod(df_pd)
                 a  cum_prod  cum_prod_reverse
            0  1.0       1.0               6.0
            1  3.0       3.0               6.0
            2  NaN       NaN               NaN
            3  2.0       6.0               2.0

            >>> agnostic_cum_prod(df_pl)
            shape: (4, 3)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”† cum_prod â”† cum_prod_reverse â”‚
            â”‚ ---  â”† ---      â”† ---              â”‚
            â”‚ i64  â”† i64      â”† i64              â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 1    â”† 1        â”† 6                â”‚
            â”‚ 3    â”† 3        â”† 6                â”‚
            â”‚ null â”† null     â”† null             â”‚
            â”‚ 2    â”† 6        â”† 2                â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_cum_prod(df_pa)
            pyarrow.Table
            a: int64
            cum_prod: int64
            cum_prod_reverse: int64
            ----
            a: [[1,3,null,2]]
            cum_prod: [[1,3,null,6]]
            cum_prod_reverse: [[6,6,null,2]]
        c                óF   •— ‰j                  | «      j                  ‰¬«      S rí   )r   Úcum_prodrï   s    €€r    r.   zExpr.cum_prod.<locals>.<lambda>—  s   ø€ ˜×/Ñ/°Ó4×=Ñ=ÀgÐ=ÔNr"   r/   rñ   s   ``r    rz  zExpr.cum_prodW  s   ù€ ð~ ~‰~ÜNó
ð 	
r"   )rª   Úcenterc               óX   ‡ ‡‡‡— t        ‰‰¬«      \  ŠŠ‰ j                  ˆˆˆ ˆfd„«      S )uÅ
  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.

        Arguments:
            window_size: The length of the window in number of elements. It must be a
                strictly positive integer.
            min_periods: 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`
            center: Set the labels at the center of the window.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1.0, 2.0, None, 4.0]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a library agnostic function:

            >>> def agnostic_rolling_sum(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         b=nw.col("a").rolling_sum(window_size=3, min_periods=1)
            ...     ).to_native()

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

            >>> agnostic_rolling_sum(df_pd)
                 a    b
            0  1.0  1.0
            1  2.0  3.0
            2  NaN  3.0
            3  4.0  6.0

            >>> agnostic_rolling_sum(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a    â”† b   â”‚
            â”‚ ---  â”† --- â”‚
            â”‚ f64  â”† f64 â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 1.0  â”† 1.0 â”‚
            â”‚ 2.0  â”† 3.0 â”‚
            â”‚ null â”† 3.0 â”‚
            â”‚ 4.0  â”† 6.0 â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_rolling_sum(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[1,2,null,4]]
            b: [[1,3,3,6]]
        ©Úwindow_sizerª   c                óJ   •— ‰j                  | «      j                  ‰‰‰¬«      S ©N)r~  rª   r{  )r   Úrolling_sum©r   r{  rª   r&   r~  s    €€€€r    r.   z"Expr.rolling_sum.<locals>.<lambda>ñ  s*   ø€ ˜×/Ñ/°Ó4×@Ñ@Ø'Ø'Øð Aô r"   ©r   r0   ©r&   r~  rª   r{  s   ````r    r  zExpr.rolling_sumš  ó2   û€ ôd $?Ø#°ô$
Ñ ˆ[ð ~‰~öó
ð 	
r"   c               óX   ‡ ‡‡‡— t        ‰‰¬«      \  ŠŠ‰ j                  ˆˆˆ ˆfd„«      S )uÒ
  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.

        Arguments:
            window_size: The length of the window in number of elements. It must be a
                strictly positive integer.
            min_periods: 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`
            center: Set the labels at the center of the window.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1.0, 2.0, None, 4.0]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a library agnostic function:

            >>> def agnostic_rolling_mean(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         b=nw.col("a").rolling_mean(window_size=3, min_periods=1)
            ...     ).to_native()

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

            >>> agnostic_rolling_mean(df_pd)
                 a    b
            0  1.0  1.0
            1  2.0  1.5
            2  NaN  1.5
            3  4.0  3.0

            >>> agnostic_rolling_mean(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a    â”† b   â”‚
            â”‚ ---  â”† --- â”‚
            â”‚ f64  â”† f64 â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 1.0  â”† 1.0 â”‚
            â”‚ 2.0  â”† 1.5 â”‚
            â”‚ null â”† 1.5 â”‚
            â”‚ 4.0  â”† 3.0 â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_rolling_mean(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[1,2,null,4]]
            b: [[1,1.5,1.5,3]]
        r}  c                óJ   •— ‰j                  | «      j                  ‰‰‰¬«      S r€  )r   Úrolling_meanr‚  s    €€€€r    r.   z#Expr.rolling_mean.<locals>.<lambda>O  s*   ø€ ˜×/Ñ/°Ó4×AÑAØ'Ø'Øð Bô r"   rƒ  r„  s   ````r    rˆ  zExpr.rolling_meanø  r…  r"   )rª   r{  r¹   c               ó\   ‡ ‡‡‡‡— t        ‰‰¬«      \  ŠŠ‰ j                  ˆˆˆˆ ˆfd„«      S )uX  Apply a rolling variance (moving variance) 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 variance.

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

        Arguments:
            window_size: The length of the window in number of elements. It must be a
                strictly positive integer.
            min_periods: 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`.
            center: Set the labels at the center of the window.
            ddof: Delta Degrees of Freedom; the divisor for a length N window is N - ddof.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1.0, 2.0, None, 4.0]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a library agnostic function:

            >>> def agnostic_rolling_var(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         b=nw.col("a").rolling_var(window_size=3, min_periods=1)
            ...     ).to_native()

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

            >>> agnostic_rolling_var(df_pd)
                 a    b
            0  1.0  NaN
            1  2.0  0.5
            2  NaN  0.5
            3  4.0  2.0

            >>> agnostic_rolling_var(df_pl)  #  doctest:+SKIP
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”† b    â”‚
            â”‚ ---  â”† ---  â”‚
            â”‚ f64  â”† f64  â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•¡
            â”‚ 1.0  â”† null â”‚
            â”‚ 2.0  â”† 0.5  â”‚
            â”‚ null â”† 0.5  â”‚
            â”‚ 4.0  â”† 2.0  â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_rolling_var(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[1,2,null,4]]
            b: [[nan,0.5,0.5,2]]
        r}  c                óL   •— ‰j                  | «      j                  ‰‰‰‰¬«      S ©N)r~  rª   r{  r¹   )r   Úrolling_var©r   r{  r¹   rª   r&   r~  s    €€€€€r    r.   z"Expr.rolling_var.<locals>.<lambda>¯  s+   ø€ ˜×/Ñ/°Ó4×@Ñ@Ø'°[ÈÐVZð Aô r"   rƒ  ©r&   r~  rª   r{  r¹   s   `````r    rŒ  zExpr.rolling_varV  s2   ü€ ôh $?Ø#°ô$
Ñ ˆ[ð ~‰~÷ó
ð 	
r"   c               ó\   ‡ ‡‡‡‡— t        ‰‰¬«      \  ŠŠ‰ j                  ˆˆˆˆ ˆfd„«      S )uþ  Apply a rolling standard deviation (moving standard deviation) 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 standard deviation.

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

        Arguments:
            window_size: The length of the window in number of elements. It must be a
                strictly positive integer.
            min_periods: 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`.
            center: Set the labels at the center of the window.
            ddof: Delta Degrees of Freedom; the divisor for a length N window is N - ddof.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1.0, 2.0, None, 4.0]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a library agnostic function:

            >>> def agnostic_rolling_std(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         b=nw.col("a").rolling_std(window_size=3, min_periods=1)
            ...     ).to_native()

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

            >>> agnostic_rolling_std(df_pd)
                 a         b
            0  1.0       NaN
            1  2.0  0.707107
            2  NaN  0.707107
            3  4.0  1.414214

            >>> agnostic_rolling_std(df_pl)  #  doctest:+SKIP
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a    â”† b        â”‚
            â”‚ ---  â”† ---      â”‚
            â”‚ f64  â”† f64      â”‚
            â•žâ•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 1.0  â”† null     â”‚
            â”‚ 2.0  â”† 0.707107 â”‚
            â”‚ null â”† 0.707107 â”‚
            â”‚ 4.0  â”† 1.414214 â”‚
            â””â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_rolling_std(df_pa)
            pyarrow.Table
            a: double
            b: double
            ----
            a: [[1,2,null,4]]
            b: [[nan,0.7071067811865476,0.7071067811865476,1.4142135623730951]]
        r}  c                óL   •— ‰j                  | «      j                  ‰‰‰‰¬«      S r‹  )r   Úrolling_stdr  s    €€€€€r    r.   z"Expr.rolling_std.<locals>.<lambda>  s-   ø€ ˜×/Ñ/°Ó4×@Ñ@Ø'Ø'ØØð	 Aô r"   rƒ  rŽ  s   `````r    r‘  zExpr.rolling_std´  s2   ü€ ôh $?Ø#°ô$
Ñ ˆ[ð ~‰~÷ó
ð 	
r"   )r  c               óf   ‡ ‡‡— h d£}‰|vrd‰› d}t        |«      ‚‰ j                  ˆˆˆ fd„«      S )uŒ  Assign ranks to data, dealing with ties appropriately.

        Notes:
            The resulting dtype may differ between backends.

        Arguments:
            method: The method used to assign ranks to tied elements.
                The following methods are available (default is 'average'):

                - 'average' : The average of the ranks that would have been assigned to
                  all the tied values is assigned to each value.
                - 'min' : The minimum of the ranks that would have been assigned to all
                    the tied values is assigned to each value. (This is also referred to
                    as "competition" ranking.)
                - 'max' : The maximum of the ranks that would have been assigned to all
                    the tied values is assigned to each value.
                - 'dense' : Like 'min', but the rank of the next highest element is
                   assigned the rank immediately after those assigned to the tied
                   elements.
                - 'ordinal' : All values are given a distinct rank, corresponding to the
                    order that the values occur in the Series.

            descending: Rank in descending order.

        Returns:
            A new expression with rank data.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [3, 6, 1, 1, 6]}

            We define a dataframe-agnostic function that computes the dense rank for
            the data:

            >>> def agnostic_dense_rank(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     result = df.with_columns(rnk=nw.col("a").rank(method="dense"))
            ...     return result.to_native()

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

            >>> agnostic_dense_rank(pd.DataFrame(data))
               a  rnk
            0  3  2.0
            1  6  3.0
            2  1  1.0
            3  1  1.0
            4  6  3.0

            >>> agnostic_dense_rank(pl.DataFrame(data))
            shape: (5, 2)
            â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ a   â”† rnk â”‚
            â”‚ --- â”† --- â”‚
            â”‚ i64 â”† u32 â”‚
            â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 3   â”† 2   â”‚
            â”‚ 6   â”† 3   â”‚
            â”‚ 1   â”† 1   â”‚
            â”‚ 1   â”† 1   â”‚
            â”‚ 6   â”† 3   â”‚
            â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dense_rank(pa.table(data))
            pyarrow.Table
            a: int64
            rnk: uint64
            ----
            a: [[3,6,1,1,6]]
            rnk: [[2,3,1,1,3]]
        >   rÒ   rÏ   ÚdenseÚaverageÚordinalzTRanking method must be one of {'average', 'min', 'max', 'dense', 'ordinal'}. Found 'Ú'c                óH   •— ‰j                  | «      j                  ‰‰¬«      S )N)Úmethodr  )r   Úrank)r   r  r˜  r&   s    €€€r    r.   zExpr.rank.<locals>.<lambda>q  s%   ø€ ˜×/Ñ/°Ó4×9Ñ9Ø¨*ð :ô r"   r/  )r&   r˜  r  Úsupported_rank_methodsr  s   ```  r    r™  z	Expr.rank  sN   ú€ òf "OÐØÐ/Ñ/ðØ ˜ ð$ð ô ˜S“/Ð!à~‰~õó
ð 	
r"   c                ó   — t        | «      S r%   )ÚExprStringNamespacer1   s    r    r  zExpr.strv  s   € ä" 4Ó(Ð(r"   c                ó   — t        | «      S r%   )ÚExprDateTimeNamespacer1   s    r    ÚdtzExpr.dtz  s   € ä$ TÓ*Ð*r"   c                ó   — t        | «      S r%   )ÚExprCatNamespacer1   s    r    ÚcatzExpr.cat~  s   € ä Ó%Ð%r"   c                ó   — t        | «      S r%   )ÚExprNameNamespacer1   s    r    r6   z	Expr.name‚  ó   € ä  Ó&Ð&r"   c                ó   — t        | «      S r%   )ÚExprListNamespacer1   s    r    r  z	Expr.list†  r¥  r"   )r'   zCallable[[Any], Any]rW   ÚNone)rW   r   )r6   r  rW   r   )r9   úCallable[[Any], Self]r:   r   r;   r   rW   r   )r&   r   r@   zDType | type[DType]rW   r   )r   ÚobjectrW   r   )r   r   rW   r   )r&   r   r¥   úfloat | Noner¦   r«  r§   r«  r¨   r«  r©   Úboolrª   Úintr«   r¬  rW   r   )r¹   r­  rW   r   r%   )r9   r©  rÆ   zDType | NonerW   r   )r&   r   rW   r   ©rW   r   )rã   r¬  rW   r   )r&   r   rê   r¬  rW   r   )rù   r­  rW   r   )r   z!Sequence[Any] | Mapping[Any, Any]rÿ   zSequence[Any] | NonerÆ   úDType | type[DType] | NonerW   r   )r  r¬  r  r¬  rW   r   )Úboth)r  úAny | IntoExprr  r±  r  r  rW   r   )r  r   rW   r   )NNN)r+  z
Any | Noner,  z%Literal['forward', 'backward'] | Noner-  ú
int | NonerW   r   )r&   r   rù   r²  r5  r«  r6  r¬  r7  r²  rW   r   )r  ústr | Iterable[str]rW   r   )rP  ÚfloatrQ  z;Literal['nearest', 'higher', 'lower', 'midpoint', 'linear']rW   r   )é
   )r   )r]  r­  rW   r   )r&   r   rù   r­  rc  r­  rW   r   )NN)r  úIntoExpr | Any | Noner  r¶  rW   r   )
r&   r   r~  r­  rª   r²  r{  r¬  rW   r   )r&   r   r~  r­  rª   r²  r{  r¬  r¹   r­  rW   r   )r”  )r&   r   r˜  z4Literal['average', 'min', 'max', 'dense', 'ordinal']r  r¬  rW   r   )r&   r   rW   zExprStringNamespace[Self])r&   r   rW   zExprDateTimeNamespace[Self])r&   r   rW   zExprCatNamespace[Self])r&   r   rW   zExprNameNamespace[Self])r&   r   rW   zExprListNamespace[Self])_Ú__name__Ú
__module__Ú__qualname__r(   r2   r5   r<   r?   rC   rJ   rM   rY   r]   r`   rc   rf   ri   rl   ro   rr   ru   rx   r{   r~   r   r„   r‡   rŠ   r   r   r“   r–   r™   r   r¢   r®   r±   r¶   r½   rÃ   rÇ   rÊ   r,   rÏ   rÒ   rÕ   rÚ   rÝ   rà   ræ   r+   rî   rô   r÷   rþ   r  r  r  r  r   r#  r&  r.  r3  r:  r=  r@  rC  rG  rJ  rM  rP  rT  rY  r\  r`  rd  rg  rj  rm  rp  rs  rw  rz  r  rˆ  rŒ  r‘  r™  Úpropertyr  rŸ  r¢  r6   r  r8   r"   r    r   r   (   sÎ  „ ó4óTó5Tón:/óx;
ó|
ó

ó

ó$ó
ó
$ó
ó$ó
ó$ó
ó$ó
ó$ó
ó

ó

ó

ó

ó$ó
ó$ó
ó$óUó1Nóf1Nðl !Ø!Ø"&Ø"ØØØ"ñd
Øðd
ð ðd
ð ð	d
ð
  ðd
ð ðd
ð ðd
ð ðd
ð ðd
ð 
ód
óL1Oóf4Qðl "#õ 4Wðl "#õ 5Wðt &*ðI
à'ðI
ð #ðI
ð 
ó	I
óV1Oóf/Nób1Nóf1Nóf3Rój3Rój1Póf/Sðb 05õ <
ó|3Nðj 05õ <
ó|@OóDCQðP %)ðZ
ð
 48ñZ
à.ðZ
ð "ðZ
ð
 1ðZ
ð 
óZ
ðx */À5õ [
ðD ð	G
à#ðG
ð $ðG
ð ð	G
ð
 
óG
óRB+óH?
óBLRó\?QóB1Sðj !Ø:>Ø ð	M
àðM
ð 8ðM
ð ð	M
ð
 
óM
ó`9Uðz ðG
ð "&Ø!&ØñG
ØðG
àðG
ð ð	G
ð
 ðG
ð ðG
ð 
óG
óRY
óv7Xór7Tór6Uóp9
óv7[ðrD
àðD
ð SðD
ð 
ó	D
ôL6Pôp6Pôp?XóB7Nôr8
ð| .2Ø-1ðO
à*ðO
ð +ðO
ð 
ó	O
ób4Oól:Tðx 27õ A
ðF 05õ A
ðF 05õ A
ðF 16õ A
ðN #'Øñ\
Øð\
àð\
ð  ð	\
ð
 ð\
ð 
ó\
ðD #'Øñ\
Øð\
àð\
ð  ð	\
ð
 ð\
ð 
ó\
ðD #'ØØñ\
Øð\
àð\
ð  ð	\
ð
 ð\
ð ð\
ð 
ó\
ðD #'ØØñ_
Øð_
àð_
ð  ð	_
ð
 ð_
ð ð_
ð 
ó_
ðF HQð_
ð !ñ	_
Øð_
àDð_
ð ð	_
ð
 
ó_
ðB ò)ó ð)ð ò+ó ð+ð ò&ó ð&ð ò'ó ð'ð ò'ó ñ'r"   r   ÚExprT)Úboundc                  ó   — e Zd Zdd„Zdd„Zy)r¡  c                ó   — || _         y r%   ©Ú_expr©r&   Úexprs     r    r(   zExprCatNamespace.__init__  ó	   € Øˆ
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uÑ  Get unique categories from column.

        Returns:
            A new expression.

        Examples:
            Let's create some dataframes:

            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"fruits": ["apple", "mango", "mango"]}
            >>> df_pd = pd.DataFrame(data, dtype="category")
            >>> df_pl = pl.DataFrame(data, schema={"fruits": pl.Categorical})

            We define a dataframe-agnostic function to get unique categories
            from column 'fruits':

            >>> def agnostic_cat_get_categories(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("fruits").cat.get_categories()).to_native()

           We can then pass any supported library such as pandas or Polars to
           `agnostic_cat_get_categories`:

            >>> agnostic_cat_get_categories(df_pd)
              fruits
            0  apple
            1  mango

            >>> agnostic_cat_get_categories(df_pl)
            shape: (2, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ fruits â”‚
            â”‚ ---    â”‚
            â”‚ str    â”‚
            â•žâ•â•â•â•â•â•â•â•â•¡
            â”‚ apple  â”‚
            â”‚ mango  â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”˜
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   r¢  Úget_categoriesr-   s    €r    r.   z1ExprCatNamespace.get_categories.<locals>.<lambda>À  s#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×MÑMÔOr"   ©rÀ  r0   r1   s   `r    rÆ  zExprCatNamespace.get_categories’  s    ø€ ðZ z‰z×#Ñ#ÛOó
ð 	
r"   N©r&   r   rÂ  r»  rW   r¨  ©r&   r   rW   r»  )r·  r¸  r¹  r(   rÆ  r8   r"   r    r¡  r¡  Ž  s   „ óô/
r"   r¡  c                  ó¾   — e Zd Zdd„Zdd„Zdddœ	 	 	 	 	 	 	 	 	 dd„Zddœ	 	 	 	 	 	 	 	 	 dd„Zddd
„Zdd„Zdd„Z	ddœdd„Z
ddd„Zddd„Zddd„Zdd d„Zdd„Zdd„Zy	)!rœ  c                ó   — || _         y r%   r¿  rÁ  s     r    r(   zExprStringNamespace.__init__Å  rÃ  r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uE  Return the length of each string as the number of characters.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"words": ["foo", "CafÃ©", "345", "æ±äº¬", None]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_len_chars(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         words_len=nw.col("words").str.len_chars()
            ...     ).to_native()

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

            >>> agnostic_str_len_chars(df_pd)
              words  words_len
            0   foo        3.0
            1  CafÃ©        4.0
            2   345        3.0
            3    æ±äº¬        2.0
            4  None        NaN

            >>> agnostic_str_len_chars(df_pl)
            shape: (5, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ words â”† words_len â”‚
            â”‚ ---   â”† ---       â”‚
            â”‚ str   â”† u32       â”‚
            â•žâ•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ foo   â”† 3         â”‚
            â”‚ CafÃ©  â”† 4         â”‚
            â”‚ 345   â”† 3         â”‚
            â”‚ æ±äº¬  â”† 2         â”‚
            â”‚ null  â”† null      â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_len_chars(df_pa)
            pyarrow.Table
            words: string
            words_len: int32
            ----
            words: [["foo","CafÃ©","345","æ±äº¬",null]]
            words_len: [[3,4,3,2,null]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   r  Ú	len_charsr-   s    €r    r.   z/ExprStringNamespace.len_chars.<locals>.<lambda>  s#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×HÑHÔJr"   rÇ  r1   s   `r    rÎ  zExprStringNamespace.len_charsÈ  s    ø€ ðv z‰z×#Ñ#ÛJó
ð 	
r"   Fr£   ©Úliteralrù   c               óP   ‡ ‡‡‡‡— ‰ j                   j                  ˆˆˆˆ ˆfd„«      S )uv  Replace first matching regex/literal substring with a new string value.

        Arguments:
            pattern: A valid regular expression pattern.
            value: String that will replace the matched substring.
            literal: Treat `pattern` as a literal string.
            n: Number of matches to replace.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"foo": ["123abc", "abc abc123"]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_replace(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     df = df.with_columns(replaced=nw.col("foo").str.replace("abc", ""))
            ...     return df.to_native()

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

            >>> agnostic_str_replace(df_pd)
                      foo replaced
            0      123abc      123
            1  abc abc123   abc123

            >>> agnostic_str_replace(df_pl)
            shape: (2, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ foo        â”† replaced â”‚
            â”‚ ---        â”† ---      â”‚
            â”‚ str        â”† str      â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 123abc     â”† 123      â”‚
            â”‚ abc abc123 â”†  abc123  â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_replace(df_pa)
            pyarrow.Table
            foo: string
            replaced: string
            ----
            foo: [["123abc","abc abc123"]]
            replaced: [["123"," abc123"]]
        c                ót   •— ‰j                   j                  | «      j                  j                  ‰‰‰‰¬«      S )NrÏ  )rÀ  r   r  Úreplace)r   rÐ  rù   Úpatternr&   r+  s    €€€€€r    r.   z-ExprStringNamespace.replace.<locals>.<lambda>D  s4   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×FÑFØ˜¨°1ð Gô r"   rÇ  )r&   rÔ  r+  rÐ  rù   s   `````r    rÓ  zExprStringNamespace.replace  s"   ü€ ðx z‰z×#Ñ#÷ó
ð 	
r"   ©rÐ  c               óL   ‡ ‡‡‡— ‰ j                   j                  ˆˆˆ ˆfd„«      S )u\  Replace all matching regex/literal substring with a new string value.

        Arguments:
            pattern: A valid regular expression pattern.
            value: String that will replace the matched substring.
            literal: Treat `pattern` as a literal string.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"foo": ["123abc", "abc abc123"]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_replace_all(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     df = df.with_columns(replaced=nw.col("foo").str.replace_all("abc", ""))
            ...     return df.to_native()

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

            >>> agnostic_str_replace_all(df_pd)
                      foo replaced
            0      123abc      123
            1  abc abc123      123

            >>> agnostic_str_replace_all(df_pl)
            shape: (2, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ foo        â”† replaced â”‚
            â”‚ ---        â”† ---      â”‚
            â”‚ str        â”† str      â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 123abc     â”† 123      â”‚
            â”‚ abc abc123 â”†  123     â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_replace_all(df_pa)
            pyarrow.Table
            foo: string
            replaced: string
            ----
            foo: [["123abc","abc abc123"]]
            replaced: [["123"," 123"]]
        c                ór   •— ‰j                   j                  | «      j                  j                  ‰‰‰¬«      S ©NrÕ  )rÀ  r   r  Úreplace_all)r   rÐ  rÔ  r&   r+  s    €€€€r    r.   z1ExprStringNamespace.replace_all.<locals>.<lambda>…  s2   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×JÑJØ˜¨ð Kô r"   rÇ  )r&   rÔ  r+  rÐ  s   ````r    rÙ  zExprStringNamespace.replace_allI  s"   û€ ðv z‰z×#Ñ#öó
ð 	
r"   Nc                óD   ‡ ‡— ‰ j                   j                  ˆˆ fd„«      S )a¦  Remove leading and trailing characters.

        Arguments:
            characters: The set of characters to be removed. All combinations of this
                set of characters will be stripped from the start and end of the string.
                If set to None (default), all leading and trailing whitespace is removed
                instead.

        Returns:
            A new expression.

        Examples:
            >>> from typing import Any
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> data = {"fruits": ["apple", "\nmango"]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_strip_chars(df_native: IntoFrame) -> dict[str, Any]:
            ...     df = nw.from_native(df_native)
            ...     df = df.with_columns(stripped=nw.col("fruits").str.strip_chars())
            ...     return df.to_dict(as_series=False)

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

            >>> agnostic_str_strip_chars(df_pd)
            {'fruits': ['apple', '\nmango'], 'stripped': ['apple', 'mango']}

            >>> agnostic_str_strip_chars(df_pl)
            {'fruits': ['apple', '\nmango'], 'stripped': ['apple', 'mango']}

            >>> agnostic_str_strip_chars(df_pa)
            {'fruits': ['apple', '\nmango'], 'stripped': ['apple', 'mango']}
        c                ól   •— ‰j                   j                  | «      j                  j                  ‰«      S r%   )rÀ  r   r  Ústrip_chars)r   Ú
charactersr&   s    €€r    r.   z1ExprStringNamespace.strip_chars.<locals>.<lambda>·  s%   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×JÑJÈ:ÔVr"   rÇ  )r&   rÝ  s   ``r    rÜ  zExprStringNamespace.strip_charsŠ  s    ù€ ðX z‰z×#Ñ#ÜVó
ð 	
r"   c                óD   ‡ ‡— ‰ j                   j                  ˆˆ fd„«      S )uù  Check if string values start with a substring.

        Arguments:
            prefix: prefix substring

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"fruits": ["apple", "mango", None]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_starts_with(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         has_prefix=nw.col("fruits").str.starts_with("app")
            ...     ).to_native()

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

            >>> agnostic_str_starts_with(df_pd)
              fruits has_prefix
            0  apple       True
            1  mango      False
            2   None       None

            >>> agnostic_str_starts_with(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ fruits â”† has_prefix â”‚
            â”‚ ---    â”† ---        â”‚
            â”‚ str    â”† bool       â”‚
            â•žâ•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ apple  â”† true       â”‚
            â”‚ mango  â”† false      â”‚
            â”‚ null   â”† null       â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_starts_with(df_pa)
            pyarrow.Table
            fruits: string
            has_prefix: bool
            ----
            fruits: [["apple","mango",null]]
            has_prefix: [[true,false,null]]
        c                ól   •— ‰j                   j                  | «      j                  j                  ‰«      S r%   )rÀ  r   r  Ústarts_with©r   Úprefixr&   s    €€r    r.   z1ExprStringNamespace.starts_with.<locals>.<lambda>õ  s%   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×JÑJÈ6ÔRr"   rÇ  ©r&   râ  s   ``r    rà  zExprStringNamespace.starts_withº  s    ù€ ðt z‰z×#Ñ#ÜRó
ð 	
r"   c                óD   ‡ ‡— ‰ j                   j                  ˆ ˆfd„«      S )uë  Check if string values end with a substring.

        Arguments:
            suffix: suffix substring

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"fruits": ["apple", "mango", None]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_ends_with(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         has_suffix=nw.col("fruits").str.ends_with("ngo")
            ...     ).to_native()

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

            >>> agnostic_str_ends_with(df_pd)
              fruits has_suffix
            0  apple      False
            1  mango       True
            2   None       None

            >>> agnostic_str_ends_with(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ fruits â”† has_suffix â”‚
            â”‚ ---    â”† ---        â”‚
            â”‚ str    â”† bool       â”‚
            â•žâ•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ apple  â”† false      â”‚
            â”‚ mango  â”† true       â”‚
            â”‚ null   â”† null       â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_ends_with(df_pa)
            pyarrow.Table
            fruits: string
            has_suffix: bool
            ----
            fruits: [["apple","mango",null]]
            has_suffix: [[false,true,null]]
        c                ól   •— ‰j                   j                  | «      j                  j                  ‰«      S r%   )rÀ  r   r  Ú	ends_with©r   r&   Úsuffixs    €€r    r.   z/ExprStringNamespace.ends_with.<locals>.<lambda>3  s%   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×HÑHÈÔPr"   rÇ  ©r&   rè  s   ``r    ræ  zExprStringNamespace.ends_withø  s    ù€ ðt z‰z×#Ñ#ÜPó
ð 	
r"   c               óH   ‡ ‡‡— ‰ j                   j                  ˆˆˆ fd„«      S )uG  Check if string contains a substring that matches a pattern.

        Arguments:
            pattern: A Character sequence or valid regular expression pattern.
            literal: If True, treats the pattern as a literal string.
                     If False, assumes the pattern is a regular expression.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"pets": ["cat", "dog", "rabbit and parrot", "dove", None]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_contains(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         default_match=nw.col("pets").str.contains("parrot|Dove"),
            ...         case_insensitive_match=nw.col("pets").str.contains("(?i)parrot|Dove"),
            ...         literal_match=nw.col("pets").str.contains(
            ...             "parrot|Dove", literal=True
            ...         ),
            ...     ).to_native()

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

            >>> agnostic_str_contains(df_pd)
                            pets default_match case_insensitive_match literal_match
            0                cat         False                  False         False
            1                dog         False                  False         False
            2  rabbit and parrot          True                   True         False
            3               dove         False                   True         False
            4               None          None                   None          None

            >>> agnostic_str_contains(df_pl)
            shape: (5, 4)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ pets              â”† default_match â”† case_insensitive_match â”† literal_match â”‚
            â”‚ ---               â”† ---           â”† ---                    â”† ---           â”‚
            â”‚ str               â”† bool          â”† bool                   â”† bool          â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ cat               â”† false         â”† false                  â”† false         â”‚
            â”‚ dog               â”† false         â”† false                  â”† false         â”‚
            â”‚ rabbit and parrot â”† true          â”† true                   â”† false         â”‚
            â”‚ dove              â”† false         â”† true                   â”† false         â”‚
            â”‚ null              â”† null          â”† null                   â”† null          â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_contains(df_pa)
            pyarrow.Table
            pets: string
            default_match: bool
            case_insensitive_match: bool
            literal_match: bool
            ----
            pets: [["cat","dog","rabbit and parrot","dove",null]]
            default_match: [[false,false,true,false,null]]
            case_insensitive_match: [[false,false,true,true,null]]
            literal_match: [[false,false,false,false,null]]
        c                óp   •— ‰j                   j                  | «      j                  j                  ‰‰¬«      S rØ  )rÀ  r   r  Úcontains)r   rÐ  rÔ  r&   s    €€€r    r.   z.ExprStringNamespace.contains.<locals>.<lambda>  s0   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×GÑGØ ð Hô r"   rÇ  )r&   rÔ  rÐ  s   ```r    rì  zExprStringNamespace.contains6  s"   ú€ ðP z‰z×#Ñ#õó
ð 	
r"   c                óH   ‡ ‡‡— ‰ j                   j                  ˆˆˆ fd„«      S )u¶  Create subslices of the string values of an expression.

        Arguments:
            offset: Start index. Negative indexing is supported.
            length: Length of the slice. If set to `None` (default), the slice is taken to the
                end of the string.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"s": ["pear", None, "papaya", "dragonfruit"]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_slice(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         s_sliced=nw.col("s").str.slice(4, length=3)
            ...     ).to_native()

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

            >>> agnostic_str_slice(df_pd)  # doctest: +NORMALIZE_WHITESPACE
                         s s_sliced
            0         pear
            1         None     None
            2       papaya       ya
            3  dragonfruit      onf

            >>> agnostic_str_slice(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ s           â”† s_sliced â”‚
            â”‚ ---         â”† ---      â”‚
            â”‚ str         â”† str      â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ pear        â”†          â”‚
            â”‚ null        â”† null     â”‚
            â”‚ papaya      â”† ya       â”‚
            â”‚ dragonfruit â”† onf      â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_slice(df_pa)
            pyarrow.Table
            s: string
            s_sliced: string
            ----
            s: [["pear",null,"papaya","dragonfruit"]]
            s_sliced: [["",null,"ya","onf"]]

            Using negative indexes:

            >>> def agnostic_str_slice_negative(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(s_sliced=nw.col("s").str.slice(-3)).to_native()

            >>> agnostic_str_slice_negative(df_pd)
                         s s_sliced
            0         pear      ear
            1         None     None
            2       papaya      aya
            3  dragonfruit      uit

            >>> agnostic_str_slice_negative(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ s           â”† s_sliced â”‚
            â”‚ ---         â”† ---      â”‚
            â”‚ str         â”† str      â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ pear        â”† ear      â”‚
            â”‚ null        â”† null     â”‚
            â”‚ papaya      â”† aya      â”‚
            â”‚ dragonfruit â”† uit      â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_slice_negative(df_pa)
            pyarrow.Table
            s: string
            s_sliced: string
            ----
            s: [["pear",null,"papaya","dragonfruit"]]
            s_sliced: [["ear",null,"aya","uit"]]
        c                óp   •— ‰j                   j                  | «      j                  j                  ‰‰¬«      S ©N)rc  Úlength©rÀ  r   r  Úslice)r   rð  rc  r&   s    €€€r    r.   z+ExprStringNamespace.slice.<locals>.<lambda>å  s0   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×DÑDØ fð Eô r"   rÇ  )r&   rc  rð  s   ```r    rò  zExprStringNamespace.slice„  s"   ú€ ð@ z‰z×#Ñ#õó
ð 	
r"   c                óD   ‡ ‡— ‰ j                   j                  ˆˆ fd„«      S )uF	  Take the first n elements of each string.

        Arguments:
            n: Number of elements to take. Negative indexing is **not** supported.

        Returns:
            A new expression.

        Notes:
            If the length of the string has fewer than `n` characters, the full string is returned.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"lyrics": ["Atatata", "taata", "taatatata", "zukkyun"]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_head(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         lyrics_head=nw.col("lyrics").str.head()
            ...     ).to_native()

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

            >>> agnostic_str_head(df_pd)
                  lyrics lyrics_head
            0    Atatata       Atata
            1      taata       taata
            2  taatatata       taata
            3    zukkyun       zukky

            >>> agnostic_str_head(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ lyrics    â”† lyrics_head â”‚
            â”‚ ---       â”† ---         â”‚
            â”‚ str       â”† str         â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ Atatata   â”† Atata       â”‚
            â”‚ taata     â”† taata       â”‚
            â”‚ taatatata â”† taata       â”‚
            â”‚ zukkyun   â”† zukky       â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_head(df_pa)
            pyarrow.Table
            lyrics: string
            lyrics_head: string
            ----
            lyrics: [["Atatata","taata","taatatata","zukkyun"]]
            lyrics_head: [["Atata","taata","taata","zukky"]]
        c                ón   •— ‰j                   j                  | «      j                  j                  d‰«      S )Nr   rñ  rø   s    €€r    r.   z*ExprStringNamespace.head.<locals>.<lambda>*  s'   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×DÑDÀQÈÔJr"   rÇ  rú   s   ``r    rT  zExprStringNamespace.headê  s    ù€ ð~ z‰z×#Ñ#ÜJó
ð 	
r"   c                óD   ‡ ‡— ‰ j                   j                  ˆˆ fd„«      S )uE	  Take the last n elements of each string.

        Arguments:
            n: Number of elements to take. Negative indexing is **not** supported.

        Returns:
            A new expression.

        Notes:
            If the length of the string has fewer than `n` characters, the full string is returned.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"lyrics": ["Atatata", "taata", "taatatata", "zukkyun"]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_tail(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         lyrics_tail=nw.col("lyrics").str.tail()
            ...     ).to_native()

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

            >>> agnostic_str_tail(df_pd)
                  lyrics lyrics_tail
            0    Atatata       atata
            1      taata       taata
            2  taatatata       atata
            3    zukkyun       kkyun

            >>> agnostic_str_tail(df_pl)
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ lyrics    â”† lyrics_tail â”‚
            â”‚ ---       â”† ---         â”‚
            â”‚ str       â”† str         â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ Atatata   â”† atata       â”‚
            â”‚ taata     â”† taata       â”‚
            â”‚ taatatata â”† atata       â”‚
            â”‚ zukkyun   â”† kkyun       â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_tail(df_pa)
            pyarrow.Table
            lyrics: string
            lyrics_tail: string
            ----
            lyrics: [["Atatata","taata","taatatata","zukkyun"]]
            lyrics_tail: [["atata","taata","atata","kkyun"]]
        c                ór   •— ‰j                   j                  | «      j                  j                  ‰ d ¬«      S rï  rñ  rø   s    €€r    r.   z*ExprStringNamespace.tail.<locals>.<lambda>m  s2   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×DÑDØr $ð Eô r"   rÇ  rú   s   ``r    rY  zExprStringNamespace.tail-  s"   ù€ ð~ z‰z×#Ñ#ôó
ð 	
r"   c                óD   ‡ ‡— ‰ j                   j                  ˆˆ fd„«      S )u¢	  Convert to Datetime dtype.

        Warning:
            As different backends auto-infer format in different ways, if `format=None`
            there is no guarantee that the result will be equal.

        Arguments:
            format: Format to use for conversion. If set to None (default), the format is
                inferred from the data.

        Returns:
            A new expression.

        Notes:
            pandas defaults to nanosecond time unit, Polars to microsecond.
            Prior to pandas 2.0, nanoseconds were the only time unit supported
            in pandas, with no ability to set any other one. The ability to
            set the time unit in pandas, if the version permits, will arrive.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = ["2020-01-01", "2020-01-02"]
            >>> df_pd = pd.DataFrame({"a": data})
            >>> df_pl = pl.DataFrame({"a": data})
            >>> df_pa = pa.table({"a": data})

            We define a dataframe-agnostic function:

            >>> def agnostic_str_to_datetime(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a").str.to_datetime(format="%Y-%m-%d")
            ...     ).to_native()

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

            >>> agnostic_str_to_datetime(df_pd)
                       a
            0 2020-01-01
            1 2020-01-02

            >>> agnostic_str_to_datetime(df_pl)
            shape: (2, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a                   â”‚
            â”‚ ---                 â”‚
            â”‚ datetime[Î¼s]        â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2020-01-01 00:00:00 â”‚
            â”‚ 2020-01-02 00:00:00 â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_to_datetime(df_pa)
            pyarrow.Table
            a: timestamp[us]
            ----
            a: [[2020-01-01 00:00:00.000000,2020-01-02 00:00:00.000000]]
        c                ón   •— ‰j                   j                  | «      j                  j                  ‰¬«      S )N)Úformat)rÀ  r   r  Úto_datetime©r   rù  r&   s    €€r    r.   z1ExprStringNamespace.to_datetime.<locals>.<lambda>´  s)   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×JÑJÐRXÐJÔYr"   rÇ  ©r&   rù  s   ``r    rú  zExprStringNamespace.to_datetimer  s    ù€ ðB z‰z×#Ñ#ÜYó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uÃ  Transform string to uppercase variant.

        Returns:
            A new expression.

        Notes:
            The PyArrow backend will convert 'ÃŸ' to 'áºž' instead of 'SS'.
            For more info see [the related issue](https://github.com/apache/arrow/issues/34599).
            There may be other unicode-edge-case-related variations across implementations.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"fruits": ["apple", "mango", None]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_to_uppercase(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         upper_col=nw.col("fruits").str.to_uppercase()
            ...     ).to_native()

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

            >>> agnostic_str_to_uppercase(df_pd)
              fruits upper_col
            0  apple     APPLE
            1  mango     MANGO
            2   None      None

            >>> agnostic_str_to_uppercase(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ fruits â”† upper_col â”‚
            â”‚ ---    â”† ---       â”‚
            â”‚ str    â”† str       â”‚
            â•žâ•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ apple  â”† APPLE     â”‚
            â”‚ mango  â”† MANGO     â”‚
            â”‚ null   â”† null      â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_to_uppercase(df_pa)
            pyarrow.Table
            fruits: string
            upper_col: string
            ----
            fruits: [["apple","mango",null]]
            upper_col: [["APPLE","MANGO",null]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   r  Úto_uppercaser-   s    €r    r.   z2ExprStringNamespace.to_uppercase.<locals>.<lambda>ô  ó#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×>Ñ>×KÑKÔMr"   rÇ  r1   s   `r    rÿ  z ExprStringNamespace.to_uppercase·  s    ø€ ðx z‰z×#Ñ#ÛMó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uª  Transform string to lowercase variant.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"fruits": ["APPLE", "MANGO", None]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_str_to_lowercase(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         lower_col=nw.col("fruits").str.to_lowercase()
            ...     ).to_native()

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

            >>> agnostic_str_to_lowercase(df_pd)
              fruits lower_col
            0  APPLE     apple
            1  MANGO     mango
            2   None      None

            >>> agnostic_str_to_lowercase(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ fruits â”† lower_col â”‚
            â”‚ ---    â”† ---       â”‚
            â”‚ str    â”† str       â”‚
            â•žâ•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ APPLE  â”† apple     â”‚
            â”‚ MANGO  â”† mango     â”‚
            â”‚ null   â”† null      â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_str_to_lowercase(df_pa)
            pyarrow.Table
            fruits: string
            lower_col: string
            ----
            fruits: [["APPLE","MANGO",null]]
            lower_col: [["apple","mango",null]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   r  Úto_lowercaser-   s    €r    r.   z2ExprStringNamespace.to_lowercase.<locals>.<lambda>/  r   r"   rÇ  r1   s   `r    r  z ExprStringNamespace.to_lowercase÷  s    ø€ ðn z‰z×#Ñ#ÛMó
ð 	
r"   rÈ  rÉ  )
rÔ  r  r+  r  rÐ  r¬  rù   r­  rW   r»  )
r&   r   rÔ  r  r+  r  rÐ  r¬  rW   r»  r%   )r&   r   rÝ  ú
str | NonerW   r»  ©r&   r   râ  r  rW   r»  ©r&   r   rè  r  rW   r»  )r&   r   rÔ  r  rÐ  r¬  rW   r»  )r&   r   rc  r­  rð  r²  rW   r»  )é   )r&   r   rù   r­  rW   r»  )r&   r   rù  r  rW   r»  )r·  r¸  r¹  r(   rÎ  rÓ  rÙ  rÜ  rà  ræ  rì  rò  rT  rY  rú  rÿ  r  r8   r"   r    rœ  rœ  Ä  sÄ   „ óó=
ð@ <AÈ1ñ@
Øð@
Ø#&ð@
Ø48ð@
ØEHð@
à	ó@
ðF BGñ?
Øð?
Ø ð?
Ø),ð?
Ø:>ð?
à	ó?
ôB.
ó`<
ó|<
ð| ?Dõ L
ô\d
ôLA
ôFC
ôJC
óJ>
ô@9
r"   rœ  c                  ó¾   — e Zd Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Z	dd„Z
dd	„Zdd
„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zddd„Zy)rž  c                ó   — || _         y r%   r¿  rÁ  s     r    r(   zExprDateTimeNamespace.__init__4  rÃ  r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uð  Extract the date from underlying DateTime representation.

        Returns:
            A new expression.

        Raises:
            NotImplementedError: If pandas default backend is being used.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [datetime(2012, 1, 7, 10, 20), datetime(2023, 3, 10, 11, 32)]}
            >>> df_pd = pd.DataFrame(data).convert_dtypes(dtype_backend="pyarrow")
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a library agnostic function:

            >>> def agnostic_dt_date(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("a").dt.date()).to_native()

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

            >>> agnostic_dt_date(df_pd)
                        a
            0  2012-01-07
            1  2023-03-10

            >>> agnostic_dt_date(df_pl)
            shape: (2, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a          â”‚
            â”‚ ---        â”‚
            â”‚ date       â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2012-01-07 â”‚
            â”‚ 2023-03-10 â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_date(df_pa)
            pyarrow.Table
            a: date32[day]
            ----
            a: [[2012-01-07,2023-03-10]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Údater-   s    €r    r.   z,ExprDateTimeNamespace.date.<locals>.<lambda>m  ó#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×BÑBÔDr"   rÇ  r1   s   `r    r  zExprDateTimeNamespace.date7  s    ø€ ðj z‰z×#Ñ#ÛDó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uP	  Extract year from underlying DateTime representation.

        Returns the year number in the calendar date.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "datetime": [
            ...         datetime(1978, 6, 1),
            ...         datetime(2024, 12, 13),
            ...         datetime(2065, 1, 1),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_year(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("datetime").dt.year().alias("year")
            ...     ).to_native()

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

            >>> agnostic_dt_year(df_pd)
                datetime  year
            0 1978-06-01  1978
            1 2024-12-13  2024
            2 2065-01-01  2065

            >>> agnostic_dt_year(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”
            â”‚ datetime            â”† year â”‚
            â”‚ ---                 â”† ---  â”‚
            â”‚ datetime[Î¼s]        â”† i32  â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•¡
            â”‚ 1978-06-01 00:00:00 â”† 1978 â”‚
            â”‚ 2024-12-13 00:00:00 â”† 2024 â”‚
            â”‚ 2065-01-01 00:00:00 â”† 2065 â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_year(df_pa)
            pyarrow.Table
            datetime: timestamp[us]
            year: int64
            ----
            datetime: [[1978-06-01 00:00:00.000000,2024-12-13 00:00:00.000000,2065-01-01 00:00:00.000000]]
            year: [[1978,2024,2065]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Úyearr-   s    €r    r.   z,ExprDateTimeNamespace.year.<locals>.<lambda>±  r  r"   rÇ  r1   s   `r    r  zExprDateTimeNamespace.yearp  ó    ø€ ð@ z‰z×#Ñ#ÛDó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uˆ	  Extract month from underlying DateTime representation.

        Returns the month number starting from 1. The return value ranges from 1 to 12.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "datetime": [
            ...         datetime(1978, 6, 1),
            ...         datetime(2024, 12, 13),
            ...         datetime(2065, 1, 1),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_month(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("datetime").dt.month().alias("month"),
            ...     ).to_native()

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

            >>> agnostic_dt_month(df_pd)
                datetime  month
            0 1978-06-01      6
            1 2024-12-13     12
            2 2065-01-01      1

            >>> agnostic_dt_month(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ datetime            â”† month â”‚
            â”‚ ---                 â”† ---   â”‚
            â”‚ datetime[Î¼s]        â”† i8    â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•¡
            â”‚ 1978-06-01 00:00:00 â”† 6     â”‚
            â”‚ 2024-12-13 00:00:00 â”† 12    â”‚
            â”‚ 2065-01-01 00:00:00 â”† 1     â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_month(df_pa)
            pyarrow.Table
            datetime: timestamp[us]
            month: int64
            ----
            datetime: [[1978-06-01 00:00:00.000000,2024-12-13 00:00:00.000000,2065-01-01 00:00:00.000000]]
            month: [[6,12,1]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Úmonthr-   s    €r    r.   z-ExprDateTimeNamespace.month.<locals>.<lambda>õ  s#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×CÑCÔEr"   rÇ  r1   s   `r    r  zExprDateTimeNamespace.month´  s    ø€ ð@ z‰z×#Ñ#ÛEó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uy	  Extract day from underlying DateTime representation.

        Returns the day of month starting from 1. The return value ranges from 1 to 31. (The last day of month differs by months.)

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "datetime": [
            ...         datetime(1978, 6, 1),
            ...         datetime(2024, 12, 13),
            ...         datetime(2065, 1, 1),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_day(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("datetime").dt.day().alias("day"),
            ...     ).to_native()

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

            >>> agnostic_dt_day(df_pd)
                datetime  day
            0 1978-06-01    1
            1 2024-12-13   13
            2 2065-01-01    1

            >>> agnostic_dt_day(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
            â”‚ datetime            â”† day â”‚
            â”‚ ---                 â”† --- â”‚
            â”‚ datetime[Î¼s]        â”† i8  â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•¡
            â”‚ 1978-06-01 00:00:00 â”† 1   â”‚
            â”‚ 2024-12-13 00:00:00 â”† 13  â”‚
            â”‚ 2065-01-01 00:00:00 â”† 1   â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_day(df_pa)
            pyarrow.Table
            datetime: timestamp[us]
            day: int64
            ----
            datetime: [[1978-06-01 00:00:00.000000,2024-12-13 00:00:00.000000,2065-01-01 00:00:00.000000]]
            day: [[1,13,1]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Údayr-   s    €r    r.   z+ExprDateTimeNamespace.day.<locals>.<lambda>9  s#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×AÑAÔCr"   rÇ  r1   s   `r    r  zExprDateTimeNamespace.dayø  s    ø€ ð@ z‰z×#Ñ#ÛCó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )un	  Extract hour from underlying DateTime representation.

        Returns the hour number from 0 to 23.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "datetime": [
            ...         datetime(1978, 1, 1, 1),
            ...         datetime(2024, 10, 13, 5),
            ...         datetime(2065, 1, 1, 10),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_hour(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("datetime").dt.hour().alias("hour")
            ...     ).to_native()

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

            >>> agnostic_dt_hour(df_pd)
                         datetime  hour
            0 1978-01-01 01:00:00     1
            1 2024-10-13 05:00:00     5
            2 2065-01-01 10:00:00    10

            >>> agnostic_dt_hour(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”
            â”‚ datetime            â”† hour â”‚
            â”‚ ---                 â”† ---  â”‚
            â”‚ datetime[Î¼s]        â”† i8   â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•¡
            â”‚ 1978-01-01 01:00:00 â”† 1    â”‚
            â”‚ 2024-10-13 05:00:00 â”† 5    â”‚
            â”‚ 2065-01-01 10:00:00 â”† 10   â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_hour(df_pa)
            pyarrow.Table
            datetime: timestamp[us]
            hour: int64
            ----
            datetime: [[1978-01-01 01:00:00.000000,2024-10-13 05:00:00.000000,2065-01-01 10:00:00.000000]]
            hour: [[1,5,10]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Úhourr-   s    €r    r.   z,ExprDateTimeNamespace.hour.<locals>.<lambda>}  r  r"   rÇ  r1   s   `r    r  zExprDateTimeNamespace.hour<  r  r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )u¸	  Extract minutes from underlying DateTime representation.

        Returns the minute number from 0 to 59.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "datetime": [
            ...         datetime(1978, 1, 1, 1, 1),
            ...         datetime(2024, 10, 13, 5, 30),
            ...         datetime(2065, 1, 1, 10, 20),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_minute(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("datetime").dt.minute().alias("minute"),
            ...     ).to_native()

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

            >>> agnostic_dt_minute(df_pd)
                         datetime  minute
            0 1978-01-01 01:01:00       1
            1 2024-10-13 05:30:00      30
            2 2065-01-01 10:20:00      20

            >>> agnostic_dt_minute(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ datetime            â”† minute â”‚
            â”‚ ---                 â”† ---    â”‚
            â”‚ datetime[Î¼s]        â”† i8     â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•¡
            â”‚ 1978-01-01 01:01:00 â”† 1      â”‚
            â”‚ 2024-10-13 05:30:00 â”† 30     â”‚
            â”‚ 2065-01-01 10:20:00 â”† 20     â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_minute(df_pa)
            pyarrow.Table
            datetime: timestamp[us]
            minute: int64
            ----
            datetime: [[1978-01-01 01:01:00.000000,2024-10-13 05:30:00.000000,2065-01-01 10:20:00.000000]]
            minute: [[1,30,20]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Úminuter-   s    €r    r.   z.ExprDateTimeNamespace.minute.<locals>.<lambda>Á  ó#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×DÑDÔFr"   rÇ  r1   s   `r    r  zExprDateTimeNamespace.minute€  s    ø€ ð@ z‰z×#Ñ#ÛFó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )u’	  Extract seconds from underlying DateTime representation.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "datetime": [
            ...         datetime(1978, 1, 1, 1, 1, 1),
            ...         datetime(2024, 10, 13, 5, 30, 14),
            ...         datetime(2065, 1, 1, 10, 20, 30),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_second(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("datetime").dt.second().alias("second"),
            ...     ).to_native()

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

            >>> agnostic_dt_second(df_pd)
                         datetime  second
            0 1978-01-01 01:01:01       1
            1 2024-10-13 05:30:14      14
            2 2065-01-01 10:20:30      30

            >>> agnostic_dt_second(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ datetime            â”† second â”‚
            â”‚ ---                 â”† ---    â”‚
            â”‚ datetime[Î¼s]        â”† i8     â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•¡
            â”‚ 1978-01-01 01:01:01 â”† 1      â”‚
            â”‚ 2024-10-13 05:30:14 â”† 14     â”‚
            â”‚ 2065-01-01 10:20:30 â”† 30     â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_second(df_pa)
            pyarrow.Table
            datetime: timestamp[us]
            second: int64
            ----
            datetime: [[1978-01-01 01:01:01.000000,2024-10-13 05:30:14.000000,2065-01-01 10:20:30.000000]]
            second: [[1,14,30]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Úsecondr-   s    €r    r.   z.ExprDateTimeNamespace.second.<locals>.<lambda>  r  r"   rÇ  r1   s   `r    r!  zExprDateTimeNamespace.secondÄ  s    ø€ ð| z‰z×#Ñ#ÛFó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )u‚
  Extract milliseconds from underlying DateTime representation.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "datetime": [
            ...         datetime(1978, 1, 1, 1, 1, 1, 0),
            ...         datetime(2024, 10, 13, 5, 30, 14, 505000),
            ...         datetime(2065, 1, 1, 10, 20, 30, 67000),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_millisecond(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("datetime").dt.millisecond().alias("millisecond"),
            ...     ).to_native()

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

            >>> agnostic_dt_millisecond(df_pd)
                             datetime  millisecond
            0 1978-01-01 01:01:01.000            0
            1 2024-10-13 05:30:14.505          505
            2 2065-01-01 10:20:30.067           67

            >>> agnostic_dt_millisecond(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ datetime                â”† millisecond â”‚
            â”‚ ---                     â”† ---         â”‚
            â”‚ datetime[Î¼s]            â”† i32         â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 1978-01-01 01:01:01     â”† 0           â”‚
            â”‚ 2024-10-13 05:30:14.505 â”† 505         â”‚
            â”‚ 2065-01-01 10:20:30.067 â”† 67          â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_millisecond(df_pa)
            pyarrow.Table
            datetime: timestamp[us]
            millisecond: int64
            ----
            datetime: [[1978-01-01 01:01:01.000000,2024-10-13 05:30:14.505000,2065-01-01 10:20:30.067000]]
            millisecond: [[0,505,67]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Úmillisecondr-   s    €r    r.   z3ExprDateTimeNamespace.millisecond.<locals>.<lambda>E  ó#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×IÑIÔKr"   rÇ  r1   s   `r    r$  z!ExprDateTimeNamespace.millisecond  ó    ø€ ð| z‰z×#Ñ#ÛKó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uˆ
  Extract microseconds from underlying DateTime representation.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "datetime": [
            ...         datetime(1978, 1, 1, 1, 1, 1, 0),
            ...         datetime(2024, 10, 13, 5, 30, 14, 505000),
            ...         datetime(2065, 1, 1, 10, 20, 30, 67000),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_microsecond(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("datetime").dt.microsecond().alias("microsecond"),
            ...     ).to_native()

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

            >>> agnostic_dt_microsecond(df_pd)
                             datetime  microsecond
            0 1978-01-01 01:01:01.000            0
            1 2024-10-13 05:30:14.505       505000
            2 2065-01-01 10:20:30.067        67000

            >>> agnostic_dt_microsecond(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ datetime                â”† microsecond â”‚
            â”‚ ---                     â”† ---         â”‚
            â”‚ datetime[Î¼s]            â”† i32         â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 1978-01-01 01:01:01     â”† 0           â”‚
            â”‚ 2024-10-13 05:30:14.505 â”† 505000      â”‚
            â”‚ 2065-01-01 10:20:30.067 â”† 67000       â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_microsecond(df_pa)
            pyarrow.Table
            datetime: timestamp[us]
            microsecond: int64
            ----
            datetime: [[1978-01-01 01:01:01.000000,2024-10-13 05:30:14.505000,2065-01-01 10:20:30.067000]]
            microsecond: [[0,505000,67000]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Úmicrosecondr-   s    €r    r.   z3ExprDateTimeNamespace.microsecond.<locals>.<lambda>‡  r%  r"   rÇ  r1   s   `r    r)  z!ExprDateTimeNamespace.microsecondH  r&  r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uq
  Extract Nanoseconds from underlying DateTime representation.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "datetime": [
            ...         datetime(1978, 1, 1, 1, 1, 1, 0),
            ...         datetime(2024, 10, 13, 5, 30, 14, 500000),
            ...         datetime(2065, 1, 1, 10, 20, 30, 60000),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_nanosecond(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("datetime").dt.nanosecond().alias("nanosecond"),
            ...     ).to_native()

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

            >>> agnostic_dt_nanosecond(df_pd)
                             datetime  nanosecond
            0 1978-01-01 01:01:01.000           0
            1 2024-10-13 05:30:14.500   500000000
            2 2065-01-01 10:20:30.060    60000000

            >>> agnostic_dt_nanosecond(df_pl)
            shape: (3, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ datetime                â”† nanosecond â”‚
            â”‚ ---                     â”† ---        â”‚
            â”‚ datetime[Î¼s]            â”† i32        â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 1978-01-01 01:01:01     â”† 0          â”‚
            â”‚ 2024-10-13 05:30:14.500 â”† 500000000  â”‚
            â”‚ 2065-01-01 10:20:30.060 â”† 60000000   â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_nanosecond(df_pa)
            pyarrow.Table
            datetime: timestamp[us]
            nanosecond: int64
            ----
            datetime: [[1978-01-01 01:01:01.000000,2024-10-13 05:30:14.500000,2065-01-01 10:20:30.060000]]
            nanosecond: [[0,500000000,60000000]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Ú
nanosecondr-   s    €r    r.   z2ExprDateTimeNamespace.nanosecond.<locals>.<lambda>É  s#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×HÑHÔJr"   rÇ  r1   s   `r    r,  z ExprDateTimeNamespace.nanosecondŠ  s    ø€ ð| z‰z×#Ñ#ÛJó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uœ  Get ordinal day.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [datetime(2020, 1, 1), datetime(2020, 8, 3)]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_ordinal_day(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         a_ordinal_day=nw.col("a").dt.ordinal_day()
            ...     ).to_native()

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

            >>> agnostic_dt_ordinal_day(df_pd)
                       a  a_ordinal_day
            0 2020-01-01              1
            1 2020-08-03            216

            >>> agnostic_dt_ordinal_day(df_pl)
            shape: (2, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a                   â”† a_ordinal_day â”‚
            â”‚ ---                 â”† ---           â”‚
            â”‚ datetime[Î¼s]        â”† i16           â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2020-01-01 00:00:00 â”† 1             â”‚
            â”‚ 2020-08-03 00:00:00 â”† 216           â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_ordinal_day(df_pa)
            pyarrow.Table
            a: timestamp[us]
            a_ordinal_day: int64
            ----
            a: [[2020-01-01 00:00:00.000000,2020-08-03 00:00:00.000000]]
            a_ordinal_day: [[1,216]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Úordinal_dayr-   s    €r    r.   z3ExprDateTimeNamespace.ordinal_day.<locals>.<lambda>  r%  r"   rÇ  r1   s   `r    r/  z!ExprDateTimeNamespace.ordinal_dayÌ  s    ø€ ðl z‰z×#Ñ#ÛKó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )uc  Extract the week day from the underlying Date representation.

        Returns:
            Returns the ISO weekday number where monday = 1 and sunday = 7

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [datetime(2020, 1, 1), datetime(2020, 8, 3)]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_weekday(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(a_weekday=nw.col("a").dt.weekday()).to_native()

            We can then pass either pandas, Polars, PyArrow, and other supported libraries to
            `agnostic_dt_weekday`:

            >>> agnostic_dt_weekday(df_pd)
                       a  a_weekday
            0 2020-01-01          3
            1 2020-08-03          1

            >>> agnostic_dt_weekday(df_pl)
            shape: (2, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a                   â”† a_weekday â”‚
            â”‚ ---                 â”† ---       â”‚
            â”‚ datetime[Î¼s]        â”† i8        â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2020-01-01 00:00:00 â”† 3         â”‚
            â”‚ 2020-08-03 00:00:00 â”† 1         â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_weekday(df_pa)
            pyarrow.Table
            a: timestamp[us]
            a_weekday: int64
            ----
            a: [[2020-01-01 00:00:00.000000,2020-08-03 00:00:00.000000]]
            a_weekday: [[3,1]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Úweekdayr-   s    €r    r.   z/ExprDateTimeNamespace.weekday.<locals>.<lambda>;  s#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×EÑEÔGr"   rÇ  r1   s   `r    r2  zExprDateTimeNamespace.weekday  s    ø€ ðh z‰z×#Ñ#ÛGó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )ug	  Get total minutes.

        Returns:
            A new expression.

        Notes:
            The function outputs the total minutes in the int dtype by default,
            however, pandas may change the dtype to float when there are missing values,
            consider using `fill_null()` and `cast` in this case.

        Examples:
            >>> from datetime import timedelta
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [timedelta(minutes=10), timedelta(minutes=20, seconds=40)]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_total_minutes(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         a_total_minutes=nw.col("a").dt.total_minutes()
            ...     ).to_native()

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

            >>> agnostic_dt_total_minutes(df_pd)
                            a  a_total_minutes
            0 0 days 00:10:00               10
            1 0 days 00:20:40               20

            >>> agnostic_dt_total_minutes(df_pl)
            shape: (2, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a            â”† a_total_minutes â”‚
            â”‚ ---          â”† ---             â”‚
            â”‚ duration[Î¼s] â”† i64             â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 10m          â”† 10              â”‚
            â”‚ 20m 40s      â”† 20              â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_total_minutes(df_pa)
            pyarrow.Table
            a: duration[us]
            a_total_minutes: int64
            ----
            a: [[600000000,1240000000]]
            a_total_minutes: [[10,20]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Útotal_minutesr-   s    €r    r.   z5ExprDateTimeNamespace.total_minutes.<locals>.<lambda>z  ó#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×KÑKÔMr"   rÇ  r1   s   `r    r5  z#ExprDateTimeNamespace.total_minutes>  ó    ø€ ðv z‰z×#Ñ#ÛMó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )u~	  Get total seconds.

        Returns:
            A new expression.

        Notes:
            The function outputs the total seconds in the int dtype by default,
            however, pandas may change the dtype to float when there are missing values,
            consider using `fill_null()` and `cast` in this case.

        Examples:
            >>> from datetime import timedelta
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [timedelta(seconds=10), timedelta(seconds=20, milliseconds=40)]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_total_seconds(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         a_total_seconds=nw.col("a").dt.total_seconds()
            ...     ).to_native()

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

            >>> agnostic_dt_total_seconds(df_pd)
                                   a  a_total_seconds
            0        0 days 00:00:10               10
            1 0 days 00:00:20.040000               20

            >>> agnostic_dt_total_seconds(df_pl)
            shape: (2, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a            â”† a_total_seconds â”‚
            â”‚ ---          â”† ---             â”‚
            â”‚ duration[Î¼s] â”† i64             â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 10s          â”† 10              â”‚
            â”‚ 20s 40ms     â”† 20              â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_total_seconds(df_pa)
            pyarrow.Table
            a: duration[us]
            a_total_seconds: int64
            ----
            a: [[10000000,20040000]]
            a_total_seconds: [[10,20]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Útotal_secondsr-   s    €r    r.   z5ExprDateTimeNamespace.total_seconds.<locals>.<lambda>¹  r6  r"   rÇ  r1   s   `r    r:  z#ExprDateTimeNamespace.total_seconds}  r7  r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )u|
  Get total milliseconds.

        Returns:
            A new expression.

        Notes:
            The function outputs the total milliseconds in the int dtype by default,
            however, pandas may change the dtype to float when there are missing values,
            consider using `fill_null()` and `cast` in this case.

        Examples:
            >>> from datetime import timedelta
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": [
            ...         timedelta(milliseconds=10),
            ...         timedelta(milliseconds=20, microseconds=40),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_total_milliseconds(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         a_total_milliseconds=nw.col("a").dt.total_milliseconds()
            ...     ).to_native()

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

            >>> agnostic_dt_total_milliseconds(df_pd)
                                   a  a_total_milliseconds
            0 0 days 00:00:00.010000                    10
            1 0 days 00:00:00.020040                    20

            >>> agnostic_dt_total_milliseconds(df_pl)
            shape: (2, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a            â”† a_total_milliseconds â”‚
            â”‚ ---          â”† ---                  â”‚
            â”‚ duration[Î¼s] â”† i64                  â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 10ms         â”† 10                   â”‚
            â”‚ 20040Âµs      â”† 20                   â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_total_milliseconds(df_pa)
            pyarrow.Table
            a: duration[us]
            a_total_milliseconds: int64
            ----
            a: [[10000,20040]]
            a_total_milliseconds: [[10,20]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Útotal_millisecondsr-   s    €r    r.   z:ExprDateTimeNamespace.total_milliseconds.<locals>.<lambda>ý  ó#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×PÑPÔRr"   rÇ  r1   s   `r    r=  z(ExprDateTimeNamespace.total_milliseconds¼  ó    ø€ ð@ z‰z×#Ñ#ÛRó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )u{
  Get total microseconds.

        Returns:
            A new expression.

        Notes:
            The function outputs the total microseconds in the int dtype by default,
            however, pandas may change the dtype to float when there are missing values,
            consider using `fill_null()` and `cast` in this case.

        Examples:
            >>> from datetime import timedelta
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": [
            ...         timedelta(microseconds=10),
            ...         timedelta(milliseconds=1, microseconds=200),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_total_microseconds(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         a_total_microseconds=nw.col("a").dt.total_microseconds()
            ...     ).to_native()

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

            >>> agnostic_dt_total_microseconds(df_pd)
                                   a  a_total_microseconds
            0 0 days 00:00:00.000010                    10
            1 0 days 00:00:00.001200                  1200

            >>> agnostic_dt_total_microseconds(df_pl)
            shape: (2, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a            â”† a_total_microseconds â”‚
            â”‚ ---          â”† ---                  â”‚
            â”‚ duration[Î¼s] â”† i64                  â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 10Âµs         â”† 10                   â”‚
            â”‚ 1200Âµs       â”† 1200                 â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_total_microseconds(df_pa)
            pyarrow.Table
            a: duration[us]
            a_total_microseconds: int64
            ----
            a: [[10,1200]]
            a_total_microseconds: [[10,1200]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Útotal_microsecondsr-   s    €r    r.   z:ExprDateTimeNamespace.total_microseconds.<locals>.<lambda>A  r>  r"   rÇ  r1   s   `r    rB  z(ExprDateTimeNamespace.total_microseconds   r?  r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )u‹
  Get total nanoseconds.

        Returns:
            A new expression.

        Notes:
            The function outputs the total nanoseconds in the int dtype by default,
            however, pandas may change the dtype to float when there are missing values,
            consider using `fill_null()` and `cast` in this case.

        Examples:
            >>> from datetime import timedelta
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = ["2024-01-01 00:00:00.000000001", "2024-01-01 00:00:00.000000002"]
            >>> df_pd = pd.DataFrame({"a": pd.to_datetime(data)})
            >>> df_pl = pl.DataFrame({"a": data}).with_columns(
            ...     pl.col("a").str.to_datetime(time_unit="ns")
            ... )

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_total_nanoseconds(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         a_diff_total_nanoseconds=nw.col("a").diff().dt.total_nanoseconds()
            ...     ).to_native()

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

            >>> agnostic_dt_total_nanoseconds(df_pd)
                                          a  a_diff_total_nanoseconds
            0 2024-01-01 00:00:00.000000001                       NaN
            1 2024-01-01 00:00:00.000000002                       1.0

            >>> agnostic_dt_total_nanoseconds(df_pl)
            shape: (2, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a                             â”† a_diff_total_nanoseconds â”‚
            â”‚ ---                           â”† ---                      â”‚
            â”‚ datetime[ns]                  â”† i64                      â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2024-01-01 00:00:00.000000001 â”† null                     â”‚
            â”‚ 2024-01-01 00:00:00.000000002 â”† 1                        â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   rŸ  Útotal_nanosecondsr-   s    €r    r.   z9ExprDateTimeNamespace.total_nanoseconds.<locals>.<lambda>x  s#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×OÑOÔQr"   rÇ  r1   s   `r    rE  z'ExprDateTimeNamespace.total_nanosecondsD  s    ø€ ðf z‰z×#Ñ#ÛQó
ð 	
r"   c                óD   ‡ ‡— ‰ j                   j                  ˆˆ fd„«      S )uY  Convert a Date/Time/Datetime column into a String column with the given format.

        Arguments:
            format: Format to format temporal column with.

        Returns:
            A new expression.

        Notes:
            Unfortunately, different libraries interpret format directives a bit
            differently.

            - Chrono, the library used by Polars, uses `"%.f"` for fractional seconds,
              whereas pandas and Python stdlib use `".%f"`.
            - PyArrow interprets `"%S"` as "seconds, including fractional seconds"
              whereas most other tools interpret it as "just seconds, as 2 digits".

            Therefore, we make the following adjustments:

            - for pandas-like libraries, we replace `"%S.%f"` with `"%S%.f"`.
            - for PyArrow, we replace `"%S.%f"` with `"%S"`.

            Workarounds like these don't make us happy, and we try to avoid them as
            much as possible, but here we feel like it's the best compromise.

            If you just want to format a date/datetime Series as a local datetime
            string, and have it work as consistently as possible across libraries,
            we suggest using:

            - `"%Y-%m-%dT%H:%M:%S%.f"` for datetimes
            - `"%Y-%m-%d"` for dates

            though note that, even then, different tools may return a different number
            of trailing zeros. Nonetheless, this is probably consistent enough for
            most applications.

            If you have an application where this is not enough, please open an issue
            and let us know.

        Examples:
            >>> from datetime import datetime
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": [
            ...         datetime(2020, 3, 1),
            ...         datetime(2020, 4, 1),
            ...         datetime(2020, 5, 1),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_dt_to_string(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a").dt.to_string("%Y/%m/%d %H:%M:%S")
            ...     ).to_native()

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

            >>> agnostic_dt_to_string(df_pd)
                                 a
            0  2020/03/01 00:00:00
            1  2020/04/01 00:00:00
            2  2020/05/01 00:00:00

            >>> agnostic_dt_to_string(df_pl)
            shape: (3, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a                   â”‚
            â”‚ ---                 â”‚
            â”‚ str                 â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2020/03/01 00:00:00 â”‚
            â”‚ 2020/04/01 00:00:00 â”‚
            â”‚ 2020/05/01 00:00:00 â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_to_string(df_pa)
            pyarrow.Table
            a: string
            ----
            a: [["2020/03/01 00:00:00.000000","2020/04/01 00:00:00.000000","2020/05/01 00:00:00.000000"]]
        c                ól   •— ‰j                   j                  | «      j                  j                  ‰«      S r%   )rÀ  r   rŸ  Ú	to_stringrû  s    €€r    r.   z1ExprDateTimeNamespace.to_string.<locals>.<lambda>Ú  s%   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×GÑGÈÔOr"   rÇ  rü  s   ``r    rH  zExprDateTimeNamespace.to_string{  s    ù€ ð| z‰z×#Ñ#ÜOó
ð 	
r"   c                óD   ‡ ‡— ‰ j                   j                  ˆ ˆfd„«      S )u	  Replace time zone.

        Arguments:
            time_zone: Target time zone.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime, timezone
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": [
            ...         datetime(2024, 1, 1, tzinfo=timezone.utc),
            ...         datetime(2024, 1, 2, tzinfo=timezone.utc),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_dt_replace_time_zone(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a").dt.replace_time_zone("Asia/Kathmandu")
            ...     ).to_native()

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

            >>> agnostic_dt_replace_time_zone(df_pd)
                                      a
            0 2024-01-01 00:00:00+05:45
            1 2024-01-02 00:00:00+05:45

            >>> agnostic_dt_replace_time_zone(df_pl)
            shape: (2, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a                            â”‚
            â”‚ ---                          â”‚
            â”‚ datetime[Î¼s, Asia/Kathmandu] â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2024-01-01 00:00:00 +0545    â”‚
            â”‚ 2024-01-02 00:00:00 +0545    â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_replace_time_zone(df_pa)
            pyarrow.Table
            a: timestamp[us, tz=Asia/Kathmandu]
            ----
            a: [[2023-12-31 18:15:00.000000Z,2024-01-01 18:15:00.000000Z]]
        c                ól   •— ‰j                   j                  | «      j                  j                  ‰«      S r%   )rÀ  r   rŸ  Úreplace_time_zone©r   r&   Ú	time_zones    €€r    r.   z9ExprDateTimeNamespace.replace_time_zone.<locals>.<lambda>  ó&   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×OÑOÐPYÔZr"   rÇ  )r&   rM  s   ``r    rK  z'ExprDateTimeNamespace.replace_time_zoneÝ  s    ù€ ðx z‰z×#Ñ#ÜZó
ð 	
r"   c                ób   ‡ ‡— ‰€d}t        |«      ‚‰ j                  j                  ˆ ˆfd„«      S )u|	  Convert to a new time zone.

        If converting from a time-zone-naive column, then conversion happens
        as if converting from UTC.

        Arguments:
            time_zone: Target time zone.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import datetime, timezone
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": [
            ...         datetime(2024, 1, 1, tzinfo=timezone.utc),
            ...         datetime(2024, 1, 2, tzinfo=timezone.utc),
            ...     ]
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_dt_convert_time_zone(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(
            ...         nw.col("a").dt.convert_time_zone("Asia/Kathmandu")
            ...     ).to_native()

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

            >>> agnostic_dt_convert_time_zone(df_pd)
                                      a
            0 2024-01-01 05:45:00+05:45
            1 2024-01-02 05:45:00+05:45

            >>> agnostic_dt_convert_time_zone(df_pl)
            shape: (2, 1)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a                            â”‚
            â”‚ ---                          â”‚
            â”‚ datetime[Î¼s, Asia/Kathmandu] â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2024-01-01 05:45:00 +0545    â”‚
            â”‚ 2024-01-02 05:45:00 +0545    â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_convert_time_zone(df_pa)
            pyarrow.Table
            a: timestamp[us, tz=Asia/Kathmandu]
            ----
            a: [[2024-01-01 00:00:00.000000Z,2024-01-02 00:00:00.000000Z]]
        z…Target `time_zone` cannot be `None` in `convert_time_zone`. Please use `replace_time_zone(None)` if you want to remove the time zone.c                ól   •— ‰j                   j                  | «      j                  j                  ‰«      S r%   )rÀ  r   rŸ  Úconvert_time_zonerL  s    €€r    r.   z9ExprDateTimeNamespace.convert_time_zone.<locals>.<lambda>`  rN  r"   )r  rÀ  r0   )r&   rM  r  s   `` r    rQ  z'ExprDateTimeNamespace.convert_time_zone  s8   ù€ ð~ Ðð ZˆCÜ˜C“.Ð Øz‰z×#Ñ#ÜZó
ð 	
r"   c                ón   ‡ ‡— ‰dvrd‰›d}t        |«      ‚‰ j                  j                  ˆ ˆfd„«      S )uô
  Return a timestamp in the given time unit.

        Arguments:
            time_unit: {'ns', 'us', 'ms'}
                Time unit.

        Returns:
            A new expression.

        Examples:
            >>> from datetime import date
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"date": [date(2001, 1, 1), None, date(2001, 1, 3)]}
            >>> df_pd = pd.DataFrame(data, dtype="datetime64[ns]")
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_dt_timestamp(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(
            ...         nw.col("date").dt.timestamp().alias("timestamp_us"),
            ...         nw.col("date").dt.timestamp("ms").alias("timestamp_ms"),
            ...     ).to_native()

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

            >>> agnostic_dt_timestamp(df_pd)
                    date  timestamp_us  timestamp_ms
            0 2001-01-01  9.783072e+14  9.783072e+11
            1        NaT           NaN           NaN
            2 2001-01-03  9.784800e+14  9.784800e+11

            >>> agnostic_dt_timestamp(df_pl)
            shape: (3, 3)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ date       â”† timestamp_us    â”† timestamp_ms â”‚
            â”‚ ---        â”† ---             â”† ---          â”‚
            â”‚ date       â”† i64             â”† i64          â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
            â”‚ 2001-01-01 â”† 978307200000000 â”† 978307200000 â”‚
            â”‚ null       â”† null            â”† null         â”‚
            â”‚ 2001-01-03 â”† 978480000000000 â”† 978480000000 â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_dt_timestamp(df_pa)
            pyarrow.Table
            date: date32[day]
            timestamp_us: int64
            timestamp_ms: int64
            ----
            date: [[2001-01-01,null,2001-01-03]]
            timestamp_us: [[978307200000000,null,978480000000000]]
            timestamp_ms: [[978307200000,null,978480000000]]
        >   ÚmsÚnsÚusz=invalid `time_unit`

Expected one of {'ns', 'us', 'ms'}, got Ú.c                ól   •— ‰j                   j                  | «      j                  j                  ‰«      S r%   )rÀ  r   rŸ  Ú	timestamp)r   r&   Ú	time_units    €€r    r.   z1ExprDateTimeNamespace.timestamp.<locals>.<lambda>©  s%   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×=Ñ=×GÑGÈ	ÔRr"   )r0  rÀ  r0   )r&   rY  r  s   `` r    rX  zExprDateTimeNamespace.timestampc  sM   ù€ ð~ Ð.Ñ.ðAØAJÀÈQðPð ô ˜S“/Ð!Øz‰z×#Ñ#ÜRó
ð 	
r"   NrÈ  rÉ  )r&   r   rù  r  rW   r»  )r&   r   rM  r  rW   r»  )r&   r   rM  r  rW   r»  )rU  )r&   r   rY  zLiteral['ns', 'us', 'ms']rW   r»  )r·  r¸  r¹  r(   r  r  r  r  r  r  r!  r$  r)  r,  r/  r2  r5  r:  r=  rB  rE  rH  rK  rQ  rX  r8   r"   r    rž  rž  3  s’   „ óó7
órB
óHB
óHB
óHB
óHB
óH@
óD@
óD@
óD@
óD8
ót6
óp=
ó~=
ó~B
óHB
óH5
ón`
óD>
ó@D
õLG
r"   rž  c                  óD   — e Zd Zd	d„Zd
d„Zdd„Zdd„Zdd„Zd
d„Zd
d„Z	y)r¤  c                ó   — || _         y r%   r¿  rÁ  s     r    r(   zExprNameNamespace.__init__®  rÃ  r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )aa  Keep the original root name of the expression.

        Returns:
            A new expression.

        Notes:
            This will undo any previous renaming operations on the expression.
            Due to implementation constraints, this method can only be called as the last
            expression in a chain. Only one name operation per expression will work.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> data = {"foo": [1, 2], "BAR": [4, 5]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_name_keep(df_native: IntoFrame) -> list[str]:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("foo").alias("alias_for_foo").name.keep()).columns

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

            >>> agnostic_name_keep(df_pd)
            ['foo']

            >>> agnostic_name_keep(df_pl)
            ['foo']

            >>> agnostic_name_keep(df_pa)
            ['foo']
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   r6   Úkeepr-   s    €r    r.   z(ExprNameNamespace.keep.<locals>.<lambda>Û  s#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×?Ñ?×DÑDÔFr"   rÇ  r1   s   `r    r^  zExprNameNamespace.keep±  s    ø€ ðR z‰z×#Ñ#ÛFó
ð 	
r"   c                óD   ‡ ‡— ‰ j                   j                  ˆˆ fd„«      S )a,  Rename the output of an expression by mapping a function over the root name.

        Arguments:
            function: Function that maps a root name to a new name.

        Returns:
            A new expression.

        Notes:
            This will undo any previous renaming operations on the expression.
            Due to implementation constraints, this method can only be called as the last
            expression in a chain. Only one name operation per expression will work.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> data = {"foo": [1, 2], "BAR": [4, 5]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> renaming_func = lambda s: s[::-1]  # reverse column name
            >>> def agnostic_name_map(df_native: IntoFrame) -> list[str]:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("foo", "BAR").name.map(renaming_func)).columns

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

            >>> agnostic_name_map(df_pd)
            ['oof', 'RAB']

            >>> agnostic_name_map(df_pl)
            ['oof', 'RAB']

            >>> agnostic_name_map(df_pa)
            ['oof', 'RAB']
        c                ól   •— ‰j                   j                  | «      j                  j                  ‰«      S r%   )rÀ  r   r6   Úmap)r   r9   r&   s    €€r    r.   z'ExprNameNamespace.map.<locals>.<lambda>  s%   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×?Ñ?×CÑCÀHÔMr"   rÇ  )r&   r9   s   ``r    ra  zExprNameNamespace.mapÞ  s    ù€ ðZ z‰z×#Ñ#ÜMó
ð 	
r"   c                óD   ‡ ‡— ‰ j                   j                  ˆˆ fd„«      S )aU  Add a prefix to the root column name of the expression.

        Arguments:
            prefix: Prefix to add to the root column name.

        Returns:
            A new expression.

        Notes:
            This will undo any previous renaming operations on the expression.
            Due to implementation constraints, this method can only be called as the last
            expression in a chain. Only one name operation per expression will work.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> data = {"foo": [1, 2], "BAR": [4, 5]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_name_prefix(df_native: IntoFrame, prefix: str) -> list[str]:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("foo", "BAR").name.prefix(prefix)).columns

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

            >>> agnostic_name_prefix(df_pd, "with_prefix_")
            ['with_prefix_foo', 'with_prefix_BAR']

            >>> agnostic_name_prefix(df_pl, "with_prefix_")
            ['with_prefix_foo', 'with_prefix_BAR']

            >>> agnostic_name_prefix(df_pa, "with_prefix_")
            ['with_prefix_foo', 'with_prefix_BAR']
        c                ól   •— ‰j                   j                  | «      j                  j                  ‰«      S r%   )rÀ  r   r6   râ  rá  s    €€r    r.   z*ExprNameNamespace.prefix.<locals>.<lambda><  ó%   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×?Ñ?×FÑFÀvÔNr"   rÇ  rã  s   ``r    râ  zExprNameNamespace.prefix  ó    ù€ ðX z‰z×#Ñ#ÜNó
ð 	
r"   c                óD   ‡ ‡— ‰ j                   j                  ˆ ˆfd„«      S )aU  Add a suffix to the root column name of the expression.

        Arguments:
            suffix: Suffix to add to the root column name.

        Returns:
            A new expression.

        Notes:
            This will undo any previous renaming operations on the expression.
            Due to implementation constraints, this method can only be called as the last
            expression in a chain. Only one name operation per expression will work.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> data = {"foo": [1, 2], "BAR": [4, 5]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_name_suffix(df_native: IntoFrame, suffix: str) -> list[str]:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("foo", "BAR").name.suffix(suffix)).columns

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

            >>> agnostic_name_suffix(df_pd, "_with_suffix")
            ['foo_with_suffix', 'BAR_with_suffix']

            >>> agnostic_name_suffix(df_pl, "_with_suffix")
            ['foo_with_suffix', 'BAR_with_suffix']

            >>> agnostic_name_suffix(df_pa, "_with_suffix")
            ['foo_with_suffix', 'BAR_with_suffix']
        c                ól   •— ‰j                   j                  | «      j                  j                  ‰«      S r%   )rÀ  r   r6   rè  rç  s    €€r    r.   z*ExprNameNamespace.suffix.<locals>.<lambda>l  rd  r"   rÇ  ré  s   ``r    rè  zExprNameNamespace.suffix?  re  r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )aŒ  Make the root column name lowercase.

        Returns:
            A new expression.

        Notes:
            This will undo any previous renaming operations on the expression.
            Due to implementation constraints, this method can only be called as the last
            expression in a chain. Only one name operation per expression will work.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> data = {"foo": [1, 2], "BAR": [4, 5]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_name_to_lowercase(df_native: IntoFrame) -> list[str]:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("foo", "BAR").name.to_lowercase()).columns

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

            >>> agnostic_name_to_lowercase(df_pd)
            ['foo', 'bar']

            >>> agnostic_name_to_lowercase(df_pl)
            ['foo', 'bar']

            >>> agnostic_name_to_lowercase(df_pa)
            ['foo', 'bar']
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   r6   r  r-   s    €r    r.   z0ExprNameNamespace.to_lowercase.<locals>.<lambda>™  ó#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×?Ñ?×LÑLÔNr"   rÇ  r1   s   `r    r  zExprNameNamespace.to_lowercaseo  ó    ø€ ðR z‰z×#Ñ#ÛNó
ð 	
r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )aŒ  Make the root column name uppercase.

        Returns:
            A new expression.

        Notes:
            This will undo any previous renaming operations on the expression.
            Due to implementation constraints, this method can only be called as the last
            expression in a chain. Only one name operation per expression will work.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> data = {"foo": [1, 2], "BAR": [4, 5]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a dataframe-agnostic function:

            >>> def agnostic_name_to_uppercase(df_native: IntoFrame) -> list[str]:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("foo", "BAR").name.to_uppercase()).columns

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

            >>> agnostic_name_to_uppercase(df_pd)
            ['FOO', 'BAR']

            >>> agnostic_name_to_uppercase(df_pl)
            ['FOO', 'BAR']

            >>> agnostic_name_to_uppercase(df_pa)
            ['FOO', 'BAR']
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   r6   rÿ  r-   s    €r    r.   z0ExprNameNamespace.to_uppercase.<locals>.<lambda>Æ  rj  r"   rÇ  r1   s   `r    rÿ  zExprNameNamespace.to_uppercaseœ  rk  r"   NrÈ  rÉ  )r&   r   r9   zCallable[[str], str]rW   r»  r  r  )
r·  r¸  r¹  r(   r^  ra  râ  rè  r  rÿ  r8   r"   r    r¤  r¤  ­  s*   „ óó+
óZ/
ób.
ó`.
ó`+
ôZ+
r"   r¤  c                  ó   — e Zd Zdd„Zdd„Zy)r§  c                ó   — || _         y r%   r¿  rÁ  s     r    r(   zExprListNamespace.__init__Ë  rÃ  r"   c                ó@   ‡ — ‰ j                   j                  ˆ fd„«      S )u   Return the number of elements in each list.

        Null values count towards the total.

        Returns:
            A new expression.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [[1, 2], [3, 4, None], None, []]}

            Let's define a dataframe-agnostic function:

            >>> def agnostic_list_len(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns(a_len=nw.col("a").list.len()).to_native()

            We can then pass pandas / PyArrow / Polars / any other supported library:

            >>> agnostic_list_len(
            ...     pd.DataFrame(data).astype({"a": pd.ArrowDtype(pa.list_(pa.int64()))})
            ... )  # doctest: +SKIP
                           a  a_len
            0        [1. 2.]      2
            1  [ 3.  4. nan]      3
            2           <NA>   <NA>
            3             []      0

            >>> agnostic_list_len(pl.DataFrame(data))
            shape: (4, 2)
            â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”
            â”‚ a            â”† a_len â”‚
            â”‚ ---          â”† ---   â”‚
            â”‚ list[i64]    â”† u32   â”‚
            â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•¡
            â”‚ [1, 2]       â”† 2     â”‚
            â”‚ [3, 4, null] â”† 3     â”‚
            â”‚ null         â”† null  â”‚
            â”‚ []           â”† 0     â”‚
            â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”˜

            >>> agnostic_list_len(pa.table(data))
            pyarrow.Table
            a: list<item: int64>
              child 0, item: int64
            a_len: uint32
            ----
            a: [[[1,2],[3,4,null],null,[]]]
            a_len: [[2,3,null,0]]
        c                ój   •— ‰j                   j                  | «      j                  j                  «       S r%   )rÀ  r   r  r`  r-   s    €r    r.   z'ExprListNamespace.len.<locals>.<lambda>  s#   ø€ ˜Ÿ
™
×5Ñ5°cÓ:×?Ñ?×CÑCÔEr"   rÇ  r1   s   `r    r`  zExprListNamespace.lenÎ  s    ø€ ðp z‰z×#Ñ#ÛEó
ð 	
r"   NrÈ  rÉ  )r·  r¸  r¹  r(   r`  r8   r"   r    r§  r§  Ê  s   „ óô:
r"   r§  c                 ó&   ‡ — dˆ fd„}t        |«      S )u!  Creates an expression that references one or more columns by their name(s).

    Arguments:
        names: Name(s) of the columns to use.

    Returns:
        A new expression.

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

        We define a dataframe-agnostic function:

        >>> def agnostic_col(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.col("a") * nw.col("b")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_col`:

        >>> agnostic_col(df_pd)
           a
        0  3
        1  8

        >>> agnostic_col(df_pl)
        shape: (2, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ a   â”‚
        â”‚ --- â”‚
        â”‚ i64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 3   â”‚
        â”‚ 8   â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_col(df_pa)
        pyarrow.Table
        a: int64
        ----
        a: [[3,8]]
    c                ó4   •—  | j                   t        ‰«      Ž S r%   )Úcolr   )r   Únamess    €r    rT   zcol.<locals>.func@  s   ø€ Øˆsw‰wœ ›Ð'Ð'r"   ©r   r   rW   r   ©r   )ru  rT   s   ` r    rt  rt    s   ø€ õj(ô ‹:Ðr"   c                 ó&   ‡ — dˆ fd„}t        |«      S )u   Creates an expression that references one or more columns by their index(es).

    Notes:
        `nth` is not supported for Polars version<1.0.0. Please use
        [`narwhals.col`][] instead.

    Arguments:
        indices: One or more indices representing the columns to retrieve.

    Returns:
        A new expression.

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

        We define a dataframe-agnostic function:

        >>> def agnostic_nth(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.nth(0) * 2).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to `agnostic_nth`:

        >>> agnostic_nth(df_pd)
           a
        0  2
        1  4

        >>> agnostic_nth(df_pl)
        shape: (2, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ a   â”‚
        â”‚ --- â”‚
        â”‚ i64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 2   â”‚
        â”‚ 4   â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_nth(df_pa)
        pyarrow.Table
        a: int64
        ----
        a: [[2,4]]
    c                ó4   •—  | j                   t        ‰«      Ž S r%   )Únthr   )r   Úindicess    €r    rT   znth.<locals>.func~  s   ø€ Øˆsw‰wœ Ó(Ð)Ð)r"   rv  rw  )r{  rT   s   ` r    rz  rz  F  s   ø€ õp*ô ‹:Ðr"   c                 ó   — t        d„ «      S )uˆ  Instantiate an expression representing all columns.

    Returns:
        A new expression.

    Examples:
        >>> import polars as pl
        >>> import pandas as pd
        >>> import pyarrow as pa
        >>> 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)
        >>> df_pa = pa.table(data)

        Let's define a dataframe-agnostic function:

        >>> def agnostic_all(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.all() * 2).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_all`:

        >>> agnostic_all(df_pd)
           a   b
        0  2   8
        1  4  10
        2  6  12

        >>> agnostic_all(df_pl)
        shape: (3, 2)
        â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”
        â”‚ a   â”† b   â”‚
        â”‚ --- â”† --- â”‚
        â”‚ i64 â”† i64 â”‚
        â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•¡
        â”‚ 2   â”† 8   â”‚
        â”‚ 4   â”† 10  â”‚
        â”‚ 6   â”† 12  â”‚
        â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”˜

        >>> agnostic_all(df_pa)
        pyarrow.Table
        a: int64
        b: int64
        ----
        a: [[2,4,6]]
        b: [[8,10,12]]
    c                ó"   — | j                  «       S r%   )r¢   ©r   s    r    r.   zall_.<locals>.<lambda>º  s
   € ˜CŸG™GœIr"   rw  r8   r"   r    Úall_r  …  s   € ôj Ñ%Ó&Ð&r"   c                 ó    — dd„} t        | «      S )uˆ  Return the number of rows.

    Returns:
        A new expression.

    Examples:
        >>> import polars as pl
        >>> import pandas as pd
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {"a": [1, 2], "b": [5, 10]}
        >>> df_pd = pd.DataFrame(data)
        >>> df_pl = pl.DataFrame(data)
        >>> df_pa = pa.table(data)

        Let's define a dataframe-agnostic function:

        >>> def agnostic_len(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.len()).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_len`:

        >>> agnostic_len(df_pd)
           len
        0    2
        >>> agnostic_len(df_pl)
        shape: (1, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ len â”‚
        â”‚ --- â”‚
        â”‚ u32 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 2   â”‚
        â””â”€â”€â”€â”€â”€â”˜
        >>> agnostic_len(df_pa)
        pyarrow.Table
        len: int64
        ----
        len: [[2]]
    c                ó"   — | j                  «       S r%   )r`  r~  s    r    rT   zlen_.<locals>.funcì  s   € Øw‰w‹yÐr"   rv  rw  )rT   s    r    Úlen_r‚  ¾  s   € ó\ô ‹:Ðr"   c                 ó    ‡ — t        ˆ fd„«      S )u  Sum all values.

    Note:
        Syntactic sugar for ``nw.col(columns).sum()``

    Arguments:
        columns: Name(s) of the columns to use in the aggregation function

    Returns:
        A new expression.

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

        We define a dataframe-agnostic function:

        >>> def agnostic_sum(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.sum("a")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_sum`:

        >>> agnostic_sum(df_pd)
           a
        0  3

        >>> agnostic_sum(df_pl)
        shape: (1, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ a   â”‚
        â”‚ --- â”‚
        â”‚ i64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 3   â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_sum(df_pa)
        pyarrow.Table
        a: int64
        ----
        a: [[3]]
    c                ó>   •—  | j                   ‰Ž j                  «       S r%   )rt  r,   ©r   Úcolumnss    €r    r.   zsum.<locals>.<lambda>'  ó   ø€ ˜G˜CŸG™G WÐ-×1Ñ1Ô3r"   rw  ©r†  s   `r    r,   r,   ò  ó   ø€ ôj Ó3Ó4Ð4r"   c                 ó    ‡ — t        ˆ fd„«      S )u  Get the mean value.

    Note:
        Syntactic sugar for ``nw.col(columns).mean()``

    Arguments:
        columns: Name(s) of the columns to use in the aggregation function

    Returns:
        A new expression.

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

        We define a dataframe agnostic function:

        >>> def agnostic_mean(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.mean("a")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_mean`:

        >>> agnostic_mean(df_pd)
             a
        0  4.0

        >>> agnostic_mean(df_pl)
        shape: (1, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ a   â”‚
        â”‚ --- â”‚
        â”‚ f64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 4.0 â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_mean(df_pa)
        pyarrow.Table
        a: double
        ----
        a: [[4]]
    c                ó>   •—  | j                   ‰Ž j                  «       S r%   )rt  r±   r…  s    €r    r.   zmean.<locals>.<lambda>_  s   ø€ ˜G˜CŸG™G WÐ-×2Ñ2Ô4r"   rw  rˆ  s   `r    r±   r±   *  s   ø€ ôj Ó4Ó5Ð5r"   c                 ó    ‡ — t        ˆ fd„«      S )uÃ  Get the median value.

    Notes:
        - Syntactic sugar for ``nw.col(columns).median()``
        - Results might slightly differ across backends due to differences in the
            underlying algorithms used to compute the median.

    Arguments:
        columns: Name(s) of the columns to use in the aggregation function

    Returns:
        A new expression.

    Examples:
        >>> import pandas as pd
        >>> import polars as pl
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {"a": [4, 5, 2]}
        >>> df_pd = pd.DataFrame(data)
        >>> df_pl = pl.DataFrame(data)
        >>> df_pa = pa.table(data)

        Let's define a dataframe agnostic function:

        >>> def agnostic_median(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.median("a")).to_native()

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

        >>> agnostic_median(df_pd)
             a
        0  4.0

        >>> agnostic_median(df_pl)
        shape: (1, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ a   â”‚
        â”‚ --- â”‚
        â”‚ f64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 4.0 â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_median(df_pa)
        pyarrow.Table
        a: double
        ----
        a: [[4]]
    c                ó>   •—  | j                   ‰Ž j                  «       S r%   )rt  r¶   r…  s    €r    r.   zmedian.<locals>.<lambda>™  s   ø€ ˜G˜CŸG™G WÐ-×4Ñ4Ô6r"   rw  rˆ  s   `r    r¶   r¶   b  s   ø€ ôn Ó6Ó7Ð7r"   c                 ó    ‡ — t        ˆ fd„«      S )u!  Return the minimum value.

    Note:
       Syntactic sugar for ``nw.col(columns).min()``.

    Arguments:
        columns: Name(s) of the columns to use in the aggregation function.

    Returns:
        A new expression.

    Examples:
        >>> import polars as pl
        >>> import pandas as pd
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {"a": [1, 2], "b": [5, 10]}
        >>> df_pd = pd.DataFrame(data)
        >>> df_pl = pl.DataFrame(data)
        >>> df_pa = pa.table(data)

        Let's define a dataframe-agnostic function:

        >>> def agnostic_min(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.min("b")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_min`:

        >>> agnostic_min(df_pd)
           b
        0  5

        >>> agnostic_min(df_pl)
        shape: (1, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ b   â”‚
        â”‚ --- â”‚
        â”‚ i64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 5   â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_min(df_pa)
        pyarrow.Table
        b: int64
        ----
        b: [[5]]
    c                ó>   •—  | j                   ‰Ž j                  «       S r%   )rt  rÏ   r…  s    €r    r.   zmin.<locals>.<lambda>Ñ  r‡  r"   rw  rˆ  s   `r    rÏ   rÏ   œ  r‰  r"   c                 ó    ‡ — t        ˆ fd„«      S )u!  Return the maximum value.

    Note:
       Syntactic sugar for ``nw.col(columns).max()``.

    Arguments:
        columns: Name(s) of the columns to use in the aggregation function.

    Returns:
        A new expression.

    Examples:
        >>> import polars as pl
        >>> import pandas as pd
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {"a": [1, 2], "b": [5, 10]}
        >>> df_pd = pd.DataFrame(data)
        >>> df_pl = pl.DataFrame(data)
        >>> df_pa = pa.table(data)

        Let's define a dataframe-agnostic function:

        >>> def agnostic_max(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.max("a")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_max`:

        >>> agnostic_max(df_pd)
           a
        0  2

        >>> agnostic_max(df_pl)
        shape: (1, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ a   â”‚
        â”‚ --- â”‚
        â”‚ i64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 2   â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_max(df_pa)
        pyarrow.Table
        a: int64
        ----
        a: [[2]]
    c                ó>   •—  | j                   ‰Ž j                  «       S r%   )rt  rÒ   r…  s    €r    r.   zmax.<locals>.<lambda>	  r‡  r"   rw  rˆ  s   `r    rÒ   rÒ   Ô  r‰  r"   c                 ó>   ‡ — ‰ sd}t        |«      ‚t        ˆ fd„«      S )u  Sum all values horizontally across columns.

    Warning:
        Unlike Polars, we support horizontal sum over numeric columns only.

    Arguments:
        exprs: Name(s) of the columns to use in the aggregation function. Accepts
            expression input.

    Returns:
        A new expression.

    Examples:
        >>> import pandas as pd
        >>> import polars as pl
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {"a": [1, 2, 3], "b": [5, 10, None]}
        >>> df_pl = pl.DataFrame(data)
        >>> df_pd = pd.DataFrame(data)
        >>> df_pa = pa.table(data)

        We define a dataframe-agnostic function:

        >>> def agnostic_sum_horizontal(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.sum_horizontal("a", "b")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to `agnostic_sum_horizontal`:

        >>> agnostic_sum_horizontal(df_pd)
              a
        0   6.0
        1  12.0
        2   3.0

        >>> agnostic_sum_horizontal(df_pl)
        shape: (3, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ a   â”‚
        â”‚ --- â”‚
        â”‚ i64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 6   â”‚
        â”‚ 12  â”‚
        â”‚ 3   â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_sum_horizontal(df_pa)
        pyarrow.Table
        a: int64
        ----
        a: [[6,12,3]]
    z:At least one expression must be passed to `sum_horizontal`c           	     ón   •—  | j                   t        ‰«      D cg c]  }t        | |«      ‘Œ c}Ž S c c}w r%   )Úsum_horizontalr   r!   ©r   ÚvÚexprss     €r    r.   z sum_horizontal.<locals>.<lambda>I  ó4   ø€ Ð&C×&Ñ&Ü18¸´Ó@±¨AÔ  QÕ'°Ñ@ñ
ùÚ@ó   ›2©r0  r   ©r—  r  s   ` r    r”  r”    s*   ø€ ñr ØJˆÜ˜‹oÐÜó	
óð r"   c                 ó>   ‡ — ‰ sd}t        |«      ‚t        ˆ fd„«      S )uI  Get the minimum value horizontally across columns.

    Notes:
        We support `min_horizontal` over numeric columns only.

    Arguments:
        exprs: Name(s) of the columns to use in the aggregation function. Accepts
            expression input.

    Returns:
        A new expression.

    Examples:
        >>> import pandas as pd
        >>> import polars as pl
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {
        ...     "a": [1, 8, 3],
        ...     "b": [4, 5, None],
        ...     "c": ["x", "y", "z"],
        ... }

        We define a dataframe-agnostic function that computes the horizontal min of "a"
        and "b" columns:

        >>> def agnostic_min_horizontal(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.min_horizontal("a", "b")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_min_horizontal`:

        >>> agnostic_min_horizontal(pd.DataFrame(data))
             a
        0  1.0
        1  5.0
        2  3.0

        >>> agnostic_min_horizontal(pl.DataFrame(data))
        shape: (3, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ a   â”‚
        â”‚ --- â”‚
        â”‚ i64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 1   â”‚
        â”‚ 5   â”‚
        â”‚ 3   â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_min_horizontal(pa.table(data))
        pyarrow.Table
        a: int64
        ----
        a: [[1,5,3]]
    z:At least one expression must be passed to `min_horizontal`c           	     ón   •—  | j                   t        ‰«      D cg c]  }t        | |«      ‘Œ c}Ž S c c}w r%   )Úmin_horizontalr   r!   r•  s     €r    r.   z min_horizontal.<locals>.<lambda>  r˜  r™  rš  r›  s   ` r    rž  rž  O  ó*   ø€ ñx ØJˆÜ˜‹oÐÜó	
óð r"   c                 ó>   ‡ — ‰ sd}t        |«      ‚t        ˆ fd„«      S )uI  Get the maximum value horizontally across columns.

    Notes:
        We support `max_horizontal` over numeric columns only.

    Arguments:
        exprs: Name(s) of the columns to use in the aggregation function. Accepts
            expression input.

    Returns:
        A new expression.

    Examples:
        >>> import pandas as pd
        >>> import polars as pl
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {
        ...     "a": [1, 8, 3],
        ...     "b": [4, 5, None],
        ...     "c": ["x", "y", "z"],
        ... }

        We define a dataframe-agnostic function that computes the horizontal max of "a"
        and "b" columns:

        >>> def agnostic_max_horizontal(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.max_horizontal("a", "b")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_max_horizontal`:

        >>> agnostic_max_horizontal(pd.DataFrame(data))
             a
        0  4.0
        1  8.0
        2  3.0

        >>> agnostic_max_horizontal(pl.DataFrame(data))
        shape: (3, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ a   â”‚
        â”‚ --- â”‚
        â”‚ i64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 4   â”‚
        â”‚ 8   â”‚
        â”‚ 3   â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_max_horizontal(pa.table(data))
        pyarrow.Table
        a: int64
        ----
        a: [[4,8,3]]
    z:At least one expression must be passed to `max_horizontal`c           	     ón   •—  | j                   t        ‰«      D cg c]  }t        | |«      ‘Œ c}Ž S c c}w r%   )Úmax_horizontalr   r!   r•  s     €r    r.   z max_horizontal.<locals>.<lambda>Õ  r˜  r™  rš  r›  s   ` r    r¢  r¢  •  rŸ  r"   c                  ó$   — e Zd Zdd„Zdd„Zdd„Zy)ÚWhenc                ó&   — t        |g«      | _        y r%   )r   Ú_predicatesr  s     r    r(   zWhen.__init__Ü  s   € Ü" J <Ó0ˆÕr"   c                óT   — | j                   D cg c]  }t        ||«      ‘Œ c}S c c}w r%   )r¦  r!   )r&   r   r–  s      r    Ú_extract_predicateszWhen._extract_predicatesß  s*   € Ø37×3CÒ3CÓDÑ3C¨aÔ! # qÕ)Ð3CÑDÐDùÒDs   %c                ó$   ‡ ‡— t        ˆ ˆfd„«      S )Nc                ór   •—  | j                   ‰j                  | «      Ž j                  t        | ‰«      «      S r%   )Úwhenr¨  Úthenr!   ©r   r&   r+  s    €€r    r.   zWhen.then.<locals>.<lambda>ä  s0   ø€ ˜˜Ÿ™ $×":Ñ":¸3Ó"?Ð@×EÑEÜ! # uÓ-ôr"   )ÚThen©r&   r+  s   ``r    r¬  z	When.thenâ  ó   ù€ Üôó
ð 	
r"   N)r  úIntoExpr | Iterable[IntoExpr]rW   r¨  rv  )r+  r   rW   r®  )r·  r¸  r¹  r(   r¨  r¬  r8   r"   r    r¤  r¤  Û  s   „ ó1óEô
r"   r¤  c                  ó   — e Zd Zdd„Zy)r®  c                ó$   ‡ ‡— t        ˆ ˆfd„«      S )Nc                óX   •— ‰j                  | «      j                  t        | ‰«      «      S r%   )r   Ú	otherwiser!   r­  s    €€r    r.   z Then.otherwise.<locals>.<lambda>í  s%   ø€ ˜×/Ñ/°Ó4×>Ñ>Ü! # uÓ-ôr"   rw  r¯  s   ``r    rµ  zThen.otherwiseë  r°  r"   N)r+  r   rW   r   )r·  r¸  r¹  rµ  r8   r"   r    r®  r®  ê  s   „ ô
r"   r®  c                 ó   — t        | Ž S )u°
  Start a `when-then-otherwise` expression.

    Expression similar to an `if-else` statement in Python. Always initiated by a
    `pl.when(<condition>).then(<value if condition>)`, and optionally followed by
    chaining one or more `.when(<condition>).then(<value>)` statements.
    Chained when-then operations should be read as Python `if, elif, ... elif`
    blocks, not as `if, if, ... if`, i.e. the first condition that evaluates to
    `True` will be picked.
    If none of the conditions are `True`, an optional
    `.otherwise(<value if all statements are false>)` can be appended at the end.
    If not appended, and none of the conditions are `True`, `None` will be returned.

    Arguments:
        predicates: Condition(s) that must be met in order to apply the subsequent
            statement. Accepts one or more boolean expressions, which are implicitly
            combined with `&`. String input is parsed as a column name.

    Returns:
        A "when" object, which `.then` can be called on.

    Examples:
        >>> import pandas as pd
        >>> import polars as pl
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {"a": [1, 2, 3], "b": [5, 10, 15]}
        >>> df_pl = pl.DataFrame(data)
        >>> df_pd = pd.DataFrame(data)
        >>> df_pa = pa.table(data)

        We define a dataframe-agnostic function:

        >>> def agnostic_when_then_otherwise(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.with_columns(
        ...         nw.when(nw.col("a") < 3).then(5).otherwise(6).alias("a_when")
        ...     ).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_when_then_otherwise`:

        >>> agnostic_when_then_otherwise(df_pd)
           a   b  a_when
        0  1   5       5
        1  2  10       5
        2  3  15       6

        >>> agnostic_when_then_otherwise(df_pl)
        shape: (3, 3)
        â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”
        â”‚ a   â”† b   â”† a_when â”‚
        â”‚ --- â”† --- â”† ---    â”‚
        â”‚ i64 â”† i64 â”† i32    â”‚
        â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•¡
        â”‚ 1   â”† 5   â”† 5      â”‚
        â”‚ 2   â”† 10  â”† 5      â”‚
        â”‚ 3   â”† 15  â”† 6      â”‚
        â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”˜

        >>> agnostic_when_then_otherwise(df_pa)
        pyarrow.Table
        a: int64
        b: int64
        a_when: int64
        ----
        a: [[1,2,3]]
        b: [[5,10,15]]
        a_when: [[5,5,6]]
    )r¤  )r  s    r    r«  r«  ó  s   € ôP ÐÐr"   c                 ó>   ‡ — ‰ sd}t        |«      ‚t        ˆ fd„«      S )u2	  Compute the bitwise AND horizontally across columns.

    Arguments:
        exprs: Name(s) of the columns to use in the aggregation function. Accepts
            expression input.

    Returns:
        A new expression.

    Examples:
        >>> import pandas as pd
        >>> import polars as pl
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {
        ...     "a": [False, False, True, True, False, None],
        ...     "b": [False, True, True, None, None, None],
        ... }
        >>> df_pl = pl.DataFrame(data)
        >>> df_pd = pd.DataFrame(data).convert_dtypes(dtype_backend="pyarrow")
        >>> df_pa = pa.table(data)

        We define a dataframe-agnostic function:

        >>> def agnostic_all_horizontal(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select("a", "b", all=nw.all_horizontal("a", "b")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_all_horizontal`:

        >>> agnostic_all_horizontal(df_pd)
               a      b    all
        0  False  False  False
        1  False   True  False
        2   True   True   True
        3   True   <NA>   <NA>
        4  False   <NA>  False
        5   <NA>   <NA>   <NA>

        >>> agnostic_all_horizontal(df_pl)
        shape: (6, 3)
        â”Œâ”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”
        â”‚ a     â”† b     â”† all   â”‚
        â”‚ ---   â”† ---   â”† ---   â”‚
        â”‚ bool  â”† bool  â”† bool  â”‚
        â•žâ•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•¡
        â”‚ false â”† false â”† false â”‚
        â”‚ false â”† true  â”† false â”‚
        â”‚ true  â”† true  â”† true  â”‚
        â”‚ true  â”† null  â”† null  â”‚
        â”‚ false â”† null  â”† false â”‚
        â”‚ null  â”† null  â”† null  â”‚
        â””â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”˜

        >>> agnostic_all_horizontal(df_pa)
        pyarrow.Table
        a: bool
        b: bool
        all: bool
        ----
        a: [[false,false,true,true,false,null]]
        b: [[false,true,true,null,null,null]]
        all: [[false,false,true,null,false,null]]
    z:At least one expression must be passed to `all_horizontal`c           	     ón   •—  | j                   t        ‰«      D cg c]  }t        | |«      ‘Œ c}Ž S c c}w r%   )Úall_horizontalr   r!   r•  s     €r    r.   z all_horizontal.<locals>.<lambda>†  r˜  r™  rš  r›  s   ` r    r¹  r¹  >  ó*   ø€ ñH ØJˆÜ˜‹oÐÜó	
óð r"   Nc                ó    ‡ ‡— t        ‰ «      rd}t        |«      ‚t        ‰ t        t        f«      rd‰ › }t        |«      ‚t        ˆˆ fd„«      S )u@  Return an expression representing a literal value.

    Arguments:
        value: The value to use as literal.
        dtype: The data type of the literal value. If not provided, the data type will
            be inferred.

    Returns:
        A new expression.

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

        We define a dataframe-agnostic function:

        >>> def agnostic_lit(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.with_columns(nw.lit(3)).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_lit`:

        >>> agnostic_lit(df_pd)
           a  literal
        0  1        3
        1  2        3

        >>> agnostic_lit(df_pl)
        shape: (2, 2)
        â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
        â”‚ a   â”† literal â”‚
        â”‚ --- â”† ---     â”‚
        â”‚ i64 â”† i32     â”‚
        â•žâ•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•¡
        â”‚ 1   â”† 3       â”‚
        â”‚ 2   â”† 3       â”‚
        â””â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

        >>> agnostic_lit(df_pa)
        pyarrow.Table
        a: int64
        literal: int64
        ----
        a: [[1,2]]
        literal: [[3,3]]
    zvnumpy arrays are not supported as literal values. Consider using `with_columns` to create a new column from the array.z,Nested datatypes are not supported yet. Got c                ó(   •— | j                  ‰‰«      S r%   )rS   )r   r@   r+  s    €€r    r.   zlit.<locals>.<lambda>Ï  s   ø€ ˜CŸG™G E¨5Ô1r"   )r   r0  r   r  Útupler  r   )r+  r@   r  s   `` r    rS   rS   Œ  sV   ù€ ôp eÔðSð 	ô ˜‹oÐä%œ$¤˜Ô'Ø<¸U¸GÐDˆÜ! #Ó&Ð&äÔ1Ó2Ð2r"   c                 ó>   ‡ — ‰ sd}t        |«      ‚t        ˆ fd„«      S )u/	  Compute the bitwise OR horizontally across columns.

    Arguments:
        exprs: Name(s) of the columns to use in the aggregation function. Accepts
            expression input.

    Returns:
        A new expression.

    Examples:
        >>> import pandas as pd
        >>> import polars as pl
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {
        ...     "a": [False, False, True, True, False, None],
        ...     "b": [False, True, True, None, None, None],
        ... }
        >>> df_pl = pl.DataFrame(data)
        >>> df_pd = pd.DataFrame(data).convert_dtypes(dtype_backend="pyarrow")
        >>> df_pa = pa.table(data)

        We define a dataframe-agnostic function:

        >>> def agnostic_any_horizontal(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select("a", "b", any=nw.any_horizontal("a", "b")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_any_horizontal`:

        >>> agnostic_any_horizontal(df_pd)
               a      b    any
        0  False  False  False
        1  False   True   True
        2   True   True   True
        3   True   <NA>   True
        4  False   <NA>   <NA>
        5   <NA>   <NA>   <NA>

        >>> agnostic_any_horizontal(df_pl)
        shape: (6, 3)
        â”Œâ”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”
        â”‚ a     â”† b     â”† any   â”‚
        â”‚ ---   â”† ---   â”† ---   â”‚
        â”‚ bool  â”† bool  â”† bool  â”‚
        â•žâ•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•¡
        â”‚ false â”† false â”† false â”‚
        â”‚ false â”† true  â”† true  â”‚
        â”‚ true  â”† true  â”† true  â”‚
        â”‚ true  â”† null  â”† true  â”‚
        â”‚ false â”† null  â”† null  â”‚
        â”‚ null  â”† null  â”† null  â”‚
        â””â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”˜

        >>> agnostic_any_horizontal(df_pa)
        pyarrow.Table
        a: bool
        b: bool
        any: bool
        ----
        a: [[false,false,true,true,false,null]]
        b: [[false,true,true,null,null,null]]
        any: [[false,true,true,true,null,null]]
    z:At least one expression must be passed to `any_horizontal`c           	     ón   •—  | j                   t        ‰«      D cg c]  }t        | |«      ‘Œ c}Ž S c c}w r%   )Úany_horizontalr   r!   r•  s     €r    r.   z any_horizontal.<locals>.<lambda>  r˜  r™  rš  r›  s   ` r    rÀ  rÀ  Ò  rº  r"   c                 ó>   ‡ — ‰ sd}t        |«      ‚t        ˆ fd„«      S )ua  Compute the mean of all values horizontally across columns.

    Arguments:
        exprs: Name(s) of the columns to use in the aggregation function. Accepts
            expression input.

    Returns:
        A new expression.

    Examples:
        >>> import pandas as pd
        >>> import polars as pl
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {
        ...     "a": [1, 8, 3],
        ...     "b": [4, 5, None],
        ...     "c": ["x", "y", "z"],
        ... }
        >>> df_pl = pl.DataFrame(data)
        >>> df_pd = pd.DataFrame(data)
        >>> df_pa = pa.table(data)

        We define a dataframe-agnostic function that computes the horizontal mean of "a"
        and "b" columns:

        >>> def agnostic_mean_horizontal(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(nw.mean_horizontal("a", "b")).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow to
        `agnostic_mean_horizontal`:

        >>> agnostic_mean_horizontal(df_pd)
             a
        0  2.5
        1  6.5
        2  3.0

        >>> agnostic_mean_horizontal(df_pl)
        shape: (3, 1)
        â”Œâ”€â”€â”€â”€â”€â”
        â”‚ a   â”‚
        â”‚ --- â”‚
        â”‚ f64 â”‚
        â•žâ•â•â•â•â•â•¡
        â”‚ 2.5 â”‚
        â”‚ 6.5 â”‚
        â”‚ 3.0 â”‚
        â””â”€â”€â”€â”€â”€â”˜

        >>> agnostic_mean_horizontal(df_pa)
        pyarrow.Table
        a: double
        ----
        a: [[2.5,6.5,3]]
    z;At least one expression must be passed to `mean_horizontal`c           	     ón   •—  | j                   t        ‰«      D cg c]  }t        | |«      ‘Œ c}Ž S c c}w r%   )Úmean_horizontalr   r!   r•  s     €r    r.   z!mean_horizontal.<locals>.<lambda>`  s4   ø€ Ð'C×'Ñ'Ü18¸´Ó@±¨AÔ  QÕ'°Ñ@ñ
ùÚ@r™  rš  r›  s   ` r    rÃ  rÃ     s*   ø€ ñx ØKˆÜ˜‹oÐÜó	
óð r"   Ú F©Ú	separatorr«   c               ó,   ‡ ‡‡‡— t        ˆ ˆˆˆfd„«      S )uI
  Horizontally concatenate columns into a single string column.

    Arguments:
        exprs: Columns to concatenate into a single string column. Accepts expression
            input. Strings are parsed as column names, other non-expression inputs are
            parsed as literals. Non-`String` columns are cast to `String`.
        *more_exprs: Additional columns to concatenate into a single string column,
            specified as positional arguments.
        separator: String that will be used to separate the values of each column.
        ignore_nulls: Ignore null values (default is `False`).
            If set to `False`, null values will be propagated and if the row contains any
            null values, the output is null.

    Returns:
        A new expression.

    Examples:
        >>> import pandas as pd
        >>> import polars as pl
        >>> import pyarrow as pa
        >>> import narwhals as nw
        >>> from narwhals.typing import IntoFrameT
        >>>
        >>> data = {
        ...     "a": [1, 2, 3],
        ...     "b": ["dogs", "cats", None],
        ...     "c": ["play", "swim", "walk"],
        ... }

        We define a dataframe-agnostic function that computes the horizontal string
        concatenation of different columns

        >>> def agnostic_concat_str(df_native: IntoFrameT) -> IntoFrameT:
        ...     df = nw.from_native(df_native)
        ...     return df.select(
        ...         nw.concat_str(
        ...             [
        ...                 nw.col("a") * 2,
        ...                 nw.col("b"),
        ...                 nw.col("c"),
        ...             ],
        ...             separator=" ",
        ...         ).alias("full_sentence")
        ...     ).to_native()

        We can pass any supported library such as Pandas, Polars, or PyArrow
        to `agnostic_concat_str`:

        >>> agnostic_concat_str(pd.DataFrame(data))
          full_sentence
        0   2 dogs play
        1   4 cats swim
        2          None

        >>> agnostic_concat_str(pl.DataFrame(data))
        shape: (3, 1)
        â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
        â”‚ full_sentence â”‚
        â”‚ ---           â”‚
        â”‚ str           â”‚
        â•žâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¡
        â”‚ 2 dogs play   â”‚
        â”‚ 4 cats swim   â”‚
        â”‚ null          â”‚
        â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜

        >>> agnostic_concat_str(pa.table(data))
        pyarrow.Table
        full_sentence: string
        ----
        full_sentence: [["2 dogs play","4 cats swim",null]]
    c           
     óº   •—  | j                   t        ‰g«      D cg c]  }t        | |«      ‘Œ c}g‰D cg c]  }t        | |«      ‘Œ c}¢­‰‰dœŽS c c}w c c}w )NrÅ  )Ú
concat_strr   r!   )r   r–  r—  r«   Ú
more_exprsrÆ  s     €€€€r    r.   zconcat_str.<locals>.<lambda>µ  se   ø€ NC—N‘NÜ07¸¸Ô0@ÓAÑ0@¨1Ô˜s AÕ&Ð0@ÑAð
á1;Ó<±¨AÔ  QÕ'°Ñ<ñ
ð  Ø%ó	
ùÚAùÚ<s
   œA¶A
rw  )r—  rÆ  r«   rÊ  s   ````r    rÉ  rÉ  f  s   û€ ô\ ö	
óð r"   )r   z'CompliantNamespace[CompliantSeriesT_co]r   r   rW   z>CompliantExpr[CompliantSeriesT_co] | CompliantSeriesT_co | Any)ru  r³  rW   r   )r{  zint | Sequence[int]rW   r   r®  )r†  r  rW   r   )r—  r±  rW   r   )r  r±  rW   r¤  r%   )r+  r   r@   r¯  rW   r   )
r—  r±  rÊ  r   rÆ  r  r«   r¬  rW   r   )8Ú
__future__r   Útypingr   r   r   r   r   r	   r
   r   r   Únarwhals.dependenciesr   Únarwhals.dtypesr   Únarwhals.utilsr   r   Útyping_extensionsr   r   Únarwhals.typingr   r   r   r   r!   r   r»  r¡  rœ  rž  r¤  r§  rt  rz  r  r‚  r,   r±   r¶   rÏ   rÒ   r”  rž  r¢  r¤  r®  r«  r¹  rS   rÀ  rÃ  rÉ  Ú__all__r8   r"   r    Ú<module>rÓ     s¬  ðÝ "å  Ý Ý Ý Ý Ý Ý Ý Ý å 0Ý +Ý 6Ý "áÝ&å%Ý-Ý2Ý3Ý(ð	Ø	0ð	Ø9<ð	àCó	÷`A'ñ `A'ñFC 	˜tÔ$€ô3
w˜u‘~ô 3
ôll
˜' %™.ô l
ô^w
˜G E™Nô w
ôt+Z
˜ ™ô Z
ôz>
˜ ™ô >
óB8óv;ó~5'ór1óh55óp56óp78ót55óp55óp@óFCóLC÷L
ñ 
ô
ˆ4ô 
óHóVKô\C3óLKó\CðR Øñ	UØ(ðUàðUð ðUð ð	Uð
 
óUðr ðr"   