U
     )3gi ã                   @  sÒ  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 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rzd dlmZ d dlmZ d dlmZ d dlZ d dl!Z"d dl#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dd d!Z0ed"d#d!Z1G d$d% d%ee0 Z2G d&d' d'e2e1 Z3G d(d) d)e2e0 Z4dS )*é    )Úannotations)ÚTYPE_CHECKING)ÚAny)ÚCallable)ÚGeneric)ÚIterable)ÚIterator)ÚLiteral)ÚNoReturn)ÚSequence)ÚTypeVar)Úoverload)Ú
get_polars)Úis_numpy_array)ÚSchema©Ú	to_native)Úflatten)Úis_sequence_but_not_str)Úparse_version)ÚBytesIO)ÚPath)Ú
ModuleTypeN)ÚSelf©ÚGroupBy©ÚLazyGroupBy©ÚSeries)ÚIntoDataFrame)ÚIntoExpr)Ú	IntoFrameÚFrameTr"   )ÚboundÚ
DataFrameTr    c                   @  s  e Zd ZU ded< ded< ddddd	Zdd
ddZdddddZddddddZdddddZe	dd
ddZ
dd
ddZdddddddZdjd dd!d"d#Zdkdd%dd&d'd(Ze	d)d
d*d+Zd,d-dd.d/d0Zd,d-dd.d1d2Zd3dd4d5d6Zd7dd8d9d:Zd7dd8d;d<Zd=d>dd?d@dAZdldBdCdDd%dEd>ddFdGdHZdIddJdKdLZdCdCdMdNd dOd>ddPdQdRZdmd$d$dTdUdd%dVd%d%d ddWdXdYZdd
dZd[Zdndd7d7dd]d^d_Zd$d$d$d$d$d$d`daddbdbdbd%d%d%dcddd	dedfZdd%d%dbdbddgdhdiZd$S )oÚ	BaseFramer   Ú_compliant_frameú Literal[('full', 'interchange')]Ú_levelr   r   ©ÚselfÚreturnc                 C  s
   | j  ¡ S ©N)r'   Ú__native_namespace__©r+   © r0   ú6/tmp/pip-unpacked-wheel-hfsjijke/narwhals/dataframe.pyr.   0   s    zBaseFrame.__native_namespace__©r,   c                 C  s
   | j  ¡ S r-   )r'   Ú__narwhals_namespace__r/   r0   r0   r1   r3   3   s    z BaseFrame.__narwhals_namespace__)Údfr,   c                 C  s   | j || jdS )N©Úlevel)Ú	__class__r)   )r+   r4   r0   r0   r1   Ú_from_compliant_dataframe6   s    þz#BaseFrame._from_compliant_dataframe)ÚargsÚkwargsr,   c                   s4    fddt |D } fdd| ¡ D }||fS )zDProcess `args` and `kwargs`, extracting underlying objects as we go.c                   s   g | ]}   |¡qS r0   ©Ú_extract_compliant)Ú.0Úvr/   r0   r1   Ú
<listcomp>?   s     z2BaseFrame._flatten_and_extract.<locals>.<listcomp>c                   s   i | ]\}}|   |¡qS r0   r;   )r=   Úkr>   r/   r0   r1   Ú
<dictcomp>@   s      z2BaseFrame._flatten_and_extract.<locals>.<dictcomp>)r   Úitems)r+   r9   r:   r0   r/   r1   Ú_flatten_and_extract=   s    zBaseFrame._flatten_and_extract)Úargr,   c                 C  s   ddl m} ddlm} t|tr(|jS t||r8|jS t||rP| |  	¡ ¡S t
 d k	rdtt|krdt| d}t||S )Nr   )ÚExprr   ZpolarszExpected Narwhals object, got: z[.

Perhaps you:
- Forgot a `nw.from_native` somewhere?
- Used `pl.col` instead of `nw.col`?)Znarwhals.exprrE   Únarwhals.seriesr   Ú
isinstancer&   r'   Z_compliant_seriesZ_callr3   r   ÚstrÚtypeÚ	TypeError)r+   rD   rE   r   Úmsgr0   r0   r1   r<   C   s    


ÿzBaseFrame._extract_compliantr   c                 C  s   t | jj ¡ S r-   )r   r'   ÚschemarB   r/   r0   r0   r1   rL   W   s    zBaseFrame.schemac                 C  s   t | j ¡ }t|S r-   )Údictr'   Úcollect_schemar   )r+   Znative_schemar0   r0   r1   rN   [   s    zBaseFrame.collect_schemaúCallable[[Any], Self]©Úfunctionr9   r:   r,   c                 O  s   || f||S r-   r0   ©r+   rQ   r9   r:   r0   r0   r1   Úpipe`   s    zBaseFrame.pipeÚindexrH   ©Únamer,   c                 C  s   |   | j |¡¡S r-   )r8   r'   Úwith_row_index©r+   rV   r0   r0   r1   rW   c   s    
ÿzBaseFrame.with_row_indexNústr | list[str] | None©r+   Úsubsetr,   c                 C  s   |   | jj|d¡S )N©r[   )r8   r'   Ú
drop_nulls©r+   r[   r0   r0   r1   r]   h   s    ÿzBaseFrame.drop_nullsú	list[str]c                 C  s   | j jS r-   )r'   Úcolumnsr/   r0   r0   r1   r`   m   s    zBaseFrame.columnsúIntoExpr | Iterable[IntoExpr]r!   ©ÚexprsÚnamed_exprsr,   c                 O  s$   | j ||\}}|  | jj||¡S r-   )rC   r8   r'   Úwith_columns©r+   rc   rd   r0   r0   r1   re   q   s    ÿzBaseFrame.with_columnsc                 O  s$   | j ||\}}|  | jj||¡S r-   )rC   r8   r'   Úselectrf   r0   r0   r1   rg   y   s    ÿzBaseFrame.selectúdict[str, str]©Úmappingr,   c                 C  s   |   | j |¡¡S r-   )r8   r'   Úrename©r+   rj   r0   r0   r1   rk      s    zBaseFrame.renameÚint©Únr,   c                 C  s   |   | j |¡¡S r-   )r8   r'   Úhead©r+   ro   r0   r0   r1   rp      s    zBaseFrame.headc                 C  s   |   | j |¡¡S r-   )r8   r'   Útailrq   r0   r0   r1   rr      s    zBaseFrame.tailzIterable[str]Úbool©r`   Ústrictr,   c                G  s   |   | jj||d¡S )N©ru   )r8   r'   Údrop©r+   ru   r`   r0   r0   r1   rw      s    ÿzBaseFrame.dropÚanyF©ÚkeepÚmaintain_orderú)Literal[('any', 'first', 'last', 'none')]©r[   r{   r|   r,   c                C  s   |   | jj|||d¡S )N)r[   r{   r|   )r8   r'   Úunique©r+   r[   r{   r|   r0   r0   r1   r      s      ÿÿzBaseFrame.uniqueú*IntoExpr | Iterable[IntoExpr] | list[bool]©Ú
predicatesr,   c                 G  sP   t |dkr0t|d tr0tdd |d D s>| j| \}}|  | jj| ¡S )Né   r   c                 s  s   | ]}t |tV  qd S r-   )rG   rs   )r=   Úxr0   r0   r1   Ú	<genexpr>¢   s     z#BaseFrame.filter.<locals>.<genexpr>)ÚlenrG   ÚlistÚallrC   r8   r'   Úfilter)r+   r   Ú_r0   r0   r1   r      s    
ÿþý
ÿzBaseFrame.filter©Ú
descendingÚ
nulls_lastústr | Iterable[str]úbool | Sequence[bool]©ÚbyÚmore_byr   r   r,   c                G  s    |   | jj|f|||d¡S )Nr   )r8   r'   Úsort©r+   r   r   r   r   r0   r0   r1   r   ©   s    ÿÿ ÿÿzBaseFrame.sortÚinnerÚ_right©Úleft_onÚright_onÚsuffixú3Literal[('inner', 'left', 'cross', 'semi', 'anti')]©ÚotherÚonÚhowr   r   r   r,   c          	   	   C  sì   d}||kr&d| d| d}t ||dkrR|d k	sF|d k	sF|d k	rRd}t||dkr|d kr|d ksr|d krd| d}t||dkrº|d k	rº|d k	sŠ|d k	rºd	| d}t||d k	rÊ| }}|  | jj|  |¡||||d
¡S )N)r   ÚleftÚcrossZantiÚsemiz2Only the following join strategies are supported: ú	; found 'ú'.r¢   z>Can not pass `left_on`, `right_on` or `on` keys for cross joinzGEither (`left_on` and `right_on`) or `on` keys should be specified for Ú.zBIf `on` is specified, `left_on` and `right_on` should be None for )r    r   r   r   )ÚNotImplementedErrorÚ
ValueErrorr8   r'   Újoinr<   )	r+   r   r   r    r   r   r   Z_supported_joinsrK   r0   r0   r1   r©   ¶   sH    
ÿÿÿ ÿÿÿûÿzBaseFrame.joinc                 C  s   |   | j ¡ ¡S r-   )r8   r'   Úcloner/   r0   r0   r1   rª   ã   s    zBaseFrame.cloner   ©r+   ro   Úoffsetr,   c                 C  s   |   | jj||d¡S )N©ro   r¬   )r8   r'   Úgather_every©r+   ro   r¬   r0   r0   r1   r®   æ   s    ÿzBaseFrame.gather_everyÚbackward©r   r   r   Úby_leftÚby_rightr   Ústrategyú
str | Noneú+Literal[('backward', 'forward', 'nearest')]©	r   r   r   r   r²   r³   r   rŽ   r,   c                C  s  d}	||	kr&d|	 d| d}
t |
|d krJ|d ks>|d krJd}
t|
|d k	rn|d k	sb|d k	rnd}
t|
|d kr¢|d kr|d k	s|d k	r¢|d kr¢d}
t|
|d k	rÆ|d k	sº|d k	rÆd}
t|
|d k	rò|  | jj|  |¡|||||d	¡S |  | jj|  |¡||||||d
¡S )N)r°   ZforwardZnearestz-Only the following strategies are supported: r€   r¥   zCEither (`left_on` and `right_on`) or `on` keys should be specified.z>If `on` is specified, `left_on` and `right_on` should be None.zGCan not specify only `by_left` or `by_right`, you need to specify both.z>If `by` is specified, `by_left` and `by_right` should be None.)r   r²   r³   r   rŽ   )r   r   r²   r³   r   rŽ   )r§   rš   r8   r'   Ú	join_asofr<   )r+   r   r   r   r   r²   r³   r   rŽ   Z_supported_strategiesrK   r0   r0   r1   rž   ë   s^    ÿÿþþÿúÿ
ùÿzBaseFrame.join_asof©r+   r   rT   Úvariable_nameÚ
value_namer,   c                C  s   |   | jj||||d¡S )N©r   rT   rº   r»   )r8   r'   Úunpivot©r+   r   rT   rº   r»   r0   r0   r1   rœ   %  s    üÿzBaseFrame.unpivot)rT   )N)N)Nr   )r   )Ú__name__Ú
__module__Ú__qualname__Ú__annotations__r.   r3   r8   rC   r<   ÚpropertyrL   rN   rS   rW   r]   r`   re   rg   rk   rp   rr   rw   r   r   r   r©   rª   r®   rž   rœ   r0   r0   r0   r1   r&   ,   s`   

 þûû  üø-	ö":r&   c                      s(  e Zd ZdZeddddZeddddZd	d
ddddZd	dddZdòd	dddddZ	ddddZ
dódddddZddd d!Zd"dd#d$Zd%dd&d'Zdôd(d	d)d*d+Zd,d	d)d-d.Zddd/d0Zed1dd2d3Zdd4d5d6d7Zed8d9d:d;d<Zed=d9d:d>d<Zed?d9d:d@d<ZedAd4d:dBd<ZedCd4d:dDd<ZedEd9d:dFd<ZedGd9d:dHd<ZedId4d:dJd<ZedKd4d:dLd<ZedMd9d:dNd<Zedd4d:dOd<ZedPd9d:dQd<ZedRd9d:dSd<ZedTd9d:dUd<ZdVdWd:dXd<ZddYdZd[d\Zed]d^d_d`dadbdcZedddedadfdcZedYdgdadhdcZdid^dYdgdadjdcZdkdldmdndoZdpd	d	d9dq fdrdsZdõd9dtd9du fdvdwZdödd9d5 fdydzZed{d fd|d}Zd9d{d~ fddZedd fddZeddddddddZed_ddddZedYddddZdddYddddZed]ddddkddddZ ed]dd_dkddddZ ed]ddYdkddddZ ddddYdkddddZ ddd9d fddZ!ddd9d fd d¡Z"d¢d9d£ fd€d¥Z#d÷dkd9d§ fdšd©Z$dødkd9d§ fdªd«Z%did¬d­dYd9d® fd¯d°Z&dùd±dd²dtd³dYd9dŽ fdµd¶Z'd·d9dž fd¹dºZ(dd»d­dYdŒdœdŸd¿Z)dddÀd­ddÁdYd9dÂ fdÃdÄZ*dúdddÆdÇd9dtdÈdtdtdd9dÉ fdÊdËZ+dddddddÌdÍd9dÎdÎdÎdtdtdtdÏd9dÐ	 fdÑdÒZ,d9d4d~dÓdÔZ-d9dYd~dÕdÖZ.d9d4d~d×dØZ/d9d9d~dÙdÚZ0dûd9dÛdÜd	dÝdÞdßZ1d9d fdàdáZ2düd9dkdkd9dã fdädåZ3d9dæd~dçdèZ4dýddddéd9dÛdêdYdÛd9dëdìdíZ5dþddddîd9dtdtdÎdÎd9dï fdðdñZ6  Z7S )ÿÚ	DataFramezê
    Narwhals DataFrame, backed by a native dataframe.

    The native dataframe might be pandas.DataFrame, polars.DataFrame, ...

    This class is not meant to be instantiated directly - instead, use
    `narwhals.from_native`.
    ztype[Series]r2   c                 C  s   ddl m} |S )Nr   r   )rF   r   )r+   r   r0   r0   r1   Ú_seriesA  s    zDataFrame._seriesztype[LazyFrame[Any]]c                 C  s   t S r-   )Ú	LazyFramer/   r0   r0   r1   Ú
_lazyframeG  s    zDataFrame._lazyframer   r(   ÚNone©r4   r6   r,   c                C  s6   || _ t|dr| ¡ | _ndt| }t|d S )NÚ__narwhals_dataframe__zCExpected an object which implements `__narwhals_dataframe__`, got: )r)   ÚhasattrrÊ   r'   rI   ÚAssertionError©r+   r4   r6   rK   r0   r0   r1   Ú__init__K  s
    
zDataFrame.__init__c                 C  s
   | j  ¡ S r-   )r'   Ú__len__r/   r0   r0   r1   rÏ   X  s    zDataFrame.__len__Nzbool | Nonez
np.ndarray)ÚdtypeÚcopyr,   c                 C  s   | j j||dS )N)rÑ   )r'   Ú	__array__)r+   rÐ   rÑ   r0   r0   r1   rÒ   [  s    zDataFrame.__array__rH   c                 C  s<   d}t |}dd|  d d| d d d d|  d	 S )
Nz' Narwhals DataFrame                    õ   âõ   âõ   â
ú|ú|
ú*| Use `.to_native` to see native output |
õ   âõ   â©r   ©r+   ÚheaderÚlengthr0   r0   r1   Ú__repr__^  s$    ÿþ
ýüûúùÿzDataFrame.__repr__zobject | NoneÚobject)Úrequested_schemar,   c              
   C  s    | j j}t|dr|j|dS zddl}W n: tk
rd } zdt| }t||W 5 d}~X Y nX t|jdk rdt| }t|d|  	¡ }|j|dS )aq  
        Export a DataFrame via the Arrow PyCapsule Interface.

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

        See [PyCapsule Interface](https://arrow.apache.org/docs/dev/format/CDataInterface/PyCapsuleInterface.html)
        for more.
        Ú__arrow_c_stream__)rá   r   NzRPyArrow>=14.0.0 is required for `DataFrame.__arrow_c_stream__` for object of type )é   r   )
r'   Ú_native_framerË   râ   ÚpyarrowÚModuleNotFoundErrorrI   r   Ú__version__Úto_arrow)r+   rá   Znative_frameÚpaÚexcrK   Zpa_tabler0   r0   r1   râ   l  s    


zDataFrame.__arrow_c_stream__zLazyFrame[Any]c                 C  s   | j | j ¡ | jdS )aí  
        Lazify the DataFrame (if possible).

        If a library does not support lazy execution, then this is a no-op.

        Examples:
            Construct pandas, Polars and PyArrow DataFrames:

            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> df = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)
            >>> df_pa = pa.table(df)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.lazy()

            Note that then, pandas and pyarrow dataframe stay eager, but Polars DataFrame becomes a Polars LazyFrame:

            >>> func(df_pd)
               foo  bar ham
            0    1  6.0   a
            1    2  7.0   b
            2    3  8.0   c
            >>> func(df_pl)
            <LazyFrame ...>
            >>> func(df_pa)
            pyarrow.Table
            foo: int64
            bar: double
            ham: string
            ----
            foo: [[1,2,3]]
            bar: [[6,7,8]]
            ham: [["a","b","c"]]
        r5   )rÇ   r'   Úlazyr)   r/   r0   r0   r1   rë     s    +zDataFrame.lazyr%   c                 C  s   | j jS )uþ  
        Convert Narwhals DataFrame to native one.

        Returns:
            Object of class that user started with.

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

            Calling `to_native` on a Narwhals DataFrame returns the native object:

            >>> nw.from_native(df_pd).to_native()
               foo  bar ham
            0    1  6.0   a
            1    2  7.0   b
            2    3  8.0   c
            >>> nw.from_native(df_pl).to_native()
            shape: (3, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â f64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6.0 â a   â
            â 2   â 7.0 â b   â
            â 3   â 8.0 â c   â
            âââââââŽââââââŽââââââ
            >>> nw.from_native(df_pa).to_native()
            pyarrow.Table
            foo: int64
            bar: double
            ham: string
            ----
            foo: [[1,2,3]]
            bar: [[6,7,8]]
            ham: [["a","b","c"]]
        )r'   rä   r/   r0   r0   r1   r   ±  s    .zDataFrame.to_nativezpd.DataFramec                 C  s
   | j  ¡ S )a§  
        Convert this DataFrame to a pandas DataFrame.

        Examples:
            Construct pandas, Polars (eager) and PyArrow DataFrames:

            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> df = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)
            >>> df_pa = pa.table(df)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.to_pandas()

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

            >>> func(df_pd)
               foo  bar ham
            0    1  6.0   a
            1    2  7.0   b
            2    3  8.0   c
            >>> func(df_pl)
               foo  bar ham
            0    1  6.0   a
            1    2  7.0   b
            2    3  8.0   c
            >>> func(df_pa)
               foo  bar ham
            0    1  6.0   a
            1    2  7.0   b
            2    3  8.0   c


        )r'   Ú	to_pandasr/   r0   r0   r1   rì   á  s    *zDataFrame.to_pandaszstr | Path | BytesIO | None)Úfiler,   c                 C  s   | j  |¡S )ae  
        Write dataframe to comma-separated values (CSV) file.

        Examples:
            Construct pandas and Polars DataFrames:

            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> df = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)
            >>> df_pa = pa.table(df)

            We define a library agnostic function:

            >>> def func(df):
            ...     df = nw.from_native(df)
            ...     return df.write_csv()

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

            >>> func(df_pd)
            'foo,bar,ham\n1,6.0,a\n2,7.0,b\n3,8.0,c\n'
            >>> func(df_pl)
            'foo,bar,ham\n1,6.0,a\n2,7.0,b\n3,8.0,c\n'
            >>> func(df_pa)
            '"foo","bar","ham"\n1,6,"a"\n2,7,"b"\n3,8,"c"\n'

            If we had passed a file name to `write_csv`, it would have been
            written to that file.
        )r'   Ú	write_csv©r+   rí   r0   r0   r1   rî     s    "zDataFrame.write_csvzstr | Path | BytesIOc                 C  s   | j  |¡ dS )aj  
        Write dataframe to parquet file.

        Examples:
            Construct pandas, Polars and PyArrow DataFrames:

            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> df = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)
            >>> df_pa = pa.table(df)

            We define a library agnostic function:

            >>> def func(df):
            ...     df = nw.from_native(df)
            ...     df.write_parquet("foo.parquet")

            We can then pass either pandas, Polars or PyArrow to `func`:

            >>> func(df_pd)  # doctest:+SKIP
            >>> func(df_pl)  # doctest:+SKIP
            >>> func(df_pa)  # doctest:+SKIP
        N)r'   Úwrite_parquetrï   r0   r0   r1   rð   1  s    zDataFrame.write_parquetc                 C  s
   | j  ¡ S )a  
        Convert this DataFrame to a NumPy ndarray.

        Examples:
            Construct pandas and polars DataFrames:

            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> df = {"foo": [1, 2, 3], "bar": [6.5, 7.0, 8.5], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)
            >>> df_pa = pa.table(df)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.to_numpy()

            We can then pass either pandas, Polars or PyArrow to `func`:

            >>> func(df_pd)
            array([[1, 6.5, 'a'],
                   [2, 7.0, 'b'],
                   [3, 8.5, 'c']], dtype=object)
            >>> func(df_pl)
            array([[1, 6.5, 'a'],
                   [2, 7.0, 'b'],
                   [3, 8.5, 'c']], dtype=object)
            >>> func(df_pa)
            array([[1, 6.5, 'a'],
                   [2, 7.0, 'b'],
                   [3, 8.5, 'c']], dtype=object)
        )r'   Úto_numpyr/   r0   r0   r1   rñ   O  s    %zDataFrame.to_numpyztuple[int, int]c                 C  s   | j jS )a  
        Get the shape of the DataFrame.

        Examples:
            Construct pandas and polars DataFrames:

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

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.shape

            We can then pass either pandas, Polars or PyArrow to `func`:

            >>> func(df_pd)
            (5, 1)
            >>> func(df_pl)
            (5, 1)
            >>> func(df_pa)
            (5, 1)
        )r'   Úshaper/   r0   r0   r1   rò   v  s     zDataFrame.shaper   rU   c                 C  s   | j | j |¡| jdS )aè  
        Get a single column by name.

        Notes:
            Although `name` is typed as `str`, pandas does allow non-string column
            names, and they will work when passed to this function if the
            `narwhals.DataFrame` is backed by a pandas dataframe with non-string
            columns. This function can only be used to extract a column by name, so
            there is no risk of ambiguity.

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

            We define a library agnostic function:

            >>> @nw.narwhalify(eager_only=True)
            ... def func(df):
            ...     name = df.columns[0]
            ...     return df.get_column(name)

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

            >>> func(df_pd)
            0    1
            1    2
            Name: a, dtype: int64
            >>> func(df_pl)  # doctest:+NORMALIZE_WHITESPACE
            shape: (2,)
            Series: 'a' [i64]
            [
                1
                2
            ]
        r5   )rÅ   r'   Ú
get_columnr)   rX   r0   r0   r1   ró     s    (
þzDataFrame.get_columnztuple[Sequence[int], slice]r   ©Úitemr,   c                 C  s   d S r-   r0   ©r+   rõ   r0   r0   r1   Ú__getitem__Å  s    zDataFrame.__getitem__z#tuple[Sequence[int], Sequence[int]]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   Ç  s    ztuple[slice, Sequence[int]]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   É  s    ztuple[Sequence[int], str]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   Ë  s    ztuple[slice, str]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   Í  s    z#tuple[Sequence[int], Sequence[str]]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   Ï  s    ztuple[slice, Sequence[str]]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   Ñ  s    ztuple[Sequence[int], int]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   Ó  s    ztuple[slice, int]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   Õ  s    zSequence[int]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   Ø  s    c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   Û  s    zSequence[str]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   Þ  s    Úslicec                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   á  s    ztuple[slice, slice]c                 C  s   d S r-   r0   rö   r0   r0   r1   r÷   ä  s    zÃstr | slice | Sequence[int] | Sequence[str] | tuple[Sequence[int], str | int] | tuple[slice, str | int] | tuple[slice | Sequence[int], Sequence[int] | Sequence[str] | slice] | tuple[slice, slice]zSeries | Selfc                 C  s@  t |tr|g}t |trPt|dkrPt |d ttfrPdt| d}t|t |trŽt|dkrŽt|d st |d trŽ|d tdkr€|d tdkr€| S |  	| j
| ¡S t |tsÔt |trêt|dkrê| j| j
| | jdS t|st |tst|r&|jdkr&|  	| j
| ¡S dt| }t|dS )a~  
        Extract column or slice of DataFrame.

        Arguments:
            item: How to slice dataframe. What happens depends on what is passed. It's easiest
                to explain by example. Suppose we have a Dataframe `df`:

                - `df['a']` extracts column `'a'` and returns a `Series`.
                - `df[0:2]` extracts the first two rows and returns a `DataFrame`.
                - `df[0:2, 'a']` extracts the first two rows from column `'a'` and returns
                    a `Series`.
                - `df[0:2, 0]` extracts the first two rows from the first column and returns
                    a `Series`.
                - `df[[0, 1], [0, 1, 2]]` extracts the first two rows and the first three columns
                    and returns a `DataFrame`
                - `df[:, [0, 1, 2]]` extracts all rows from the first three columns and returns a
                  `DataFrame`.
                - `df[:, ['a', 'c']]` extracts all rows and columns `'a'` and `'c'` and returns a
                  `DataFrame`.
                - `df[['a', 'c']]` extracts all rows and columns `'a'` and `'c'` and returns a
                  `DataFrame`.
                - `df[0: 2, ['a', 'c']]` extracts the first two rows and columns `'a'` and `'c'` and
                    returns a `DataFrame`
                - `df[:, 0: 2]` extracts all rows from the first two columns and returns a `DataFrame`
                - `df[:, 'a': 'c']` extracts all rows and all columns positioned between `'a'` and `'c'`
                    _inclusive_ and returns a `DataFrame`. For example, if the columns are
                    `'a', 'd', 'c', 'b'`, then that would extract columns `'a'`, `'d'`, and `'c'`.

        Notes:
            - Integers are always interpreted as positions
            - Strings are always interpreted as column names.

            In contrast with Polars, pandas allows non-string column names.
            If you don't know whether the column name you're trying to extract
            is definitely a string (e.g. `df[df.columns[0]]`) then you should
            use `DataFrame.get_column` instead.

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

            We define a library agnostic function:

            >>> @nw.narwhalify(eager_only=True)
            ... def func(df):
            ...     return df["a"]

            We can then pass either pandas, Polars or PyArrow to `func`:

            >>> func(df_pd)
            0    1
            1    2
            Name: a, dtype: int64
            >>> func(df_pl)  # doctest:+NORMALIZE_WHITESPACE
            shape: (2,)
            Series: 'a' [i64]
            [
                1
                2
            ]
            >>> func(df_pa)  # doctest:+ELLIPSIS
            <pyarrow.lib.ChunkedArray object at ...>
            [
              [
                1,
                2
              ]
            ]

        é   r   zExpected str or slice, got: z].

Hint: if you were trying to get a single element out of a dataframe, use `DataFrame.item`.r   Nr5   )rG   rm   Útupler   rH   rI   rJ   r   rø   r8   r'   rÅ   r)   r   Úndim©r+   rõ   rK   r0   r0   r1   r÷   ç  sL    Y
ÿ
þýÿÿ
þ
ýý  þÿþýýrs   )Úkeyr,   c                 C  s
   || j kS r-   )r`   )r+   rý   r0   r0   r1   Ú__contains__f  s    zDataFrame.__contains__.©Ú	as_serieszLiteral[True]zdict[str, Series])r   r,   c                C  s   d S r-   r0   ©r+   r   r0   r0   r1   Úto_dicti  s    zDataFrame.to_dictzLiteral[False]zdict[str, list[Any]]c                C  s   d S r-   r0   r  r0   r0   r1   r  k  s    z(dict[str, Series] | dict[str, list[Any]]c                C  s   d S r-   r0   r  r0   r0   r1   r  m  s    Tc                  s2   |r$ fdd j j|d ¡ D S  j j|dS )a5  
        Convert DataFrame to a dictionary mapping column name to values.

        Arguments:
            as_series: If set to true ``True``, then the values are Narwhals Series,
                        otherwise the values are Any.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> df = {
            ...     "A": [1, 2, 3, 4, 5],
            ...     "fruits": ["banana", "banana", "apple", "apple", "banana"],
            ...     "B": [5, 4, 3, 2, 1],
            ...     "animals": ["beetle", "fly", "beetle", "beetle", "beetle"],
            ...     "optional": [28, 300, None, 2, -30],
            ... }
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)
            >>> df_pa = pa.table(df)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.to_dict(as_series=False)

            We can then pass either pandas, Polars or PyArrow to `func`:

            >>> func(df_pd)
            {'A': [1, 2, 3, 4, 5], 'fruits': ['banana', 'banana', 'apple', 'apple', 'banana'], 'B': [5, 4, 3, 2, 1], 'animals': ['beetle', 'fly', 'beetle', 'beetle', 'beetle'], 'optional': [28.0, 300.0, nan, 2.0, -30.0]}
            >>> func(df_pl)
            {'A': [1, 2, 3, 4, 5], 'fruits': ['banana', 'banana', 'apple', 'apple', 'banana'], 'B': [5, 4, 3, 2, 1], 'animals': ['beetle', 'fly', 'beetle', 'beetle', 'beetle'], 'optional': [28, 300, None, 2, -30]}
            >>> func(df_pa)
            {'A': [1, 2, 3, 4, 5], 'fruits': ['banana', 'banana', 'apple', 'apple', 'banana'], 'B': [5, 4, 3, 2, 1], 'animals': ['beetle', 'fly', 'beetle', 'beetle', 'beetle'], 'optional': [28, 300, None, 2, -30]}
        c                   s"   i | ]\}}| j | jd qS )r5   )rÅ   r)   )r=   rý   Úvaluer/   r0   r1   rA     s   ü þz%DataFrame.to_dict.<locals>.<dictcomp>rÿ   )r'   r  rB   r  r0   r/   r1   r  o  s    )
ÿû	rm   ztuple[Any, ...])rT   r,   c                 C  s   | j  |¡S )aÁ  
        Get values at given row.

        !!!note
            You should NEVER use this method to iterate over a DataFrame;
            if you require row-iteration you should strongly prefer use of iter_rows() instead.

        Arguments:
            index: Row number.

        Notes:
            cuDF doesn't support this method.

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

            Let's define a library-agnostic function to get the second row.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.row(1)

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

            >>> func(df_pd)
            (2, 5)
            >>> func(df_pl)
            (2, 5)
        )r'   Úrow)r+   rT   r0   r0   r1   r  €  s    #zDataFrame.rowrO   rP   c                   s   t  j|f||S )u  
        Pipe function call.

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

            Let's define a dataframe-agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.pipe(
            ...         lambda _df: _df.select([x for x in _df.columns if len(x) == 1])
            ...     )

            We can then pass either pandas or Polars:

            >>> func(df_pd)
               a
            0  1
            1  2
            2  3
            >>> func(df_pl)
            shape: (3, 1)
            âââââââ
            â a   â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ
        ©ÚsuperrS   rR   ©r7   r0   r1   rS   Ê  s    'zDataFrame.piperY   rZ   c                   s   t  j|dS )uî  
        Drop null values.

        Arguments:
            subset: Column name(s) for which null values are considered. If set to None
                (default), use all columns.

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

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import narwhals as nw
            >>> data = {"a": [1.0, 2.0, None], "ba": [1.0, None, 2.0]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)

            Let's define a dataframe-agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.drop_nulls()

            We can then pass either pandas or Polars:

            >>> func(df_pd)
                 a   ba
            0  1.0  1.0
            >>> func(df_pl)
            shape: (1, 2)
            âââââââ¬ââââââ
            â a   â ba  â
            â --- â --- â
            â f64 â f64 â
            âââââââªââââââ¡
            â 1.0 â 1.0 â
            âââââââŽââââââ
        r\   ©r  r]   r^   r  r0   r1   r]   ó  s    )zDataFrame.drop_nullsrT   c                   s   t   |¡S )uõ  
        Insert column which enumerates rows.

        Examples:
            Construct pandas as polars DataFrames:

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

            Let's define a dataframe-agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.with_row_index()

            We can then pass either pandas or Polars:

            >>> func(df_pd)
               index  a  b
            0      0  1  4
            1      1  2  5
            2      2  3  6
            >>> func(df_pl)
            shape: (3, 3)
            âââââââââ¬ââââââ¬ââââââ
            â index â a   â b   â
            â ---   â --- â --- â
            â u32   â i64 â i64 â
            âââââââââªââââââªââââââ¡
            â 0     â 1   â 4   â
            â 1     â 2   â 5   â
            â 2     â 3   â 6   â
            âââââââââŽââââââŽââââââ
        ©r  rW   rX   r  r0   r1   rW     s    'zDataFrame.with_row_indexr   c                   s   t  jS )a¶  
        Get an ordered mapping of column names to their data type.

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import narwhals as nw
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6.0, 7.0, 8.0],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.schema

            You can pass either pandas or Polars to `func`:

            >>> df_pd_schema = func(df_pd)
            >>> df_pd_schema
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})

            >>> df_pl_schema = func(df_pl)
            >>> df_pl_schema
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
        ©r  rL   r/   r  r0   r1   rL   G  s    !zDataFrame.schemar*   c                   s
   t   ¡ S )aÀ  
        Get an ordered mapping of column names to their data type.

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import narwhals as nw
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6.0, 7.0, 8.0],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.collect_schema()

            You can pass either pandas or Polars to `func`:

            >>> df_pd_schema = func(df_pd)
            >>> df_pd_schema
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})

            >>> df_pl_schema = func(df_pl)
            >>> df_pl_schema
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
        ©r  rN   r/   r  r0   r1   rN   j  s     zDataFrame.collect_schemar_   c                   s   t  jS )aH  
        Get column names.

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

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.columns

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

            >>> func(df_pd)
            ['foo', 'bar', 'ham']
            >>> func(df_pl)
            ['foo', 'bar', 'ham']
            >>> func(df_pa)
            ['foo', 'bar', 'ham']
        ©r  r`   r/   r  r0   r1   r`     s    zDataFrame.columnsF©Únamedzlist[tuple[Any, ...]])r  r,   c                C  s   d S r-   r0   ©r+   r  r0   r0   r1   Úrows¬  s    zDataFrame.rowszlist[dict[str, Any]]c                C  s   d S r-   r0   r  r0   r0   r1   r  ²  s    z,list[tuple[Any, ...]] | list[dict[str, Any]]c                C  s   d S r-   r0   r  r0   r0   r1   r  ž  s    c                C  s   | j j|dS )ag  
        Returns all data in the DataFrame as a list of rows of python-native values.

        Arguments:
            named: By default, each row is returned as a tuple of values given
                in the same order as the frame columns. Setting named=True will
                return rows of dictionaries instead.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df, *, named):
            ...     return df.rows(named=named)

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

            >>> func(df_pd, named=False)
            [(1, 6.0, 'a'), (2, 7.0, 'b'), (3, 8.0, 'c')]
            >>> func(df_pd, named=True)
            [{'foo': 1, 'bar': 6.0, 'ham': 'a'}, {'foo': 2, 'bar': 7.0, 'ham': 'b'}, {'foo': 3, 'bar': 8.0, 'ham': 'c'}]
            >>> func(df_pl, named=False)
            [(1, 6.0, 'a'), (2, 7.0, 'b'), (3, 8.0, 'c')]
            >>> func(df_pl, named=True)
            [{'foo': 1, 'bar': 6.0, 'ham': 'a'}, {'foo': 2, 'bar': 7.0, 'ham': 'b'}, {'foo': 3, 'bar': 8.0, 'ham': 'c'}]
        r  )r'   r  r  r0   r0   r1   r  ¿  s    &)Úbuffer_sizezIterator[tuple[Any, ...]])r  r  r,   c                C  s   d S r-   r0   ©r+   r  r  r0   r0   r1   Ú	iter_rowsç  s    zDataFrame.iter_rowszIterator[dict[str, Any]]c                C  s   d S r-   r0   r  r0   r0   r1   r  ì  s    z4Iterator[tuple[Any, ...]] | Iterator[dict[str, Any]]c                C  s   d S r-   r0   r  r0   r0   r1   r  ñ  s    i   ©r  r  c                C  s   | j j||dS )aß  
        Returns an iterator over the DataFrame of rows of python-native values.

        Arguments:
            named: By default, each row is returned as a tuple of values given
                in the same order as the frame columns. Setting named=True will
                return rows of dictionaries instead.
            buffer_size: Determines the number of rows that are buffered
                internally while iterating over the data.
                See https://docs.pola.rs/api/python/stable/reference/dataframe/api/polars.DataFrame.iter_rows.html

        Notes:
            cuDF doesn't support this method.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df, *, named):
            ...     return df.iter_rows(named=named)

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

            >>> [row for row in func(df_pd, named=False)]
            [(1, 6.0, 'a'), (2, 7.0, 'b'), (3, 8.0, 'c')]
            >>> [row for row in func(df_pd, named=True)]
            [{'foo': 1, 'bar': 6.0, 'ham': 'a'}, {'foo': 2, 'bar': 7.0, 'ham': 'b'}, {'foo': 3, 'bar': 8.0, 'ham': 'c'}]
            >>> [row for row in func(df_pl, named=False)]
            [(1, 6.0, 'a'), (2, 7.0, 'b'), (3, 8.0, 'c')]
            >>> [row for row in func(df_pl, named=True)]
            [{'foo': 1, 'bar': 6.0, 'ham': 'a'}, {'foo': 2, 'bar': 7.0, 'ham': 'b'}, {'foo': 3, 'bar': 8.0, 'ham': 'c'}]
        r  )r'   r  r  r0   r0   r1   r  ö  s    *ra   r!   rb   c                   s   t  j||S )u:	  
        Add columns to this DataFrame.

        Added columns will replace existing columns with the same name.

        Arguments:
            *exprs: Column(s) to add, specified as positional arguments.
                     Accepts expression input. Strings are parsed as column names, other
                     non-expression inputs are parsed as literals.

            **named_exprs: Additional columns to add, specified as keyword arguments.
                            The columns will be renamed to the keyword used.

        Returns:
            DataFrame: A new DataFrame with the columns added.

        Note:
            Creating a new DataFrame using this method does not create a new copy of
            existing data.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {
            ...     "a": [1, 2, 3, 4],
            ...     "b": [0.5, 4, 10, 13],
            ...     "c": [True, True, False, True],
            ... }
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)

            Let's define a dataframe-agnostic function in which we pass an expression
            to add it as a new column:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.with_columns((nw.col("a") * 2).alias("a*2"))

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

            >>> func(df_pd)
               a     b      c  a*2
            0  1   0.5   True    2
            1  2   4.0   True    4
            2  3  10.0  False    6
            3  4  13.0   True    8
            >>> func(df_pl)
            shape: (4, 4)
            âââââââ¬âââââââ¬ââââââââ¬ââââââ
            â a   â b    â c     â a*2 â
            â --- â ---  â ---   â --- â
            â i64 â f64  â bool  â i64 â
            âââââââªâââââââªââââââââªââââââ¡
            â 1   â 0.5  â true  â 2   â
            â 2   â 4.0  â true  â 4   â
            â 3   â 10.0 â false â 6   â
            â 4   â 13.0 â true  â 8   â
            âââââââŽâââââââŽââââââââŽââââââ
        ©r  re   rf   r  r0   r1   re   "  s    ?zDataFrame.with_columnsc                   s   t  j||S )uÎ  
        Select columns from this DataFrame.

        Arguments:
            *exprs: Column(s) to select, specified as positional arguments.
                     Accepts expression input. Strings are parsed as column names,
                     other non-expression inputs are parsed as literals.

            **named_exprs: Additional columns to select, specified as keyword arguments.
                            The columns will be renamed to the keyword used.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6, 7, 8],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)

            Let's define a dataframe-agnostic function in which we pass the name of a
            column to select that column.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.select("foo")

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

            >>> func(df_pd)
               foo
            0    1
            1    2
            2    3
            >>> func(df_pl)
            shape: (3, 1)
            âââââââ
            â foo â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ

            Multiple columns can be selected by passing a list of column names.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.select(["foo", "bar"])
            >>> func(df_pd)
               foo  bar
            0    1    6
            1    2    7
            2    3    8
            >>> func(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 6   â
            â 2   â 7   â
            â 3   â 8   â
            âââââââŽââââââ

            Multiple columns can also be selected using positional arguments instead of a
            list. Expressions are also accepted.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.select(nw.col("foo"), nw.col("bar") + 1)
            >>> func(df_pd)
               foo  bar
            0    1    7
            1    2    8
            2    3    9
            >>> func(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 7   â
            â 2   â 8   â
            â 3   â 9   â
            âââââââŽââââââ

            Use keyword arguments to easily name your expression inputs.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.select(threshold=nw.col("foo") * 2)
            >>> func(df_pd)
               threshold
            0          2
            1          4
            2          6
            >>> func(df_pl)
            shape: (3, 1)
            âââââââââââââ
            â threshold â
            â ---       â
            â i64       â
            âââââââââââââ¡
            â 2         â
            â 4         â
            â 6         â
            âââââââââââââ
        ©r  rg   rf   r  r0   r1   rg   c  s    yzDataFrame.selectrh   ri   c                   s   t   |¡S )u=  
        Rename column names.

        Arguments:
            mapping: Key value pairs that map from old name to new name.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {"foo": [1, 2, 3], "bar": [6, 7, 8], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.rename({"foo": "apple"})

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

            >>> func(df_pd)
               apple  bar ham
            0      1    6   a
            1      2    7   b
            2      3    8   c
            >>> func(df_pl)
            shape: (3, 3)
            âââââââââ¬ââââââ¬ââââââ
            â apple â bar â ham â
            â ---   â --- â --- â
            â i64   â i64 â str â
            âââââââââªââââââªââââââ¡
            â 1     â 6   â a   â
            â 2     â 7   â b   â
            â 3     â 8   â c   â
            âââââââââŽââââââŽââââââ
        ©r  rk   rl   r  r0   r1   rk   Þ  s    (zDataFrame.renameé   rn   c                   s   t   |¡S )uÈ  
        Get the first `n` rows.

        Arguments:
            n: Number of rows to return. If a negative value is passed, return all rows
                except the last `abs(n)`.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {
            ...     "foo": [1, 2, 3, 4, 5],
            ...     "bar": [6, 7, 8, 9, 10],
            ...     "ham": ["a", "b", "c", "d", "e"],
            ... }
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)

            Let's define a dataframe-agnostic function that gets the first 3 rows.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.head(3)

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

            >>> func(df_pd)
               foo  bar ham
            0    1    6   a
            1    2    7   b
            2    3    8   c
            >>> func(df_pl)
            shape: (3, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            â 2   â 7   â b   â
            â 3   â 8   â c   â
            âââââââŽââââââŽââââââ
        ©r  rp   rq   r  r0   r1   rp     s    -zDataFrame.headc                   s   t   |¡S )uÇ  
        Get the last `n` rows.

        Arguments:
            n: Number of rows to return. If a negative value is passed, return all rows
                except the first `abs(n)`.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {
            ...     "foo": [1, 2, 3, 4, 5],
            ...     "bar": [6, 7, 8, 9, 10],
            ...     "ham": ["a", "b", "c", "d", "e"],
            ... }
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)

            Let's define a dataframe-agnostic function that gets the last 3 rows.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.tail(3)

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

            >>> func(df_pd)
               foo  bar ham
            2    3    8   c
            3    4    9   d
            4    5   10   e
            >>> func(df_pl)
            shape: (3, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 3   â 8   â c   â
            â 4   â 9   â d   â
            â 5   â 10  â e   â
            âââââââŽââââââŽââââââ
        ©r  rr   rq   r  r0   r1   rr   7  s    -zDataFrame.tailrv   r   rt   c                  s   t  jt|d|iS )u  
        Remove columns from the dataframe.

        Arguments:
            *columns: Names of the columns that should be removed from the dataframe.
            strict: Validate that all column names exist in the schema and throw an
                exception if a column name does not exist in the schema.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> data = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.drop("ham")

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

            >>> func(df_pd)
               foo  bar
            0    1  6.0
            1    2  7.0
            2    3  8.0
            >>> func(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â f64 â
            âââââââªââââââ¡
            â 1   â 6.0 â
            â 2   â 7.0 â
            â 3   â 8.0 â
            âââââââŽââââââ

            Use positional arguments to drop multiple columns.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.drop("foo", "ham")

            >>> func(df_pd)
               bar
            0  6.0
            1  7.0
            2  8.0
            >>> func(df_pl)
            shape: (3, 1)
            âââââââ
            â bar â
            â --- â
            â f64 â
            âââââââ¡
            â 6.0 â
            â 7.0 â
            â 8.0 â
            âââââââ
        ru   ©r  rw   r   rx   r  r0   r1   rw   f  s    AzDataFrame.dropry   rz   r}   r~   c                  s   t  j|||dS )u}  
        Drop duplicate rows from this dataframe.

        Arguments:
            subset: Column name(s) to consider when identifying duplicate rows.
            keep: {'first', 'last', 'any', 'none'}
                Which of the duplicate rows to keep.

                * 'any': Does not give any guarantee of which row is kept.
                        This allows more optimizations.
                * 'none': Don't keep duplicate rows.
                * 'first': Keep first unique row.
                * 'last': Keep last unique row.
            maintain_order: Keep the same order as the original DataFrame. This may be more
                expensive to compute. Settings this to `True` blocks the possibility
                to run on the streaming engine for Polars.

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

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.unique(["bar", "ham"])

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

            >>> func(df_pd)
               foo bar ham
            0    1   a   b
            >>> func(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â str â str â
            âââââââªââââââªââââââ¡
            â 1   â a   â b   â
            âââââââŽââââââŽââââââ
        rz   ©r  r   r   r  r0   r1   r   ©  s    9zDataFrame.uniquer   r   c                   s   t  j| S )uï  
        Filter the rows in the DataFrame based on one or more predicate expressions.

        The original order of the remaining rows is preserved.

        Arguments:
            *predicates: Expression(s) that evaluates to a boolean Series. Can
                also be a (single!) boolean list.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6, 7, 8],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)

            Let's define a dataframe-agnostic function in which we filter on
            one condition.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.filter(nw.col("foo") > 1)

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

            >>> func(df_pd)
               foo  bar ham
            1    2    7   b
            2    3    8   c
            >>> func(df_pl)
            shape: (2, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 2   â 7   â b   â
            â 3   â 8   â c   â
            âââââââŽââââââŽââââââ

            Filter on multiple conditions, combined with and/or operators:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.filter((nw.col("foo") < 3) & (nw.col("ham") == "a"))
            >>> func(df_pd)
               foo  bar ham
            0    1    6   a
            >>> func(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            âââââââŽââââââŽââââââ

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.filter((nw.col("foo") == 1) | (nw.col("ham") == "c"))
            >>> func(df_pd)
               foo  bar ham
            0    1    6   a
            2    3    8   c
            >>> func(df_pl)
            shape: (2, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            â 3   â 8   â c   â
            âââââââŽââââââŽââââââ

            Provide multiple filters using `*args` syntax:

            >>> @nw.narwhalify
            ... def func(df):
            ...     dframe = df.filter(
            ...         nw.col("foo") <= 2,
            ...         ~nw.col("ham").is_in(["b", "c"]),
            ...     )
            ...     return dframe
            >>> func(df_pd)
               foo  bar ham
            0    1    6   a
            >>> func(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            âââââââŽââââââŽââââââ
        ©r  r   ©r+   r   r  r0   r1   r   ä  s    hzDataFrame.filter©Údrop_null_keyszGroupBy[Self]©Úkeysr   r,   c                G  s$   ddl m} || ft|d|iS )uà	  
        Start a group by operation.

        Arguments:
            *keys: Column(s) to group by. Accepts multiple columns names as a list.
            drop_null_keys: if True, then groups where any key is null won't be included
                in the result.

        Returns:
            GroupBy: Object which can be used to perform aggregations.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {
            ...     "a": ["a", "b", "a", "b", "c"],
            ...     "b": [1, 2, 1, 3, 3],
            ...     "c": [5, 4, 3, 2, 1],
            ... }
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)

            Let's define a dataframe-agnostic function in which we group by one column
            and call `agg` to compute the grouped sum of another column.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.group_by("a").agg(nw.col("b").sum()).sort("a")

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

            >>> func(df_pd)
               a  b
            0  a  2
            1  b  5
            2  c  3
            >>> func(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â str â i64 â
            âââââââªââââââ¡
            â a   â 2   â
            â b   â 5   â
            â c   â 3   â
            âââââââŽââââââ

            Group by multiple columns by passing a list of column names.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.group_by(["a", "b"]).agg(nw.max("c")).sort("a", "b")
            >>> func(df_pd)
               a  b  c
            0  a  1  5
            1  b  2  4
            2  b  3  2
            3  c  3  1
            >>> func(df_pl)
            shape: (4, 3)
            âââââââ¬ââââââ¬ââââââ
            â a   â b   â c   â
            â --- â --- â --- â
            â str â i64 â i64 â
            âââââââªââââââªââââââ¡
            â a   â 1   â 5   â
            â b   â 2   â 4   â
            â b   â 3   â 2   â
            â c   â 3   â 1   â
            âââââââŽââââââŽââââââ
        r   r   r   )Únarwhals.group_byr   r   )r+   r   r"  r   r0   r0   r1   Úgroup_byN  s    LzDataFrame.group_byr   r   r   c                  s   t  j|f|||dS )uä  
        Sort the dataframe by the given columns.

        Arguments:
            by: Column(s) names to sort by.
            *more_by: Additional columns to sort by, specified as positional arguments.
            descending: Sort in descending order. When sorting by multiple columns, can be
                specified per column by passing a sequence of booleans.
            nulls_last: Place null values last.

        Warning:
            Unlike Polars, it is not possible to specify a sequence of booleans for
            `nulls_last` in order to control per-column behaviour. Instead a single
            boolean is applied for all `by` columns.

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data = {
            ...     "a": [1, 2, None],
            ...     "b": [6.0, 5.0, 4.0],
            ...     "c": ["a", "c", "b"],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)

            Let's define a dataframe-agnostic function in which we sort by multiple
            columns in different orders

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.sort("c", "a", descending=[False, True])

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

            >>> func(df_pd)
                 a    b  c
            0  1.0  6.0  a
            2  NaN  4.0  b
            1  2.0  5.0  c
            >>> func(df_pl)
            shape: (3, 3)
            ââââââââ¬ââââââ¬ââââââ
            â a    â b   â c   â
            â ---  â --- â --- â
            â i64  â f64 â str â
            ââââââââªââââââªââââââ¡
            â 1    â 6.0 â a   â
            â null â 4.0 â b   â
            â 2    â 5.0 â c   â
            ââââââââŽââââââŽââââââ
        r   ©r  r   r   r  r0   r1   r     s    <zDataFrame.sortr   r   r   r   r   c                  s   t  j||||||dS )uï	  
        Join in SQL-like fashion.

        Arguments:
            other: Lazy DataFrame to join with.
            on: Name(s) of the join columns in both DataFrames. If set, `left_on` and
                `right_on` should be None.
            how: Join strategy.

                  * *inner*: Returns rows that have matching values in both tables.
                  * *cross*: Returns the Cartesian product of rows from both tables.
                  * *semi*: Filter rows that have a match in the right table.
                  * *anti*: Filter rows that do not have a match in the right table.
            left_on: Join column of the left DataFrame.
            right_on: Join column of the right DataFrame.
            suffix: Suffix to append to columns with a duplicate name.

        Returns:
            A new joined DataFrame

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6.0, 7.0, 8.0],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> data_other = {
            ...     "apple": ["x", "y", "z"],
            ...     "ham": ["a", "b", "d"],
            ... }

            >>> df_pd = pd.DataFrame(data)
            >>> other_pd = pd.DataFrame(data_other)

            >>> df_pl = pl.DataFrame(data)
            >>> other_pl = pl.DataFrame(data_other)

            Let's define a dataframe-agnostic function in which we join over "ham" column:

            >>> @nw.narwhalify
            ... def join_on_ham(df, other_any):
            ...     return df.join(other_any, left_on="ham", right_on="ham")

            We can now pass either pandas or Polars to the function:

            >>> join_on_ham(df_pd, other_pd)
               foo  bar ham apple
            0    1  6.0   a     x
            1    2  7.0   b     y

            >>> join_on_ham(df_pl, other_pl)
            shape: (2, 4)
            âââââââ¬ââââââ¬ââââââ¬ââââââââ
            â foo â bar â ham â apple â
            â --- â --- â --- â ---   â
            â i64 â f64 â str â str   â
            âââââââªââââââªââââââªââââââââ¡
            â 1   â 6.0 â a   â x     â
            â 2   â 7.0 â b   â y     â
            âââââââŽââââââŽââââââŽââââââââ
        ©r    r   r   r   r   ©r  r©   ©r+   r   r   r    r   r   r   r  r0   r1   r©   Ü  s    J     ÿzDataFrame.joinr°   r±   rµ   r¶   r·   c          	   
     s   t  j||||||||dS )u!  
        Perform an asof join.

        This is similar to a left-join except that we match on nearest key rather than equal keys.

        Both DataFrames must be sorted by the asof_join key.

        Arguments:
            other: DataFrame to join with.

            left_on: Name(s) of the left join column(s).

            right_on: Name(s) of the right join column(s).

            on: Join column of both DataFrames. If set, left_on and right_on should be None.

            by_left: join on these columns before doing asof join

            by_right: join on these columns before doing asof join

            by: join on these columns before doing asof join

            strategy: Join strategy. The default is "backward".

                  * *backward*: selects the last row in the right DataFrame whose "on" key is less than or equal to the left's key.
                  * *forward*: selects the first row in the right DataFrame whose "on" key is greater than or equal to the left's key.
                  * *nearest*: search selects the last row in the right DataFrame whose value is nearest to the left's key.

        Returns:
            A new joined DataFrame

        Examples:
            >>> from datetime import datetime
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data_gdp = {
            ...     "datetime": [
            ...         datetime(2016, 1, 1),
            ...         datetime(2017, 1, 1),
            ...         datetime(2018, 1, 1),
            ...         datetime(2019, 1, 1),
            ...         datetime(2020, 1, 1),
            ...     ],
            ...     "gdp": [4164, 4411, 4566, 4696, 4827],
            ... }
            >>> data_population = {
            ...     "datetime": [
            ...         datetime(2016, 3, 1),
            ...         datetime(2018, 8, 1),
            ...         datetime(2019, 1, 1),
            ...     ],
            ...     "population": [82.19, 82.66, 83.12],
            ... }
            >>> gdp_pd = pd.DataFrame(data_gdp)
            >>> population_pd = pd.DataFrame(data_population)

            >>> gdp_pl = pl.DataFrame(data_gdp).sort("datetime")
            >>> population_pl = pl.DataFrame(data_population).sort("datetime")

            Let's define a dataframe-agnostic function in which we join over "datetime" column:

            >>> @nw.narwhalify
            ... def join_asof_datetime(df, other_any, strategy):
            ...     return df.join_asof(other_any, on="datetime", strategy=strategy)

            We can now pass either pandas or Polars to the function:

            >>> join_asof_datetime(population_pd, gdp_pd, strategy="backward")
                datetime  population   gdp
            0 2016-03-01       82.19  4164
            1 2018-08-01       82.66  4566
            2 2019-01-01       83.12  4696

            >>> join_asof_datetime(population_pl, gdp_pl, strategy="backward")
            shape: (3, 3)
            âââââââââââââââââââââââ¬âââââââââââââ¬âââââââ
            â datetime            â population â gdp  â
            â ---                 â ---        â ---  â
            â datetime[ÎŒs]        â f64        â i64  â
            âââââââââââââââââââââââªâââââââââââââªâââââââ¡
            â 2016-03-01 00:00:00 â 82.19      â 4164 â
            â 2018-08-01 00:00:00 â 82.66      â 4566 â
            â 2019-01-01 00:00:00 â 83.12      â 4696 â
            âââââââââââââââââââââââŽâââââââââââââŽâââââââ

            Here is a real-world times-series example that uses `by` argument.

            >>> from datetime import datetime
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data_quotes = {
            ...     "datetime": [
            ...         datetime(2016, 5, 25, 13, 30, 0, 23),
            ...         datetime(2016, 5, 25, 13, 30, 0, 23),
            ...         datetime(2016, 5, 25, 13, 30, 0, 30),
            ...         datetime(2016, 5, 25, 13, 30, 0, 41),
            ...         datetime(2016, 5, 25, 13, 30, 0, 48),
            ...         datetime(2016, 5, 25, 13, 30, 0, 49),
            ...         datetime(2016, 5, 25, 13, 30, 0, 72),
            ...         datetime(2016, 5, 25, 13, 30, 0, 75),
            ...     ],
            ...     "ticker": [
            ...         "GOOG",
            ...         "MSFT",
            ...         "MSFT",
            ...         "MSFT",
            ...         "GOOG",
            ...         "AAPL",
            ...         "GOOG",
            ...         "MSFT",
            ...     ],
            ...     "bid": [720.50, 51.95, 51.97, 51.99, 720.50, 97.99, 720.50, 52.01],
            ...     "ask": [720.93, 51.96, 51.98, 52.00, 720.93, 98.01, 720.88, 52.03],
            ... }
            >>> data_trades = {
            ...     "datetime": [
            ...         datetime(2016, 5, 25, 13, 30, 0, 23),
            ...         datetime(2016, 5, 25, 13, 30, 0, 38),
            ...         datetime(2016, 5, 25, 13, 30, 0, 48),
            ...         datetime(2016, 5, 25, 13, 30, 0, 48),
            ...         datetime(2016, 5, 25, 13, 30, 0, 48),
            ...     ],
            ...     "ticker": ["MSFT", "MSFT", "GOOG", "GOOG", "AAPL"],
            ...     "price": [51.95, 51.95, 720.77, 720.92, 98.0],
            ...     "quantity": [75, 155, 100, 100, 100],
            ... }
            >>> quotes_pd = pd.DataFrame(data_quotes)
            >>> trades_pd = pd.DataFrame(data_trades)
            >>> quotes_pl = pl.DataFrame(data_quotes).sort("datetime")
            >>> trades_pl = pl.DataFrame(data_trades).sort("datetime")

            Let's define a dataframe-agnostic function in which we join over "datetime" and by "ticker" columns:

            >>> @nw.narwhalify
            ... def join_asof_datetime_by_ticker(df, other_any):
            ...     return df.join_asof(other_any, on="datetime", by="ticker")

            We can now pass either pandas or Polars to the function:

            >>> join_asof_datetime_by_ticker(trades_pd, quotes_pd)
                                datetime ticker   price  quantity     bid     ask
            0 2016-05-25 13:30:00.000023   MSFT   51.95        75   51.95   51.96
            1 2016-05-25 13:30:00.000038   MSFT   51.95       155   51.97   51.98
            2 2016-05-25 13:30:00.000048   GOOG  720.77       100  720.50  720.93
            3 2016-05-25 13:30:00.000048   GOOG  720.92       100  720.50  720.93
            4 2016-05-25 13:30:00.000048   AAPL   98.00       100     NaN     NaN

            >>> join_asof_datetime_by_ticker(trades_pl, quotes_pl)
            shape: (5, 6)
            ââââââââââââââââââââââââââââââ¬âââââââââ¬âââââââââ¬âââââââââââ¬ââââââââ¬âââââââââ
            â datetime                   â ticker â price  â quantity â bid   â ask    â
            â ---                        â ---    â ---    â ---      â ---   â ---    â
            â datetime[ÎŒs]               â str    â f64    â i64      â f64   â f64    â
            ââââââââââââââââââââââââââââââªâââââââââªâââââââââªâââââââââââªââââââââªâââââââââ¡
            â 2016-05-25 13:30:00.000023 â MSFT   â 51.95  â 75       â 51.95 â 51.96  â
            â 2016-05-25 13:30:00.000038 â MSFT   â 51.95  â 155      â 51.97 â 51.98  â
            â 2016-05-25 13:30:00.000048 â GOOG   â 720.77 â 100      â 720.5 â 720.93 â
            â 2016-05-25 13:30:00.000048 â GOOG   â 720.92 â 100      â 720.5 â 720.93 â
            â 2016-05-25 13:30:00.000048 â AAPL   â 98.0   â 100      â null  â null   â
            ââââââââââââââââââââââââââââââŽâââââââââŽâââââââââŽâââââââââââŽââââââââŽâââââââââ
        r±   ©r  rž   ©	r+   r   r   r   r   r²   r³   r   rŽ   r  r0   r1   rž   *  s     0øzDataFrame.join_asofc                 C  s   | j | j ¡ | jdS )aË  
        Get a mask of all duplicated rows in this DataFrame.

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> df_pd = pd.DataFrame(
            ...     {
            ...         "a": [1, 2, 3, 1],
            ...         "b": ["x", "y", "z", "x"],
            ...     }
            ... )
            >>> df_pl = pl.DataFrame(
            ...     {
            ...         "a": [1, 2, 3, 1],
            ...         "b": ["x", "y", "z", "x"],
            ...     }
            ... )

            Let's define a dataframe-agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.is_duplicated()

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

            >>> func(df_pd)  # doctest: +NORMALIZE_WHITESPACE
            0     True
            1    False
            2    False
            3     True
            dtype: bool

            >>> func(df_pl)  # doctest: +NORMALIZE_WHITESPACE
            shape: (4,)
            Series: '' [bool]
            [
                true
                false
                false
                true
            ]
        r5   )rÅ   r'   Úis_duplicatedr)   r/   r0   r0   r1   r+  å  s    .þzDataFrame.is_duplicatedc                 C  s
   | j  ¡ S )aÞ  
        Check if the dataframe is empty.

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl

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

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.filter(nw.col("foo") > 10).is_empty()

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

            >>> df_pd = pd.DataFrame({"foo": [1, 2, 3], "bar": [4, 5, 6]})
            >>> df_pl = pl.DataFrame({"foo": [1, 2, 3], "bar": [4, 5, 6]})
            >>> func(df_pd), func(df_pl)
            (True, True)

            >>> df_pd = pd.DataFrame({"foo": [100, 2, 3], "bar": [4, 5, 6]})
            >>> df_pl = pl.DataFrame({"foo": [100, 2, 3], "bar": [4, 5, 6]})
            >>> func(df_pd), func(df_pl)
            (False, False)
        )r'   Úis_emptyr/   r0   r0   r1   r,  	  s    zDataFrame.is_emptyc                 C  s   | j | j ¡ | jdS )aÅ  
        Get a mask of all unique rows in this DataFrame.

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> df_pd = pd.DataFrame(
            ...     {
            ...         "a": [1, 2, 3, 1],
            ...         "b": ["x", "y", "z", "x"],
            ...     }
            ... )
            >>> df_pl = pl.DataFrame(
            ...     {
            ...         "a": [1, 2, 3, 1],
            ...         "b": ["x", "y", "z", "x"],
            ...     }
            ... )

            Let's define a dataframe-agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.is_unique()

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

            >>> func(df_pd)  # doctest: +NORMALIZE_WHITESPACE
            0    False
            1     True
            2     True
            3    False
            dtype: bool

            >>> func(df_pl)  # doctest: +NORMALIZE_WHITESPACE
            shape: (4,)
            Series: '' [bool]
            [
                false
                 true
                 true
                false
            ]
        r5   )rÅ   r'   Ú	is_uniquer)   r/   r0   r0   r1   r-  6	  s    .þzDataFrame.is_uniquec                 C  s   |   | j ¡ ¡S )ud  
        Create a new DataFrame that shows the null counts per column.

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

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> df_pd = pd.DataFrame(
            ...     {
            ...         "foo": [1, None, 3],
            ...         "bar": [6, 7, None],
            ...         "ham": ["a", "b", "c"],
            ...     }
            ... )
            >>> df_pl = pl.DataFrame(
            ...     {
            ...         "foo": [1, None, 3],
            ...         "bar": [6, 7, None],
            ...         "ham": ["a", "b", "c"],
            ...     }
            ... )

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

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.null_count()

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

            >>> func(df_pd)
               foo  bar  ham
            0    1    1    0

            >>> func(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â u32 â u32 â u32 â
            âââââââªââââââªââââââ¡
            â 1   â 1   â 0   â
            âââââââŽââââââŽââââââ
        )r8   r'   Ú
null_countr/   r0   r0   r1   r.  i	  s    2zDataFrame.null_countz
int | Nonezint | str | None)r+   r  Úcolumnr,   c                 C  s   | j j||dS )aì  
        Return the DataFrame as a scalar, or return the element at the given row/column.

        Notes:
            If row/col not provided, this is equivalent to df[0,0], with a check that the shape is (1,1).
            With row/col, this is equivalent to df[row,col].

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

            Let's define a dataframe-agnostic function that returns item at given row/column

            >>> @nw.narwhalify
            ... def func(df, row, column):
            ...     return df.item(row, column)

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

            >>> func(df_pd, 1, 1), func(df_pd, 2, "b")
            (np.int64(5), np.int64(6))

            >>> func(df_pl, 1, 1), func(df_pl, 2, "b")
            (5, 6)
        )r  r/  )r'   rõ   )r+   r  r/  r0   r0   r1   rõ   	  s    zDataFrame.itemc                   s
   t   ¡ S )uÄ  
        Create a copy of this DataFrame.

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

            Let's define a dataframe-agnostic function in which we clone the DataFrame:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.clone()

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

            >>> func(df_pl)
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 3   â
            â 2   â 4   â
            âââââââŽââââââ
        ©r  rª   r/   r  r0   r1   rª   œ	  s    "zDataFrame.cloner   r«   c                   s   t  j||dS )u  
        Take every nth row in the DataFrame and return as a new DataFrame.

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

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

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

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.gather_every(n=2, offset=1)

            >>> func(df_pd)
               a  b
            1  2  6
            3  4  8

            >>> func(df_pl)
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 2   â 6   â
            â 4   â 8   â
            âââââââŽââââââ
        r­   ©r  r®   r¯   r  r0   r1   r®   á	  s    'zDataFrame.gather_everyzpa.Tablec                 C  s
   | j  ¡ S )a  
        Convert to arrow table.

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

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

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.to_arrow()

            >>> func(df_pd)
            pyarrow.Table
            foo: int64
            bar: string
            ----
            foo: [[1,2,3]]
            bar: [["a","b","c"]]

            >>> func(df_pl)  # doctest:+NORMALIZE_WHITESPACE
            pyarrow.Table
            foo: int64
            bar: large_string
            ----
            foo: [[1,2,3]]
            bar: [["a","b","c"]]
        )r'   rè   r/   r0   r0   r1   rè   

  s    "zDataFrame.to_arrow)ÚfractionÚwith_replacementÚseedzfloat | None)r+   ro   r2  r3  r4  r,   c                C  s   |   | jj||||d¡S )uy  
        Sample from this DataFrame.

        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.

        Notes:
            The results may not be consistent across libraries.

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

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.sample(n=2, seed=123)

            We can then pass either pandas or Polars to `func`:
            >>> func(df_pd)
               a  b
            3  4  y
            0  1  x
            >>> func(df_pl)
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â str â
            âââââââªââââââ¡
            â 2   â y   â
            â 3   â x   â
            âââââââŽââââââ

            As you can see, by using the same seed, the result will be consistent within
            the same backend, but not necessarely across different backends.
        )ro   r2  r3  r4  )r8   r'   Úsample)r+   ro   r2  r3  r4  r0   r0   r1   r5  .
  s    6   ÿÿzDataFrame.sample©rT   rº   r»   r¹   c                  s   t  j||||dS )u²  
        Unpivot a DataFrame from wide to long format.

        Optionally leaves identifiers set.

        This function is useful to massage a DataFrame into a format where one or more
        columns are identifier variables (index) while all other columns, considered
        measured variables (on), are "unpivoted" to the row axis leaving just
        two non-identifier columns, 'variable' and 'value'.

        Arguments:
            on: Column(s) to use as values variables; if `on` is empty all columns that
                are not in `index` will be used.
            index: Column(s) to use as identifier variables.
            variable_name: Name to give to the `variable` column. Defaults to "variable".
            value_name: Name to give to the `value` column. Defaults to "value".

        Notes:
            If you're coming from pandas, this is similar to `pandas.DataFrame.melt`,
            but with `index` replacing `id_vars` and `on` replacing `value_vars`.
            In other frameworks, you might know this operation as `pivot_longer`.

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

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.unpivot(on=["b", "c"], index="a")

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

            >>> func(pl.DataFrame(data))
            shape: (6, 3)
            âââââââ¬âââââââââââ¬ââââââââ
            â a   â variable â value â
            â --- â ---      â ---   â
            â str â str      â i64   â
            âââââââªâââââââââââªââââââââ¡
            â x   â b        â 1     â
            â y   â b        â 3     â
            â z   â b        â 5     â
            â x   â c        â 2     â
            â y   â c        â 4     â
            â z   â c        â 6     â
            âââââââŽâââââââââââŽââââââââ

            >>> func(pd.DataFrame(data))
               a variable  value
            0  x        b      1
            1  y        b      3
            2  z        b      5
            3  x        c      2
            4  y        c      4
            5  z        c      6

            >>> func(pa.table(data))
            pyarrow.Table
            a: string
            variable: string
            value: int64
            ----
            a: [["x","y","z"],["x","y","z"]]
            variable: [["b","b","b"],["c","c","c"]]
            value: [[1,3,5],[2,4,6]]
        rŒ   ©r  rœ   rŸ   r  r0   r1   rœ   j
  s    S   ÿzDataFrame.unpivot)NN)N)N)N)rT   )r  )r  )N)Nr   )NN)r   )N)N)8r¿   rÀ   rÁ   Ú__doc__rÃ   rÅ   rÇ   rÎ   rÏ   rÒ   rß   râ   rë   r   rì   rî   rð   rñ   rò   ró   r   r÷   rþ   r  r  rS   r]   rW   rL   rN   r`   r  r  re   rg   rk   rp   rr   rw   r   r   r$  r   r©   rž   r+  r,  r-  r.  rõ   rª   r®   rè   r5  rœ   Ú__classcell__r0   r0   r  r1   rÄ   7  s  	-0,$'!-ÿ5&)+)""ý	ý(ÿÿÿ ÿ,A{*//E þû;kÿTûA  üø"Rö& <334 $)& þú> þúrÄ   c                      s°  e Zd ZdZeddddZdddd	d
dZddddZdddddZddddZ	ddddZ
ddddd fddZdydd dd! fd"d#Zdzddd% fd&d'Zed(d fd)d*Zdd(d+ fd,d-Zed.d fd/d0Zd1d2dd3 fd4d5Zd1d2dd3 fd6d7Zd8dd9 fd:d;Zd{d=dd> fd?d@Zd|d=dd> fdAdBZdCdDdEdFddG fdHdIZd}dJdKdLd dMdFddN fdOdPZdQddR fdSdTZdKdUdEdFdVdWdXdYZdKdKdZdEdd[dFdd\ fd]d^Zd~ddd`dadd dbd d dddc fdddeZdddddddfdgddhdhdhd d d diddj	 fdkdlZdd fdmdnZdddodpZddd=d=ddr fdsdtZdddddudd d dhdhddv fdwdxZ   Z!S )rÆ   zê
    Narwhals DataFrame, backed by a native dataframe.

    The native dataframe might be pandas.DataFrame, polars.LazyFrame, ...

    This class is not meant to be instantiated directly - instead, use
    `narwhals.from_native`.
    ztype[DataFrame[Any]]r2   c                 C  s   t S r-   )rÄ   r/   r0   r0   r1   Ú
_dataframeÌ
  s    zLazyFrame._dataframer   r(   rÈ   rÉ   c                C  s6   || _ t|dr| ¡ | _ndt| }t|d S )NÚ__narwhals_lazyframe__zVExpected Polars LazyFrame or an object that implements `__narwhals_lazyframe__`, got: )r)   rË   r;  r'   rI   rÌ   rÍ   r0   r0   r1   rÎ   Ð
  s
    
zLazyFrame.__init__rH   c                 C  s<   d}t |}dd|  d d| d d d d|  d	 S )
Nz' Narwhals LazyFrame                    rÓ   rÔ   rÕ   rÖ   r×   rØ   rÙ   rÚ   rÛ   rÜ   r0   r0   r1   rß   Ý
  s$    ÿþ
ýüûúùÿzLazyFrame.__repr__zstr | slicer
   rô   c                 C  s   d}t |d S )Nz%Slicing is not supported on LazyFrame)rJ   rü   r0   r0   r1   r÷   ë
  s    zLazyFrame.__getitem__zDataFrame[Any]c                 C  s   | j | j ¡ | jdS )uÉ  
        Materialize this LazyFrame into a DataFrame.

        Returns:
            DataFrame

        Examples:
            >>> import narwhals as nw
            >>> import polars as pl
            >>> lf_pl = pl.LazyFrame(
            ...     {
            ...         "a": ["a", "b", "a", "b", "b", "c"],
            ...         "b": [1, 2, 3, 4, 5, 6],
            ...         "c": [6, 5, 4, 3, 2, 1],
            ...     }
            ... )
            >>> lf = nw.from_native(lf_pl)
            >>> lf
            âââââââââââââââââââââââââââââââââââââââââ
            | Narwhals LazyFrame                    |
            | Use `.to_native` to see native output |
            âââââââââââââââââââââââââââââââââââââââââ
            >>> df = lf.group_by("a").agg(nw.all().sum()).collect()
            >>> df.to_native().sort("a")
            shape: (3, 3)
            âââââââ¬ââââââ¬ââââââ
            â a   â b   â c   â
            â --- â --- â --- â
            â str â i64 â i64 â
            âââââââªââââââªââââââ¡
            â a   â 4   â 10  â
            â b   â 11  â 10  â
            â c   â 6   â 1   â
            âââââââŽââââââŽââââââ
        r5   )r:  r'   Úcollectr)   r/   r0   r0   r1   r<  ï
  s    $þzLazyFrame.collectr#   c                 C  s   t | ddS )u  
        Convert Narwhals LazyFrame to native one.

        Returns:
            Object of class that user started with.

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

            Calling `to_native` on a Narwhals DataFrame returns the native object:

            >>> nw.from_native(df_pd).lazy().to_native()
               foo  bar ham
            0    1  6.0   a
            1    2  7.0   b
            2    3  8.0   c
            >>> nw.from_native(df_pl).to_native().collect()
            shape: (3, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â f64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6.0 â a   â
            â 2   â 7.0 â b   â
            â 3   â 8.0 â c   â
            âââââââŽââââââŽââââââ
        F)Znarwhals_objectZpass_throughr   r/   r0   r0   r1   r     s    %zLazyFrame.to_nativerO   r   rP   c                   s   t  j|f||S )uº  
        Pipe function call.

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

            Let's define a dataframe-agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.pipe(lambda _df: _df.select("a"))

            We can then pass either pandas or Polars:

            >>> func(df_pd)
               a
            0  1
            1  2
            2  3
            >>> func(df_pl).collect()
            shape: (3, 1)
            âââââââ
            â a   â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ
        r  rR   r  r0   r1   rS   @  s    %zLazyFrame.pipeNrY   rZ   c                   s   t  j|dS )uø  
        Drop null values.

        Arguments:
            subset: Column name(s) for which null values are considered. If set to None
                (default), use all columns.

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

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import narwhals as nw
            >>> data = {"a": [1.0, 2.0, None], "ba": [1.0, None, 2.0]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.LazyFrame(data)

            Let's define a dataframe-agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.drop_nulls()

            We can then pass either pandas or Polars:

            >>> func(df_pd)
                 a   ba
            0  1.0  1.0
            >>> func(df_pl).collect()
            shape: (1, 2)
            âââââââ¬ââââââ
            â a   â ba  â
            â --- â --- â
            â f64 â f64 â
            âââââââªââââââ¡
            â 1.0 â 1.0 â
            âââââââŽââââââ
        r\   r  r^   r  r0   r1   r]   g  s    )zLazyFrame.drop_nullsrT   rU   c                   s   t   |¡S )uË  
        Insert column which enumerates rows.

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

            Let's define a dataframe-agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.with_row_index()

            We can then pass either pandas or Polars:

            >>> func(df_pd)
               index  a  b
            0      0  1  4
            1      1  2  5
            2      2  3  6
            >>> func(df_pl).collect()
            shape: (3, 3)
            âââââââââ¬ââââââ¬ââââââ
            â index â a   â b   â
            â ---   â --- â --- â
            â u32   â i64 â i64 â
            âââââââââªââââââªââââââ¡
            â 0     â 1   â 4   â
            â 1     â 2   â 5   â
            â 2     â 3   â 6   â
            âââââââââŽââââââŽââââââ
        r	  rX   r  r0   r1   rW     s    %zLazyFrame.with_row_indexr   c                   s   t  jS )a0  
        Get an ordered mapping of column names to their data type.

        Examples:
            >>> import polars as pl
            >>> import narwhals as nw
            >>> lf_pl = pl.LazyFrame(
            ...     {
            ...         "foo": [1, 2, 3],
            ...         "bar": [6.0, 7.0, 8.0],
            ...         "ham": ["a", "b", "c"],
            ...     }
            ... )
            >>> lf = nw.from_native(lf_pl)
            >>> lf.schema  # doctest: +SKIP
            Schema({'foo': Int64, 'bar': Float64, 'ham', String})
        r
  r/   r  r0   r1   rL   ¹  s    zLazyFrame.schemar*   c                   s
   t   ¡ S )a(  
        Get an ordered mapping of column names to their data type.

        Examples:
            >>> import polars as pl
            >>> import narwhals as nw
            >>> lf_pl = pl.LazyFrame(
            ...     {
            ...         "foo": [1, 2, 3],
            ...         "bar": [6.0, 7.0, 8.0],
            ...         "ham": ["a", "b", "c"],
            ...     }
            ... )
            >>> lf = nw.from_native(lf_pl)
            >>> lf.collect_schema()
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
        r  r/   r  r0   r1   rN   Î  s    zLazyFrame.collect_schemar_   c                   s   t  jS )a¶  
        Get column names.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(df)
            >>> lf_pl = pl.LazyFrame(df)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.columns

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

            >>> func(df_pd)
            ['foo', 'bar', 'ham']
            >>> func(lf_pl)  # doctest: +SKIP
            ['foo', 'bar', 'ham']
        r  r/   r  r0   r1   r`   â  s    zLazyFrame.columnsra   r!   rb   c                   s   t  j||S )u%  
        Add columns to this LazyFrame.

        Added columns will replace existing columns with the same name.

        Arguments:
            *exprs: Column(s) to add, specified as positional arguments.
                     Accepts expression input. Strings are parsed as column names, other
                     non-expression inputs are parsed as literals.

            **named_exprs: Additional columns to add, specified as keyword arguments.
                            The columns will be renamed to the keyword used.

        Returns:
            LazyFrame: A new LazyFrame with the columns added.

        Note:
            Creating a new LazyFrame using this method does not create a new copy of
            existing data.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {
            ...     "a": [1, 2, 3, 4],
            ...     "b": [0.5, 4, 10, 13],
            ...     "c": [True, True, False, True],
            ... }
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)
            >>> lf_pl = pl.LazyFrame(df)

            Let's define a dataframe-agnostic function in which we pass an expression
            to add it as a new column:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.with_columns((nw.col("a") * 2).alias("2a"))

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

            >>> func(df_pd)
               a     b      c  2a
            0  1   0.5   True   2
            1  2   4.0   True   4
            2  3  10.0  False   6
            3  4  13.0   True   8
            >>> func(df_pl)
            shape: (4, 4)
            âââââââ¬âââââââ¬ââââââââ¬ââââââ
            â a   â b    â c     â 2a  â
            â --- â ---  â ---   â --- â
            â i64 â f64  â bool  â i64 â
            âââââââªâââââââªââââââââªââââââ¡
            â 1   â 0.5  â true  â 2   â
            â 2   â 4.0  â true  â 4   â
            â 3   â 10.0 â false â 6   â
            â 4   â 13.0 â true  â 8   â
            âââââââŽâââââââŽââââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (4, 4)
            âââââââ¬âââââââ¬ââââââââ¬ââââââ
            â a   â b    â c     â 2a  â
            â --- â ---  â ---   â --- â
            â i64 â f64  â bool  â i64 â
            âââââââªâââââââªââââââââªââââââ¡
            â 1   â 0.5  â true  â 2   â
            â 2   â 4.0  â true  â 4   â
            â 3   â 10.0 â false â 6   â
            â 4   â 13.0 â true  â 8   â
            âââââââŽâââââââŽââââââââŽââââââ
        r  rf   r  r0   r1   re   þ  s    LzLazyFrame.with_columnsc                   s   t  j||S )uí  
        Select columns from this LazyFrame.

        Arguments:
            *exprs: Column(s) to select, specified as positional arguments.
                Accepts expression input. Strings are parsed as column names.
            **named_exprs: Additional columns to select, specified as keyword arguments.
                The columns will be renamed to the keyword used.

        Notes:
            If you'd like to select a column whose name isn't a string (for example,
            if you're working with pandas) then you should explicitly use `nw.col` instead
            of just passing the column name. For example, to select a column named
            `0` use `df.select(nw.col(0))`, not `df.select(0)`.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6, 7, 8],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)
            >>> lf_pl = pl.LazyFrame(df)

            Let's define a dataframe-agnostic function in which we pass the name of a
            column to select that column.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.select("foo")

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

            >>> func(df_pd)
               foo
            0    1
            1    2
            2    3
            >>> func(df_pl)
            shape: (3, 1)
            âââââââ
            â foo â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ
            >>> func(lf_pl).collect()
            shape: (3, 1)
            âââââââ
            â foo â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ

            Multiple columns can be selected by passing a list of column names.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.select(["foo", "bar"])
            >>> func(df_pd)
               foo  bar
            0    1    6
            1    2    7
            2    3    8
            >>> func(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 6   â
            â 2   â 7   â
            â 3   â 8   â
            âââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 6   â
            â 2   â 7   â
            â 3   â 8   â
            âââââââŽââââââ

            Multiple columns can also be selected using positional arguments instead of a
            list. Expressions are also accepted.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.select(nw.col("foo"), nw.col("bar") + 1)
            >>> func(df_pd)
               foo  bar
            0    1    7
            1    2    8
            2    3    9
            >>> func(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 7   â
            â 2   â 8   â
            â 3   â 9   â
            âââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 7   â
            â 2   â 8   â
            â 3   â 9   â
            âââââââŽââââââ

            Use keyword arguments to easily name your expression inputs.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.select(threshold=nw.col("foo") * 2)
            >>> func(df_pd)
               threshold
            0          2
            1          4
            2          6
            >>> func(df_pl)
            shape: (3, 1)
            âââââââââââââ
            â threshold â
            â ---       â
            â i64       â
            âââââââââââââ¡
            â 2         â
            â 4         â
            â 6         â
            âââââââââââââ
            >>> func(lf_pl).collect()
            shape: (3, 1)
            âââââââââââââ
            â threshold â
            â ---       â
            â i64       â
            âââââââââââââ¡
            â 2         â
            â 4         â
            â 6         â
            âââââââââââââ
        r  rf   r  r0   r1   rg   L  s     +zLazyFrame.selectrh   ri   c                   s   t   |¡S )uÂ  
        Rename column names.

        Arguments:
            mapping: Key value pairs that map from old name to new name, or a
                      function that takes the old name as input and returns the
                      new name.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> data = {"foo": [1, 2, 3], "bar": [6, 7, 8], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(data)
            >>> lf_pl = pl.LazyFrame(data)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.rename({"foo": "apple"})

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

            >>> func(df_pd)
               apple  bar ham
            0      1    6   a
            1      2    7   b
            2      3    8   c
            >>> func(lf_pl).collect()
            shape: (3, 3)
            âââââââââ¬ââââââ¬ââââââ
            â apple â bar â ham â
            â ---   â --- â --- â
            â i64   â i64 â str â
            âââââââââªââââââªââââââ¡
            â 1     â 6   â a   â
            â 2     â 7   â b   â
            â 3     â 8   â c   â
            âââââââââŽââââââŽââââââ
        r  rl   r  r0   r1   rk   ø  s    *zLazyFrame.renamer  rm   rn   c                   s   t   |¡S )u  
        Get the first `n` rows.

        Arguments:
            n: Number of rows to return.

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data = {
            ...     "a": [1, 2, 3, 4, 5, 6],
            ...     "b": [7, 8, 9, 10, 11, 12],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> lf_pl = pl.LazyFrame(data)

            Let's define a dataframe-agnostic function that gets the first 3 rows.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.head(3)

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

            >>> func(df_pd)
               a  b
            0  1  7
            1  2  8
            2  3  9
            >>> func(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 7   â
            â 2   â 8   â
            â 3   â 9   â
            âââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 7   â
            â 2   â 8   â
            â 3   â 9   â
            âââââââŽââââââ
        r  rq   r  r0   r1   rp   $  s    7zLazyFrame.headc                   s   t   |¡S )u  
        Get the last `n` rows.

        Arguments:
            n: Number of rows to return.

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data = {
            ...     "a": [1, 2, 3, 4, 5, 6],
            ...     "b": [7, 8, 9, 10, 11, 12],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> lf_pl = pl.LazyFrame(data)

            Let's define a dataframe-agnostic function that gets the last 3 rows.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.tail(3)

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

            >>> func(df_pd)
               a   b
            3  4  10
            4  5  11
            5  6  12
            >>> func(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 4   â 10  â
            â 5   â 11  â
            â 6   â 12  â
            âââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 4   â 10  â
            â 5   â 11  â
            â 6   â 12  â
            âââââââŽââââââ
        r  rq   r  r0   r1   rr   ]  s    7zLazyFrame.tailTrv   r   rs   rt   c                  s   t  jt|d|iS )uP  
        Remove columns from the LazyFrame.

        Arguments:
            *columns: Names of the columns that should be removed from the dataframe.
            strict: Validate that all column names exist in the schema and throw an
                exception if a column name does not exist in the schema.

        Warning:
            `strict` argument is ignored for `polars<1.0.0`.

            Please consider upgrading to a newer version or pass to eager mode.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> data = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(data)
            >>> lf_pl = pl.LazyFrame(data)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.drop("ham")

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

            >>> func(df_pd)
               foo  bar
            0    1  6.0
            1    2  7.0
            2    3  8.0
            >>> func(lf_pl).collect()
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â f64 â
            âââââââªââââââ¡
            â 1   â 6.0 â
            â 2   â 7.0 â
            â 3   â 8.0 â
            âââââââŽââââââ

            Use positional arguments to drop multiple columns.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.drop("foo", "ham")

            >>> func(df_pd)
               bar
            0  6.0
            1  7.0
            2  8.0
            >>> func(lf_pl).collect()
            shape: (3, 1)
            âââââââ
            â bar â
            â --- â
            â f64 â
            âââââââ¡
            â 6.0 â
            â 7.0 â
            â 8.0 â
            âââââââ
        ru   r  rx   r  r0   r1   rw     s    FzLazyFrame.dropry   Frz   r}   r~   c                  s   t  j|||dS )u  
        Drop duplicate rows from this LazyFrame.

        Arguments:
            subset: Column name(s) to consider when identifying duplicate rows.
                     If set to `None`, use all columns.
            keep: {'first', 'last', 'any', 'none'}
                Which of the duplicate rows to keep.

                * 'any': Does not give any guarantee of which row is kept.
                        This allows more optimizations.
                * 'none': Don't keep duplicate rows.
                * 'first': Keep first unique row.
                * 'last': Keep last unique row.
            maintain_order: Keep the same order as the original DataFrame. This may be more
                expensive to compute. Settings this to `True` blocks the possibility
                to run on the streaming engine for Polars.

        Returns:
            LazyFrame: LazyFrame with unique rows.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> data = {
            ...     "foo": [1, 2, 3, 1],
            ...     "bar": ["a", "a", "a", "a"],
            ...     "ham": ["b", "b", "b", "b"],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> lf_pl = pl.LazyFrame(data)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.unique(["bar", "ham"])

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

            >>> func(df_pd)
               foo bar ham
            0    1   a   b
            >>> func(lf_pl).collect()
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â str â str â
            âââââââªââââââªââââââ¡
            â 1   â a   â b   â
            âââââââŽââââââŽââââââ
        rz   r  r   r  r0   r1   r   Þ  s    =zLazyFrame.uniquer   r   c                   s   t  j| S )u&  
        Filter the rows in the LazyFrame based on a predicate expression.

        The original order of the remaining rows is preserved.

        Arguments:
            *predicates: Expression that evaluates to a boolean Series. Can
                also be a (single!) boolean list.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6, 7, 8],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> lf_pl = pl.LazyFrame(data)

            Let's define a dataframe-agnostic function in which we filter on
            one condition.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.filter(nw.col("foo") > 1)

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

            >>> func(df_pd)
               foo  bar ham
            1    2    7   b
            2    3    8   c
            >>> func(df_pl)
            shape: (2, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 2   â 7   â b   â
            â 3   â 8   â c   â
            âââââââŽââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (2, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 2   â 7   â b   â
            â 3   â 8   â c   â
            âââââââŽââââââŽââââââ

            Filter on multiple conditions:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.filter((nw.col("foo") < 3) & (nw.col("ham") == "a"))
            >>> func(df_pd)
               foo  bar ham
            0    1    6   a
            >>> func(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            âââââââŽââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            âââââââŽââââââŽââââââ

            Provide multiple filters using `*args` syntax:

            >>> @nw.narwhalify
            ... def func(df):
            ...     dframe = df.filter(
            ...         nw.col("foo") == 1,
            ...         nw.col("ham") == "a",
            ...     )
            ...     return dframe
            >>> func(df_pd)
               foo  bar ham
            0    1    6   a
            >>> func(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            âââââââŽââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            âââââââŽââââââŽââââââ

            Filter on an OR condition:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.filter((nw.col("foo") == 1) | (nw.col("ham") == "c"))
            >>> func(df_pd)
               foo  bar ham
            0    1    6   a
            2    3    8   c
            >>> func(df_pl)
            shape: (2, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            â 3   â 8   â c   â
            âââââââŽââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (2, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            â 3   â 8   â c   â
            âââââââŽââââââŽââââââ
        r  r  r  r0   r1   r     s     zLazyFrame.filterr  zLazyGroupBy[Self]r!  c                G  s$   ddl m} || ft|d|iS )u  
        Start a group by operation.

        Arguments:
            *keys:
                Column(s) to group by. Accepts expression input. Strings are
                parsed as column names.
            drop_null_keys: if True, then groups where any key is null won't be
                included in the result.

        Examples:
            Group by one column and call `agg` to compute the grouped sum of
            another column.

            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {
            ...     "a": ["a", "b", "a", "b", "c"],
            ...     "b": [1, 2, 1, 3, 3],
            ...     "c": [5, 4, 3, 2, 1],
            ... }
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.DataFrame(df)
            >>> lf_pl = pl.LazyFrame(df)

            Let's define a dataframe-agnostic function in which we group by one column
            and call `agg` to compute the grouped sum of another column.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.group_by("a").agg(nw.col("b").sum()).sort("a")

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

            >>> func(df_pd)
               a  b
            0  a  2
            1  b  5
            2  c  3
            >>> func(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â str â i64 â
            âââââââªââââââ¡
            â a   â 2   â
            â b   â 5   â
            â c   â 3   â
            âââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â str â i64 â
            âââââââªââââââ¡
            â a   â 2   â
            â b   â 5   â
            â c   â 3   â
            âââââââŽââââââ

            Group by multiple columns by passing a list of column names.

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.group_by(["a", "b"]).agg(nw.max("c")).sort(["a", "b"])
            >>> func(df_pd)
               a  b  c
            0  a  1  5
            1  b  2  4
            2  b  3  2
            3  c  3  1
            >>> func(df_pl)
            shape: (4, 3)
            âââââââ¬ââââââ¬ââââââ
            â a   â b   â c   â
            â --- â --- â --- â
            â str â i64 â i64 â
            âââââââªââââââªââââââ¡
            â a   â 1   â 5   â
            â b   â 2   â 4   â
            â b   â 3   â 2   â
            â c   â 3   â 1   â
            âââââââŽââââââŽââââââ
            >>> func(lf_pl).collect()
            shape: (4, 3)
            âââââââ¬ââââââ¬ââââââ
            â a   â b   â c   â
            â --- â --- â --- â
            â str â i64 â i64 â
            âââââââªââââââªââââââ¡
            â a   â 1   â 5   â
            â b   â 2   â 4   â
            â b   â 3   â 2   â
            â c   â 3   â 1   â
            âââââââŽââââââŽââââââ
        r   r   r   )r#  r   r   )r+   r   r"  r   r0   r0   r1   r$  °  s    fzLazyFrame.group_byr   r   r   c                  s   t  j|f|||dS )ue  
        Sort the LazyFrame by the given columns.

        Arguments:
            by: Column(s) names to sort by.
            *more_by: Additional columns to sort by, specified as positional arguments.
            descending: Sort in descending order. When sorting by multiple columns, can be
                specified per column by passing a sequence of booleans.
            nulls_last: Place null values last; can specify a single boolean applying to
                all columns or a sequence of booleans for per-column control.

        Warning:
            Unlike Polars, it is not possible to specify a sequence of booleans for
            `nulls_last` in order to control per-column behaviour. Instead a single
            boolean is applied for all `by` columns.

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data = {
            ...     "a": [1, 2, None],
            ...     "b": [6.0, 5.0, 4.0],
            ...     "c": ["a", "c", "b"],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_lf = pl.LazyFrame(data)

            Let's define a dataframe-agnostic function in which we sort by multiple
            columns in different orders

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.sort("c", "a", descending=[False, True])

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

            >>> func(df_pd)
                 a    b  c
            0  1.0  6.0  a
            2  NaN  4.0  b
            1  2.0  5.0  c
            >>> func(df_lf).collect()
            shape: (3, 3)
            ââââââââ¬ââââââ¬ââââââ
            â a    â b   â c   â
            â ---  â --- â --- â
            â i64  â f64 â str â
            ââââââââªââââââªââââââ¡
            â 1    â 6.0 â a   â
            â null â 4.0 â b   â
            â 2    â 5.0 â c   â
            ââââââââŽââââââŽââââââ
        r   r%  r   r  r0   r1   r     s    =zLazyFrame.sortr   r   r   r   r   c                  s   t  j||||||dS )u	
  
        Add a join operation to the Logical Plan.

        Arguments:
            other: Lazy DataFrame to join with.
            on: Name(s) of the join columns in both DataFrames. If set, `left_on` and
                `right_on` should be None.
            how: Join strategy.

                  * *inner*: Returns rows that have matching values in both tables.
                  * *cross*: Returns the Cartesian product of rows from both tables.
                  * *semi*: Filter rows that have a match in the right table.
                  * *anti*: Filter rows that do not have a match in the right table.
            left_on: Join column of the left DataFrame.
            right_on: Join column of the right DataFrame.
            suffix: Suffix to append to columns with a duplicate name.

        Returns:
            A new joined LazyFrame

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6.0, 7.0, 8.0],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> data_other = {
            ...     "apple": ["x", "y", "z"],
            ...     "ham": ["a", "b", "d"],
            ... }

            >>> df_pd = pd.DataFrame(data)
            >>> other_pd = pd.DataFrame(data_other)

            >>> df_pl = pl.LazyFrame(data)
            >>> other_pl = pl.LazyFrame(data_other)

            Let's define a dataframe-agnostic function in which we join over "ham" column:

            >>> @nw.narwhalify
            ... def join_on_ham(df, other_any):
            ...     return df.join(other_any, left_on="ham", right_on="ham")

            We can now pass either pandas or Polars to the function:

            >>> join_on_ham(df_pd, other_pd)
               foo  bar ham apple
            0    1  6.0   a     x
            1    2  7.0   b     y

            >>> join_on_ham(df_pl, other_pl).collect()
            shape: (2, 4)
            âââââââ¬ââââââ¬ââââââ¬ââââââââ
            â foo â bar â ham â apple â
            â --- â --- â --- â ---   â
            â i64 â f64 â str â str   â
            âââââââªââââââªââââââªââââââââ¡
            â 1   â 6.0 â a   â x     â
            â 2   â 7.0 â b   â y     â
            âââââââŽââââââŽââââââŽââââââââ
        r&  r'  r(  r  r0   r1   r©   Y  s    J     ÿzLazyFrame.joinr°   r±   rµ   r¶   r·   c          	   
     s   t  j||||||||dS )u!  
        Perform an asof join.

        This is similar to a left-join except that we match on nearest key rather than equal keys.

        Both DataFrames must be sorted by the asof_join key.

        Arguments:
            other: DataFrame to join with.

            left_on: Name(s) of the left join column(s).

            right_on: Name(s) of the right join column(s).

            on: Join column of both DataFrames. If set, left_on and right_on should be None.

            by_left: join on these columns before doing asof join

            by_right: join on these columns before doing asof join

            by: join on these columns before doing asof join

            strategy: Join strategy. The default is "backward".

                  * *backward*: selects the last row in the right DataFrame whose "on" key is less than or equal to the left's key.
                  * *forward*: selects the first row in the right DataFrame whose "on" key is greater than or equal to the left's key.
                  * *nearest*: search selects the last row in the right DataFrame whose value is nearest to the left's key.

        Returns:
            A new joined DataFrame

        Examples:
            >>> from datetime import datetime
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data_gdp = {
            ...     "datetime": [
            ...         datetime(2016, 1, 1),
            ...         datetime(2017, 1, 1),
            ...         datetime(2018, 1, 1),
            ...         datetime(2019, 1, 1),
            ...         datetime(2020, 1, 1),
            ...     ],
            ...     "gdp": [4164, 4411, 4566, 4696, 4827],
            ... }
            >>> data_population = {
            ...     "datetime": [
            ...         datetime(2016, 3, 1),
            ...         datetime(2018, 8, 1),
            ...         datetime(2019, 1, 1),
            ...     ],
            ...     "population": [82.19, 82.66, 83.12],
            ... }
            >>> gdp_pd = pd.DataFrame(data_gdp)
            >>> population_pd = pd.DataFrame(data_population)
            >>> gdp_pl = pl.LazyFrame(data_gdp).sort("datetime")
            >>> population_pl = pl.LazyFrame(data_population).sort("datetime")

            Let's define a dataframe-agnostic function in which we join over "datetime" column:

            >>> @nw.narwhalify
            ... def join_asof_datetime(df, other_any, strategy):
            ...     return df.join_asof(other_any, on="datetime", strategy=strategy)

            We can now pass either pandas or Polars to the function:

            >>> join_asof_datetime(population_pd, gdp_pd, strategy="backward")
                datetime  population   gdp
            0 2016-03-01       82.19  4164
            1 2018-08-01       82.66  4566
            2 2019-01-01       83.12  4696

            >>> join_asof_datetime(population_pl, gdp_pl, strategy="backward").collect()
            shape: (3, 3)
            âââââââââââââââââââââââ¬âââââââââââââ¬âââââââ
            â datetime            â population â gdp  â
            â ---                 â ---        â ---  â
            â datetime[ÎŒs]        â f64        â i64  â
            âââââââââââââââââââââââªâââââââââââââªâââââââ¡
            â 2016-03-01 00:00:00 â 82.19      â 4164 â
            â 2018-08-01 00:00:00 â 82.66      â 4566 â
            â 2019-01-01 00:00:00 â 83.12      â 4696 â
            âââââââââââââââââââââââŽâââââââââââââŽâââââââ

            Here is a real-world times-series example that uses `by` argument.

            >>> from datetime import datetime
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data_quotes = {
            ...     "datetime": [
            ...         datetime(2016, 5, 25, 13, 30, 0, 23),
            ...         datetime(2016, 5, 25, 13, 30, 0, 23),
            ...         datetime(2016, 5, 25, 13, 30, 0, 30),
            ...         datetime(2016, 5, 25, 13, 30, 0, 41),
            ...         datetime(2016, 5, 25, 13, 30, 0, 48),
            ...         datetime(2016, 5, 25, 13, 30, 0, 49),
            ...         datetime(2016, 5, 25, 13, 30, 0, 72),
            ...         datetime(2016, 5, 25, 13, 30, 0, 75),
            ...     ],
            ...     "ticker": [
            ...         "GOOG",
            ...         "MSFT",
            ...         "MSFT",
            ...         "MSFT",
            ...         "GOOG",
            ...         "AAPL",
            ...         "GOOG",
            ...         "MSFT",
            ...     ],
            ...     "bid": [720.50, 51.95, 51.97, 51.99, 720.50, 97.99, 720.50, 52.01],
            ...     "ask": [720.93, 51.96, 51.98, 52.00, 720.93, 98.01, 720.88, 52.03],
            ... }
            >>> data_trades = {
            ...     "datetime": [
            ...         datetime(2016, 5, 25, 13, 30, 0, 23),
            ...         datetime(2016, 5, 25, 13, 30, 0, 38),
            ...         datetime(2016, 5, 25, 13, 30, 0, 48),
            ...         datetime(2016, 5, 25, 13, 30, 0, 48),
            ...         datetime(2016, 5, 25, 13, 30, 0, 48),
            ...     ],
            ...     "ticker": ["MSFT", "MSFT", "GOOG", "GOOG", "AAPL"],
            ...     "price": [51.95, 51.95, 720.77, 720.92, 98.0],
            ...     "quantity": [75, 155, 100, 100, 100],
            ... }
            >>> quotes_pd = pd.DataFrame(data_quotes)
            >>> trades_pd = pd.DataFrame(data_trades)
            >>> quotes_pl = pl.LazyFrame(data_quotes).sort("datetime")
            >>> trades_pl = pl.LazyFrame(data_trades).sort("datetime")

            Let's define a dataframe-agnostic function in which we join over "datetime" and by "ticker" columns:

            >>> @nw.narwhalify
            ... def join_asof_datetime_by_ticker(df, other_any):
            ...     return df.join_asof(other_any, on="datetime", by="ticker")

            We can now pass either pandas or Polars to the function:

            >>> join_asof_datetime_by_ticker(trades_pd, quotes_pd)
                                datetime ticker   price  quantity     bid     ask
            0 2016-05-25 13:30:00.000023   MSFT   51.95        75   51.95   51.96
            1 2016-05-25 13:30:00.000038   MSFT   51.95       155   51.97   51.98
            2 2016-05-25 13:30:00.000048   GOOG  720.77       100  720.50  720.93
            3 2016-05-25 13:30:00.000048   GOOG  720.92       100  720.50  720.93
            4 2016-05-25 13:30:00.000048   AAPL   98.00       100     NaN     NaN

            >>> join_asof_datetime_by_ticker(trades_pl, quotes_pl).collect()
            shape: (5, 6)
            ââââââââââââââââââââââââââââââ¬âââââââââ¬âââââââââ¬âââââââââââ¬ââââââââ¬âââââââââ
            â datetime                   â ticker â price  â quantity â bid   â ask    â
            â ---                        â ---    â ---    â ---      â ---   â ---    â
            â datetime[ÎŒs]               â str    â f64    â i64      â f64   â f64    â
            ââââââââââââââââââââââââââââââªâââââââââªâââââââââªâââââââââââªââââââââªâââââââââ¡
            â 2016-05-25 13:30:00.000023 â MSFT   â 51.95  â 75       â 51.95 â 51.96  â
            â 2016-05-25 13:30:00.000038 â MSFT   â 51.95  â 155      â 51.97 â 51.98  â
            â 2016-05-25 13:30:00.000048 â GOOG   â 720.77 â 100      â 720.5 â 720.93 â
            â 2016-05-25 13:30:00.000048 â GOOG   â 720.92 â 100      â 720.5 â 720.93 â
            â 2016-05-25 13:30:00.000048 â AAPL   â 98.0   â 100      â null  â null   â
            ââââââââââââââââââââââââââââââŽâââââââââŽâââââââââŽâââââââââââŽââââââââŽâââââââââ
        r±   r)  r*  r  r0   r1   rž   §  s     /øzLazyFrame.join_asofc                   s
   t   ¡ S )uÍ  
        Create a copy of this DataFrame.

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

            Let's define a dataframe-agnostic function in which we copy the DataFrame:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.clone()

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

            >>> func(df_pl).collect()
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 3   â
            â 2   â 4   â
            âââââââŽââââââ
        r0  r/   r  r0   r1   rª   `  s    "zLazyFrame.clonec                 C  s   | S )a  
        Lazify the DataFrame (if possible).

        If a library does not support lazy execution, then this is a no-op.

        Examples:
            Construct pandas and Polars objects:

            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> df = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(df)
            >>> df_pl = pl.LazyFrame(df)

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.lazy()

            Note that then, pandas dataframe stay eager, and the Polars LazyFrame stays lazy:

            >>> func(df_pd)
               foo  bar ham
            0    1  6.0   a
            1    2  7.0   b
            2    3  8.0   c
            >>> func(df_pl)
            <LazyFrame ...>
        r0   r/   r0   r0   r1   rë     s     zLazyFrame.lazyr   r«   c                   s   t  j||dS )u  
        Take every nth row in the DataFrame and return as a new DataFrame.

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

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data = {"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}
            >>> df_pd = pd.DataFrame(data)
            >>> lf_pl = pl.LazyFrame(data)

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

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.gather_every(n=2, offset=1)

            >>> func(df_pd)
               a  b
            1  2  6
            3  4  8

            >>> func(lf_pl).collect()
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 2   â 6   â
            â 4   â 8   â
            âââââââŽââââââ
        r­   r1  r¯   r  r0   r1   r®   Š  s    'zLazyFrame.gather_everyr6  r¹   c                  s   t  j||||dS )u+	  
        Unpivot a DataFrame from wide to long format.

        Optionally leaves identifiers set.

        This function is useful to massage a DataFrame into a format where one or more
        columns are identifier variables (index) while all other columns, considered
        measured variables (on), are "unpivoted" to the row axis leaving just
        two non-identifier columns, 'variable' and 'value'.

        Arguments:
            on: Column(s) to use as values variables; if `on` is empty all columns that
                are not in `index` will be used.
            index: Column(s) to use as identifier variables.
            variable_name: Name to give to the `variable` column. Defaults to "variable".
            value_name: Name to give to the `value` column. Defaults to "value".

        Notes:
            If you're coming from pandas, this is similar to `pandas.DataFrame.melt`,
            but with `index` replacing `id_vars` and `on` replacing `value_vars`.
            In other frameworks, you might know this operation as `pivot_longer`.

        Examples:
            >>> import narwhals as nw
            >>> import polars as pl
            >>> data = {
            ...     "a": ["x", "y", "z"],
            ...     "b": [1, 3, 5],
            ...     "c": [2, 4, 6],
            ... }

            We define a library agnostic function:

            >>> @nw.narwhalify
            ... def func(lf):
            ...     return (
            ...         lf.unpivot(on=["b", "c"], index="a").sort(["variable", "a"]).collect()
            ...     )

            >>> func(pl.LazyFrame(data))
            shape: (6, 3)
            âââââââ¬âââââââââââ¬ââââââââ
            â a   â variable â value â
            â --- â ---      â ---   â
            â str â str      â i64   â
            âââââââªâââââââââââªââââââââ¡
            â x   â b        â 1     â
            â y   â b        â 3     â
            â z   â b        â 5     â
            â x   â c        â 2     â
            â y   â c        â 4     â
            â z   â c        â 6     â
            âââââââŽâââââââââââŽââââââââ
        rŒ   r7  rŸ   r  r0   r1   rœ   Ï  s    >   ÿzLazyFrame.unpivot)N)rT   )r  )r  )N)Nr   )r   )N)"r¿   rÀ   rÁ   r8  rÃ   r:  rÎ   rß   r÷   r<  r   rS   r]   rW   rL   rN   r`   re   rg   rk   rp   rr   rw   r   r   r$  r   r©   rž   rª   rë   r®   rœ   r9  r0   r0   r  r1   rÆ   Â
  sx   	)('+'N -,99J þû? ÿnûB  üø"Rö& :$"+ þúrÆ   )5Ú
__future__r   Útypingr   r   r   r   r   r   r	   r
   r   r   r   Znarwhals.dependenciesr   r   Znarwhals.schemar   Znarwhals.translater   Znarwhals.utilsr   r   r   Úior   Úpathlibr   Útypesr   ZnumpyÚnpZpandasÚpdrå   ré   Ztyping_extensionsr   r#  r   r   rF   r   Znarwhals.typingr    r!   r"   r#   r%   r&   rÄ   rÆ   r0   r0   r0   r1   Ú<module>   st                        