Ë
    TÇ.h;ž ã                  ó   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rTd 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/ d dl,m0Z0 d d lm1Z1  ed!d"¬#«      Z2 ed$d%¬#«      Z3 G d& d'ee2   «      Z4 G d( d)e4e3   «      Z5 G d* d+e4e2   «      Z6y),é    )Ú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)ÚSizeUnit)ÚImplementationÚFrameTr#   )ÚboundÚ
DataFrameTr!   c                  óâ   e Zd ZU ded<   ded<   d)dZd*dZd+dZd,dZd-d	Ze	d.d
«       Z
d.dZd/dZd0d1dZd2d3dZe	d4d«       Z	 	 	 	 	 	 d5dZ	 	 	 	 	 	 d5dZd6dZd7dZd7dZd8dZ	 d2ddd	 	 	 	 	 	 	 d9dZ	 	 	 	 	 	 d:dZddd	 	 	 	 	 	 	 	 	 d;dZ	 	 d<dddd	 	 	 	 	 	 	 	 	 	 	 	 	 d=d Zd>d!Zd?d@d"Zddddddd#d$	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dAd%Z	 	 	 	 	 	 	 	 	 	 	 	 dBd&ZdCd'ZdDd(Zy)EÚ	BaseFramer   Ú_compliant_frameú&Literal['full', 'lazy', 'interchange']Ú_levelc                ó6    | j                   j                  «       S ©N)r+   Ú__native_namespace__©Úselfs    úf/var/www/html/School_Mangement_New/src/backend/venv/lib/python3.12/site-packages/narwhals/dataframe.pyr0   zBaseFrame.__native_namespace__2   s    Ø×$Ñ$×9Ñ9Ó;Ð;ó    c                ó6    | j                   j                  «       S r/   )r+   Ú__narwhals_namespace__r1   s    r3   r6   z BaseFrame.__narwhals_namespace__5   s    Ø×$Ñ$×;Ñ;Ó=Ð=r4   c                ó<    | j                  || j                  ¬«      S )N©Úlevel)Ú	__class__r-   )r2   Údfs     r3   Ú_from_compliant_dataframez#BaseFrame._from_compliant_dataframe8   s"    à~~ØØ++ð ó 
ð 	
r4   c                óÒ    t        |«      D cg c]  }| j                  |«       }}|j                  «       D ci c]  \  }}|| j                  |«       }}}||fS c c}w c c}}w )zDProcess `args` and `kwargs`, extracting underlying objects as we go.)r   Ú_extract_compliantÚitems)r2   ÚargsÚkwargsÚvÚks        r3   Ú_flatten_and_extractzBaseFrame._flatten_and_extract?   sd    ä4;žD³MÖBšq×'Ñ'šÕ*ÐBÐBØ<B¿L¹L»N×K±D°A°q!T×,Ñ,šQÓ/Ñ/ÐKÑKØV|Ðùò CùÛKs
   A»A#c                óP   ddl m} ddlm} t	        |t
        «      r|j                  S t	        ||«      r|j                  S t	        ||«      r|j                  | j                  «       «      S t        «       0dt        t        |«      «      v rdt        |«       d}t        |«      |S )Nr   )ÚExprr   ÚpolarszExpected Narwhals object, got: z[.

Perhaps you:
- Forgot a `nw.from_native` somewhere?
- Used `pl.col` instead of `nw.col`?)Únarwhals.exprrF   Únarwhals.seriesr    Ú
isinstancer*   r+   Ú_compliant_seriesÚ_to_compliant_exprr6   r   ÚstrÚtypeÚ	TypeError)r2   ÚargrF   r    Úmsgs        r3   r>   zBaseFrame._extract_compliantE   s    Ý&Ý*äc9Ô%Ø×'Ñ'Ð'Üc6Ô"Ø×(Ñ(Ð(Üc4Ô Ø×)Ñ)š$×*EÑ*EÓ*GÓHÐHÜ<Ð#šŽCŒžS»	³NÑ(Bà1Ž$°s³)°ð =7ð 7ð ô C.Ð Ø
r4   c                ó\    t        | j                  j                  j                  «       «      S r/   )r   r+   Úschemar?   r1   s    r3   rS   zBaseFrame.schemaY   s"    äd×+Ñ+×2Ñ2×8Ñ8Ó:Ó;Ð;r4   c                ó^    t        | j                  j                  «       «      }t        |«      S r/   )Údictr+   Úcollect_schemar   )r2   Únative_schemas     r3   rV   zBaseFrame.collect_schema]   s&    ÜT×2Ñ2×AÑAÓCÓDämÓ$Ð$r4   c                ó     || g|¢­i |€S r/   © )r2   Úfunctionr@   rA   s       r3   ÚpipezBaseFrame.pipeb   s    ÙÐ.tÒ. vÑ.Ð.r4   c                óV    | j                  | j                  j                  |«      «      S r/   )r<   r+   Úwith_row_index©r2   Únames     r3   r]   zBaseFrame.with_row_indexe   s)    Ø×-Ñ-Ø×!Ñ!×0Ñ0°Ó6ó
ð 	
r4   Nc                óX    | j                  | j                  j                  |¬«      «      S )N©Úsubset)r<   r+   Ú
drop_nulls)r2   rb   s     r3   rc   zBaseFrame.drop_nullsj   s,    Ø×-Ñ-Ø×!Ñ!×,Ñ,°FÐ,Ó;ó
ð 	
r4   c                ó.    | j                   j                  S r/   )r+   Úcolumnsr1   s    r3   re   zBaseFrame.columnso   s    à×$Ñ$×,Ñ,Ð,r4   c                ó     | j                   |i |€\  }}| j                   | j                  j                  |i |€«      S r/   )rD   r<   r+   Úwith_columns©r2   ÚexprsÚnamed_exprss      r3   rg   zBaseFrame.with_columnss   sN    ð 7T×6Ñ6žÐMÀÑMÑ{Ø×-Ñ-Ø.D×!Ñ!×.Ñ.°ÐEžÑEó
ð 	
r4   c                ó     | j                   |i |€\  }}| j                   | j                  j                  |i |€«      S r/   )rD   r<   r+   Úselectrh   s      r3   rl   zBaseFrame.select{   sN    ð
 7T×6Ñ6žÐMÀÑMÑ{Ø×-Ñ-Ø(D×!Ñ!×(Ñ(š%Ð?°;Ñ?ó
ð 	
r4   c                óV    | j                  | j                  j                  |«      «      S r/   )r<   r+   Úrename)r2   Úmappings     r3   rn   zBaseFrame.rename   s$    Ø×-Ñ-šd×.CÑ.C×.JÑ.JÈ7Ó.SÓTÐTr4   c                óV    | j                  | j                  j                  |«      «      S r/   )r<   r+   Úhead©r2   Úns     r3   rq   zBaseFrame.head   ó$    Ø×-Ñ-šd×.CÑ.C×.HÑ.HÈÓ.KÓLÐLr4   c                óV    | j                  | j                  j                  |«      «      S r/   )r<   r+   Útailrr   s     r3   rv   zBaseFrame.tail   rt   r4   c               óZ    | j                  | j                  j                  ||¬«      «      S )N©Ústrict)r<   r+   Údrop)r2   ry   re   s      r3   rz   zBaseFrame.drop   s.    Ø×-Ñ-Ø×!Ñ!×&Ñ& w°vÐ&Ó>ó
ð 	
r4   ÚanyF©ÚkeepÚmaintain_orderc               ó\    | j                  | j                  j                  |||¬«      «      S )N)rb   r}   r~   )r<   r+   Úunique)r2   rb   r}   r~   s       r3   r   zBaseFrame.unique   s7    ð ×-Ñ-Ø×!Ñ!×(Ñ(Ø Džð )ó ó
ð 	
r4   c                óî    t        |«      dk(  r(t        |d   t        «      rt        d |d   D «       «      s | j                  |i |€\  }}| j                   | j                  j                  |i |€«      S )Né   r   c              3  ó<   K   | ]  }t        |t        «        y ­wr/   )rJ   Úbool)Ú.0Úxs     r3   ú	<genexpr>z#BaseFrame.filter.<locals>.<genexpr>Š   s   è ø Ò?šAJq€$×'Ñ?ùs   )ÚlenrJ   ÚlistÚallrD   r<   r+   Úfilter)r2   Ú
predicatesÚconstraintss      r3   r   zBaseFrame.filter    s    ô 
OqÒ Ü: a=¬$Ô/ÜÑ?°žA±Ô?Ô?à&? d×&?Ñ&?Øð'Ø*ñ'Ñ#Jð ×-Ñ-Ø(D×!Ñ!×(Ñ(š*ÐDžÑDó
ð 	
r4   ©Ú
descendingÚ
nulls_lastc               ób    | j                   | j                  j                  |g|¢­||d«      S )Nr   )r<   r+   Úsort)r2   Úbyr   r   Úmore_bys        r3   r   zBaseFrame.sort¯   sB    ð ×-Ñ-Ø&D×!Ñ!×&Ñ&ØðØñØ)3À
òó
ð 	
r4   Ú_right©Úleft_onÚright_onÚsuffixc          	     ó^   d}||vrd| d| d}t        |«      |dk(  r|||d}t        |«      |dk7  r|||d| d}t        |«      |dk7  r|||d	| d}t        |«      ||x}}| j                  | j                  j	                  | j                  |«      ||||¬
«      «      S )N)ÚinnerÚleftÚcrossÚ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 )Úhowr   r   r   )ÚNotImplementedErrorÚ
ValueErrorr<   r+   Újoinr>   )	r2   ÚotherÚonr£   r   r   r   Ú_supported_joinsrQ   s	            r3   rŠ   zBaseFrame.joinŒ   s   ð FÐàÐ&Ñ&ØFÐGWÐFXÐXaÐbeÐafÐfhÐiCÜ% cÓ*Ð*à'>ØÐ 8Ð#7ž2ž>àRCÜS/Ð!à'>rzšwšÀ(ÐBRØ[Ð\_Ð[`Ð`aÐbCÜS/Ð!à'>ØN Ð 3°xÐ7KàVÐWZÐV[Ð[\Ð]CÜS/Ð!à>Ø!#Ð#Ghà×-Ñ-Ø×!Ñ!×&Ñ&Ø×'Ñ'šÓ.ØØØ!Øð 'ó ó
ð 	
r4   c                óT    | j                  | j                  j                  «       «      S r/   )r<   r+   Úcloner1   s    r3   r«   zBaseFrame.cloneé   s"    Ø×-Ñ-šd×.CÑ.C×.IÑ.IÓ.KÓLÐLr4   c                óZ    | j                  | j                  j                  ||¬«      «      S )N©rs   Úoffset)r<   r+   Úgather_every)r2   rs   r®   s      r3   r¯   zBaseFrame.gather_everyì   s.    Ø×-Ñ-Ø×!Ñ!×.Ñ.°ž6Ð.ÓBó
ð 	
r4   Úbackward©r   r   rš   Úby_leftÚby_rightr   Ústrategyc               óØ   d}	||	vrd|	 d| d}
t        |
«      |||d}
t        |
«      |||d}
t        |
«      |||||d}
t        |
«      |||d}
t        |
«      |?| j                  | j                  j	                  | j                  |«      |||||¬	«      «      S | j                  | j                  j	                  | j                  |«      ||||||¬
«      «      S )N)r°   ÚforwardÚ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¥   r<   r+   Ú	join_asofr>   )r2   r§   r   r   rš   r²   r³   r   rŽ   Ú_supported_strategiesrQ   s              r3   rž   zBaseFrame.join_asofñ   s_   ð !CÐàÐ0Ñ0ØAÐBWÐAXÐXaÐbjÐakÐkmÐnCÜ% cÓ*Ð*àJW_°Ð0@ØWCÜS/Ð!ØN Ð!4žÐ8LØRCÜS/Ð!ØJØ_ Ð!5ØÐ#šÐ(8ð Zð ô S/Ð!ØN Ð!4žÐ8LØRCÜS/Ð!Ø>Ø×1Ñ1Ø×%Ñ%×/Ñ/Ø×+Ñ+šEÓ2ØØ#Ø%ØØ%ð 0ó ó	ð 	ð ×-Ñ-Ø×!Ñ!×+Ñ+Ø×'Ñ'šÓ.ØØ!ØØ!ØØ!ð ,ó ó

ð 
	
r4   c               ó^    | j                  | j                  j                  ||||¬«      «      S )N©rš   ÚindexÚvariable_nameÚ
value_name)r<   r+   Úunpivot)r2   rš   rŒ   rœ   rŸ   s        r3   r¿   zBaseFrame.unpivot+  s<    ð ×-Ñ-Ø×!Ñ!×)Ñ)ØØØ+Ø%ð	 *ó ó
ð 	
r4   c                ó    d}t        |«      )Nz«DataFrame.__neq__ and LazyFrame.__neq__ are not implemented, please use expressions instead.

Hint: instead of
    df != 0
you may want to use
    df.select(nw.all() != 0)©r€   ©r2   r§   rQ   s      r3   Ú__neq__zBaseFrame.__neq__<  ó    ð+ð 	ô " #Ó&Ð&r4   c                ó    d}t        |«      )Nz©DataFrame.__eq__ and LazyFrame.__eq__ are not implemented, please use expressions instead.

Hint: instead of
    df == 0
you may want to use
    df.select(nw.all() == 0)rÁ   rÂ   s      r3   Ú__eq__zBaseFrame.__eq__G  rÄ   r4   )r2   r   Úreturnr   ©rÇ   r   )r;   r   rÇ   r   )r@   r   rA   r   rÇ   r   )rP   r   rÇ   r   ©rÇ   r   ©rZ   zCallable[[Any], Self]r@   r   rA   r   rÇ   r   ©rŒ   ©r_   rM   rÇ   r   r/   ©r2   r   rb   ústr | list[str] | NonerÇ   r   ©rÇ   z	list[str]©ri   zIntoExpr | Iterable[IntoExpr]rj   r"   rÇ   r   ©ro   zdict[str, str]rÇ   r   ©rs   ÚintrÇ   r   )re   zIterable[str]ry   r   rÇ   r   ©rb   rÎ   r}   z'Literal['any', 'first', 'last', 'none']r~   r   rÇ   r   ©r   z*IntoExpr | Iterable[IntoExpr] | list[bool]r   r   rÇ   r   ©
r   ústr | Iterable[str]r   rM   r   zbool | Sequence[bool]r   r   rÇ   r   ©Nr   ©r§   r   rš   rÎ   r£   z1Literal['inner', 'left', 'cross', 'semi', 'anti']r   rÎ   r   rÎ   r   rM   rÇ   r   ©rÇ   r   ©r   ©r2   r   rs   rÓ   r®   rÓ   rÇ   r   ©r§   r   r   ú
str | Noner   rÞ   rš   rÞ   r²   rÎ   r³   rÎ   r   rÎ   rŽ   z)Literal['backward', 'forward', 'nearest']rÇ   r   ©r2   r   rš   rÎ   rŒ   rÎ   rœ   rÞ   rŸ   rÞ   rÇ   r   )r§   r   rÇ   r   )r§   ÚobjectrÇ   r   ) Ú__name__Ú
__module__Ú__qualname__Ú__annotations__r0   r6   r<   rD   r>   ÚpropertyrS   rV   r[   r]   rc   re   rg   rl   rn   rq   rv   rz   r   r   r   rŠ   r«   r¯   rž   r¿   rÃ   rÆ   rY   r4   r3   r*   r*   .   sx   ØÓØ2Ó2ó<ó>ó
óóð( ò<ó ð<ó%ó
/ô
ô

ð
 ò-ó ð-ð
Ø3ð
ØDLð
à	ó
ð
à-ð
ð  ð
ð 
ó	
óUóMóMó
ð *.ð
ð 9>Ø$ñ
à&ð
ð 6ð	
ð
 ð
ð 
ó
ð
ØEð
ØVYð
à	ó
ð& -2Ø ñ
àð
ð ð
ð *ð	
ð
 ð
ð 
ó
ð  &*ØAHð	+
ð +/Ø+/Øñ+
àð+
ð #ð+
ð ?ð	+
ð (ð+
ð )ð+
ð ð+
ð 
ó+
óZMô
ð #Ø#ØØ*.Ø+/Ø%)Ø>Hñ8
àð8
ð ð	8
ð
 ð8
ð ð8
ð (ð8
ð )ð8
ð #ð8
ð <ð8
ð 
ó8
ðt
Øð
à"ð
ð &ð	
ð
 "ð
ð ð
ð 
ó
ó"	'ô	'r4   r*   c                  óÔ    e Zd ZdZedcd«       Zeddd«       Z	 	 	 	 	 	 dedZedfd«       ZdgdZ	dhdidZ
djd	Zdkdld
ZdmdZdndZdodZdkdpdZdqdZdrdZedsd«       ZdtdZdudvdZedwd«       Zedxd«       Zedyd«       Zedzd«       Zed{d«       Zed|d«       Zed}d«       Zed~d«       Zedd«       Zedd«       Zedd«       Zedd«       Zedd «       Zedd!«       Z	 	 	 	 dd"Zdd#Zed$d%dd&«       Zedd'«       Ze	 	 	 	 dd(«       Zd)d%	 	 	 dd*Zdd+Zd fd,Zdkd fd-Zdd fd.Zed fd/«       Zd fd0Zed fd1«       Z ed2d3	 	 	 dd4«       Z!e	 	 	 	 dd5«       Z!e	 	 	 	 dd6«       Z!d2d3	 	 	 dd7Z!ed$d8	 	 	 	 	 dd9«       Z"ed$d8	 	 	 	 	 dd:«       Z"ed$d8	 	 	 	 	 dd;«       Z"d2d<d=	 	 	 	 	 dd>Z"	 	 	 	 	 	 d fd?Z#	 	 	 	 	 	 d fd@Z$d fdAZ%dd fdBZ&dd fdCZ'd)dDd fdEZ(	 dkdFd2dG	 	 	 	 	 	 	 d fdHZ)	 	 	 	 	 	 d fdIZ*d2dJ	 	 	 	 	 ddKZ+d2d2dL	 	 	 	 	 	 	 	 	 d  fdMZ,	 	 d¡dddNdO	 	 	 	 	 	 	 	 	 	 	 	 	 d¢ fdPZ-dddddddQdR	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d£ fdSZ.d€dTZ/d¥dUZ0d€dVZ1dŠdWZ2dhd§dXZ3dš fdYZ4d©dª fdZZ5dddd)d2d[d\	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d«d]Z6d¬d^Z7	 dkdd2dd_	 	 	 	 	 	 	 	 	 	 	 d­d`Z8	 dkdddda	 	 	 	 	 	 	 	 	 	 	 d® fdbZ9 xZ:S )¯Ú	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`.
    c                ó    ddl m} |S )Nr   r   )rI   r    )r2   r    s     r3   Ú_serieszDataFrame._series\  s
    å*àr4   c                ó    t         S r/   )Ú	LazyFramer1   s    r3   Ú
_lazyframezDataFrame._lazyframeb  ó    äÐr4   c               ó    || _         t        |d«      r|j                  «       | _        y dt	        |«       }t        |«      )NÚ__narwhals_dataframe__zCExpected an object which implements `__narwhals_dataframe__`, got: )r-   Úhasattrrï   r+   rN   ÚAssertionError©r2   r;   r9   rQ   s       r3   Ú__init__zDataFrame.__init__f  sG    ð ?DÜ2Ð/Ô0Ø)+×)BÑ)BÓ)DDÕ!àWÔX\Ð]_ÓX`ÐWaÐbCÜ  Ó%Ð%r4   c                ó.    | j                   j                  S )aó  Return implementation of native frame.

        This can be useful when you need to some special-casing for
        some libraries for features outside of Narwhals' scope - for
        example, when dealing with pandas' Period Dtype.

        Returns:
            Implementation.

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> df_native = pd.DataFrame({"a": [1, 2, 3]})
            >>> df = nw.from_native(df_native)
            >>> df.implementation
            <Implementation.PANDAS: 1>
            >>> df.implementation.is_pandas()
            True
            >>> df.implementation.is_pandas_like()
            True
            >>> df.implementation.is_polars()
            False
        ©r+   Ú_implementationr1   s    r3   ÚimplementationzDataFrame.implementations  s    ð2 ×$Ñ$×4Ñ4Ð4r4   c                ó6    | j                   j                  «       S r/   )r+   Ú__len__r1   s    r3   rù   zDataFrame.__len__  s    Ø×$Ñ$×,Ñ,Ó.Ð.r4   Nc                ó<    | j                   j                  ||¬«      S )N)Úcopy)r+   Ú	__array__)r2   Údtyperû   s      r3   rü   zDataFrame.__array__  s    Ø×$Ñ$×.Ñ.šuž4Ð.Ó@Ð@r4   c                ó^    d}t        |«      }dd|z  z   dz   d| dz   dz   dz   d|z  z   d	z   S )
Nz' Narwhals DataFrame                    õ   âõ   âõ   â
ú|ú|
ú*| Use `.to_native` to see native output |
õ   âõ   â©r   ©r2   ÚheaderÚlengths      r3   Ú__repr__zDataFrame.__repr__  ój    Ø:ÜVàØfnñàñð &oñð <ñ	<ð
 ñð fnñð ñð		
r4   c                ór   | j                   j                  }t        |d«      r|j                  |¬«      S 	 ddl}t        |j                  «      dk  rdt        |«       }t        |«      d| j                  «       }|j                  |¬«      S # t
        $ r}dt        |«       }t        |«      |d}~ww xY w)ah  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__)Úrequested_schemar   NzRPyArrow>=14.0.0 is required for `DataFrame.__arrow_c_stream__` for object of type )é   r   )
r+   Ú_native_framerð   r  ÚpyarrowÚModuleNotFoundErrorrN   r   Ú__version__Úto_arrow)r2   r  Únative_frameÚpaÚexcrQ   Úpa_tables          r3   r  zDataFrame.__arrow_c_stream__¢  sÄ    ð ×,Ñ,×:Ñ:Ü<Ð!5Ô6Ø×2Ñ2ÐDTÐ2ÓUÐUð	4Û ô Ó(š7Ò2ØfÔgkÐlxÓgyÐfzÐ{CÜ% cÓ*°Ð4Ø==?Ø×*Ñ*Ð<LÐ*ÓMÐMøô #ò 	4ØfÔgkÐlxÓgyÐfzÐ{CÜ% cÓ*°Ð3ûð	4ús   ¶B Â	B6ÂB1Â1B6c                óX    | 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.

        Returns:
            A new LazyFrame.

        Examples:
            Construct pandas, Polars and PyArrow DataFrames:

            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> 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 agnostic_lazy(df_native: IntoFrame) -> IntoFrame:
            ...     df = nw.from_native(df_native)
            ...     return df.lazy().to_native()

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

            >>> agnostic_lazy(df_pd)
               foo  bar ham
            0    1  6.0   a
            1    2  7.0   b
            2    3  8.0   c
            >>> agnostic_lazy(df_pl)
            <LazyFrame ...>
            >>> agnostic_lazy(df_pa)
            pyarrow.Table
            foo: int64
            bar: double
            ham: string
            ----
            foo: [[1,2,3]]
            bar: [[6,7,8]]
            ham: [["a","b","c"]]
        Úlazyr8   )rì   r+   r  r1   s    r3   r  zDataFrame.lazy¹  s'    ð^ t×4Ñ4×9Ñ9Ó;À6ÓJÐJr4   c                ó.    | 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  r1   s    r3   r   zDataFrame.to_nativeê  s    ðX ×$Ñ$×2Ñ2Ð2r4   c                ó6    | j                   j                  «       S )a  Convert this DataFrame to a pandas DataFrame.

        Returns:
            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
            >>> from narwhals.typing import IntoDataFrame
            >>>
            >>> 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 agnostic_to_pandas(df_native: IntoDataFrame) -> pd.DataFrame:
            ...     df = nw.from_native(df_native)
            ...     return df.to_pandas()

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

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


        )r+   Ú	to_pandasr1   s    r3   r  zDataFrame.to_pandas  s    ð\ ×$Ñ$×.Ñ.Ó0Ð0r4   c                ó8    | j                   j                  |«      S )a\  Write dataframe to comma-separated values (CSV) file.

        Arguments:
            file: String, path object or file-like object to which the dataframe will be
                written. If None, the resulting csv format is returned as a string.

        Returns:
            String or None.

        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:

            >>> 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©r2   Úfiles     r3   r   zDataFrame.write_csvH  s    ðP ×$Ñ$×.Ñ.štÓ4Ð4r4   c                ó:    | j                   j                  |«       y)a  Write dataframe to parquet file.

        Arguments:
            file: String, path object or file-like object to which the dataframe will be
                written.

        Returns:
            None.

        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!  s     r3   r$  zDataFrame.write_parquetr  s    ðD 	×Ñ×+Ñ+šDÕ1r4   c                ó6    | j                   j                  «       S )a  Convert this DataFrame to a NumPy ndarray.

        Returns:
            A NumPy ndarray array.

        Examples:
            Construct pandas and polars DataFrames:

            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> import numpy as np
            >>> from narwhals.typing import IntoDataFrame
            >>>
            >>> 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:

            >>> def agnostic_to_numpy(df_native: IntoDataFrame) -> np.ndarray:
            ...     df = nw.from_native(df_native)
            ...     return df.to_numpy()

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

            >>> agnostic_to_numpy(df_pd)
            array([[1, 6.5, 'a'],
                   [2, 7.0, 'b'],
                   [3, 8.5, 'c']], dtype=object)
            >>> agnostic_to_numpy(df_pl)
            array([[1, 6.5, 'a'],
                   [2, 7.0, 'b'],
                   [3, 8.5, 'c']], dtype=object)
            >>> agnostic_to_numpy(df_pa)
            array([[1, 6.5, 'a'],
                   [2, 7.0, 'b'],
                   [3, 8.5, 'c']], dtype=object)
        )r+   Úto_numpyr1   s    r3   r&  zDataFrame.to_numpy  s    ðT ×$Ñ$×-Ñ-Ó/Ð/r4   c                ó.    | j                   j                  S )a  Get the shape of the DataFrame.

        Returns:
            The shape of the dataframe as a tuple.

        Examples:
            Construct pandas and polars DataFrames:

            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoDataFrame
            >>>
            >>> 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:

            >>> def agnostic_shape(df_native: IntoDataFrame) -> tuple[int, int]:
            ...     df = nw.from_native(df_native)
            ...     return df.shape

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

            >>> agnostic_shape(df_pd)
            (5, 1)
            >>> agnostic_shape(df_pl)
            (5, 1)
            >>> agnostic_shape(df_pa)
            (5, 1)
        )r+   Úshaper1   s    r3   r(  zDataFrame.shapeÂ  s    ðH ×$Ñ$×*Ñ*Ð*r4   c                ón    | j                  | j                  j                  |«      | j                  ¬«      S )a^  Get a single column by name.

        Arguments:
            name: The column name as a string.

        Returns:
            A Narwhals Series, backed by a native series.

        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
            >>> from narwhals.typing import IntoDataFrame
            >>> from narwhals.typing import IntoSeries
            >>>
            >>> data = {"a": [1, 2], "b": [3, 4]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)

            We define a library agnostic function:

            >>> def agnostic_get_column(df_native: IntoDataFrame) -> IntoSeries:
            ...     df = nw.from_native(df_native)
            ...     name = df.columns[0]
            ...     return df.get_column(name).to_native()

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

            >>> agnostic_get_column(df_pd)
            0    1
            1    2
            Name: a, dtype: int64
            >>> agnostic_get_column(df_pl)  # doctest:+NORMALIZE_WHITESPACE
            shape: (2,)
            Series: 'a' [i64]
            [
                1
                2
            ]
        r8   )ré   r+   Ú
get_columnr-   r^   s     r3   r*  zDataFrame.get_columnè  s6    ð` ||Ø×!Ñ!×,Ñ,šTÓ2Ø++ð ó 
ð 	
r4   c                ó:    | j                   j                  |¬«      S )a  Return an estimation of the total (heap) allocated size of the `DataFrame`.

        Estimated size is given in the specified unit (bytes by default).

        Arguments:
            unit: 'b', 'kb', 'mb', 'gb', 'tb', 'bytes', 'kilobytes', 'megabytes',
                    'gigabytes', or 'terabytes'.

        Returns:
            Integer or Float.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoDataFrameT
            >>> 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)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_estimated_size(df_native: IntoDataFrameT) -> int | float:
            ...     df = nw.from_native(df_native)
            ...     return df.estimated_size()

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

            >>> agnostic_estimated_size(df_pd)
            np.int64(330)
            >>> agnostic_estimated_size(df_pl)
            51
            >>> agnostic_estimated_size(df_pa)
            63
        )Úunit)r+   Úestimated_size)r2   r,  s     r3   r-  zDataFrame.estimated_size  s    ðT ×$Ñ$×3Ñ3žÐ3Ó>Ð>r4   c                 ó    y r/   rY   ©r2   Úitems     r3   Ú__getitem__zDataFrame.__getitem__I  ó    ØFIr4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__K  ó    ØNQr4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__M  r2  r4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__O  ó    ØKNr4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__Q  ó    ØCFr4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__S  r4  r4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__U  r2  r4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__W  r7  r4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__Y  r9  r4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__\  ó    Ø8;r4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem___  s    Ø58r4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__b  r?  r4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__e  s    Ø03r4   c                 ó    y r/   rY   r/  s     r3   r1  zDataFrame.__getitem__h  s    Ø>Ar4   c                óT   t        |t        «      r|g}t        |t        «      rAt        |«      dk(  r3t        |d   t        t        f«      rdt        |«       d}t        |«      t        |t        «      rqt        |«      dk(  rct        |d   «      st        |d   t        «      rB|d   t        d«      k(  r|d   t        d«      k(  r| S | j                  | j                  |   «      S t        |t        «      st        |t        «      r8t        |«      dk(  r*| j                  | j                  |   | j                  ¬«      S t        |«      s*t        |t        «      st        |«      r-|j                  dk(  r| j                  | j                  |   «      S dt        |«       }t        |«      )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'`.

        Returns:
            A Narwhals Series, backed by a native series.

        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
            >>> from narwhals.typing import IntoDataFrame
            >>> from narwhals.typing import IntoSeries
            >>>
            >>> 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:

            >>> def agnostic_slice(df_native: IntoDataFrame) -> IntoSeries:
            ...     df = nw.from_native(df_native)
            ...     return df["a"].to_native()

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

            >>> agnostic_slice(df_pd)
            0    1
            1    2
            Name: a, dtype: int64
            >>> agnostic_slice(df_pl)  # doctest:+NORMALIZE_WHITESPACE
            shape: (2,)
            Series: 'a' [i64]
            [
                1
                2
            ]
            >>> agnostic_slice(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   Nr8   )rJ   rÓ   Útupler   rM   rN   rO   r   Úslicer<   r+   ré   r-   r   Úndim©r2   r0  rQ   s      r3   r1  zDataFrame.__getitem__k  sy   ô| dCÔ Ø6DätUÔ#ÜD	QÜD G€c¬3 ZÔ0ð /¬t°D«zšlð ;3ð 3ð ô
 C.Ð ätUÔ#ÜD	QÜ(šša©Ô1ŽZÀÀQÁÌÔ5OàAw% +Ò%š$šq©'ŽUž4³[Ò*@ØØ×1Ñ1°$×2GÑ2GÈÑ2MÓNÐNÜdCÔ €Z°ŽeÔ%<ÄÀTÃÈaÂØ<<Ø×%Ñ% dÑ+Økkð  ó ð ô $ DÔ)Ü$€Ô&ÜtÔ$š¯©°aªà×1Ñ1°$×2GÑ2GÈÑ2MÓNÐNð 1Ž°d³°Ð=CÜC.Ð r4   c                ó    || j                   v S r/   )re   )r2   Úkeys     r3   Ú__contains__zDataFrame.__contains__ï  s    ØdllÐ"Ð"r4   .©Ú	as_seriesc                ó    y r/   rY   ©r2   rN  s     r3   Úto_dictzDataFrame.to_dictò  s    ØTWr4   c                ó    y r/   rY   rP  s     r3   rQ  zDataFrame.to_dictô  s    ØMPr4   c                ó    y r/   rY   rP  s     r3   rQ  zDataFrame.to_dictö  s    ð 9<r4   Tc          	     óü    |rY| j                   j                  |¬«      j                  «       D ci c]#  \  }}|| j                  || j                  ¬«      % c}}S | j                   j                  |¬«      S c c}}w )a}  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.

        Returns:
            A mapping from column name to values / Series.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoDataFrame
            >>>
            >>> 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:

            >>> def agnostic_to_dict(
            ...     df_native: IntoDataFrame,
            ... ) -> dict[str, list[int | str | float | None]]:
            ...     df = nw.from_native(df_native)
            ...     return df.to_dict(as_series=False)

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

            >>> agnostic_to_dict(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]}
            >>> agnostic_to_dict(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]}
            >>> agnostic_to_dict(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]}
        rM  r8   )r+   rQ  r?   ré   r-   )r2   rN  rK  Úvalues       r3   rQ  zDataFrame.to_dictú  s    ñ^ ð #'×"7Ñ"7×"?Ñ"?Ø'ð #@ó #ç%'÷ñ
 Cð	 T\\ØØ++ð "ó ñ óð ð ×$Ñ$×,Ñ,°yÐ,ÓAÐAùós   °(A8c                ó8    | j                   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
            >>> from narwhals.typing import IntoDataFrame
            >>> from typing import Any
            >>>
            >>> 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.

            >>> def agnostic_row(df_native: IntoDataFrame) -> tuple[Any, ...]:
            ...     df = nw.from_native(df_native)
            ...     return df.row(1)

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

            >>> agnostic_row(df_pd)
            (2, 5)
            >>> agnostic_row(df_pl)
            (2, 5)
        )r+   Úrow)r2   rŒ   s     r3   rW  zDataFrame.row5  s    ðJ ×$Ñ$×(Ñ(šÓ/Ð/r4   c                ó*    t        |   |g|¢­i |€S )uÒ  Pipe function call.

        Examples:
            >>> import polars as pl
            >>> import pandas as pd
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

            >>> def agnostic_pipe(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.pipe(
            ...         lambda _df: _df.select(
            ...             [x for x in _df.columns if len(x) == 1]
            ...         ).to_native()
            ...     )

            We can then pass either pandas or Polars:

            >>> agnostic_pipe(df_pd)
               a
            0  1
            1  2
            2  3
            >>> agnostic_pipe(df_pl)
            shape: (3, 1)
            âââââââ
            â a   â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ
        ©Úsuperr[   ©r2   rZ   r@   rA   r:   s       r3   r[   zDataFrame.pipe]  s   ø ôT w|HÐ6 tÒ6švÑ6Ð6r4   c                ó$    t         |   |¬«      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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

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

            We can then pass either pandas or Polars:

            >>> agnostic_drop_nulls(df_pd)
                 a   ba
            0  1.0  1.0
            >>> agnostic_drop_nulls(df_pl)
            shape: (1, 2)
            âââââââ¬ââââââ
            â a   â ba  â
            â --- â --- â
            â f64 â f64 â
            âââââââªââââââ¡
            â 1.0 â 1.0 â
            âââââââŽââââââ
        ra   ©rZ  rc   ©r2   rb   r:   s     r3   rc   zDataFrame.drop_nulls  ó   ø ôT wÑ!šÐ!Ó0Ð0r4   c                ó"    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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

            >>> def agnostic_with_row_index(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_row_index().to_native()

            We can then pass either pandas or Polars:

            >>> agnostic_with_row_index(df_pd)
               index  a  b
            0      0  1  4
            1      1  2  5
            2      2  3  6
            >>> agnostic_with_row_index(df_pl)
            shape: (3, 3)
            âââââââââ¬ââââââ¬ââââââ
            â index â a   â b   â
            â ---   â --- â --- â
            â u32   â i64 â i64 â
            âââââââââªââââââªââââââ¡
            â 0     â 1   â 4   â
            â 1     â 2   â 5   â
            â 2     â 3   â 6   â
            âââââââââŽââââââŽââââââ
        ©rZ  r]   ©r2   r_   r:   s     r3   r]   zDataFrame.with_row_indexµ  s   ø ôP wÑ% dÓ+Ð+r4   c                ó    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
            >>> from narwhals.schema import Schema
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> 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:

            >>> def agnostic_schema(df_native: IntoFrame) -> Schema:
            ...     df = nw.from_native(df_native)
            ...     return df.schema

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

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

            >>> df_pl_schema = agnostic_schema(df_pl)
            >>> df_pl_schema
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
        ©rZ  rS   ©r2   r:   s    r3   rS   zDataFrame.schemaß  s   ø ôF w~Ðr4   c                ó     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
            >>> from narwhals.schema import Schema
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> 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:

            >>> def agnostic_collect_schema(df_native: IntoFrame) -> Schema:
            ...     df = nw.from_native(df_native)
            ...     return df.collect_schema()

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

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

            >>> df_pl_schema = agnostic_collect_schema(df_pl)
            >>> df_pl_schema
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
        ©rZ  rV   re  s    r3   rV   zDataFrame.collect_schema  s   ø ôD wÑ%Ó'Ð'r4   c                ó    t         |   S )aè  Get column names.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> 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 agnostic_columns(df_native: IntoFrame) -> list[str]:
            ...     df = nw.from_native(df_native)
            ...     return df.columns

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

            >>> agnostic_columns(df_pd)
            ['foo', 'bar', 'ham']
            >>> agnostic_columns(df_pl)
            ['foo', 'bar', 'ham']
            >>> agnostic_columns(df_pa)
            ['foo', 'bar', 'ham']
        ©rZ  re   re  s    r3   re   zDataFrame.columns(  s   ø ô> wÐr4   F©Únamedc                ó    y r/   rY   ©r2   rk  s     r3   ÚrowszDataFrame.rowsI  s    ð
 !$r4   c                ó    y r/   rY   rm  s     r3   rn  zDataFrame.rowsO  s    ð
  #r4   c                ó    y r/   rY   rm  s     r3   rn  zDataFrame.rowsU  s    ð
 8;r4   c               ó:    | j                   j                  |¬«      S )a^  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'}]
        rj  )r+   rn  rm  s     r3   rn  zDataFrame.rows\  s    ðJ ×$Ñ$×)Ñ)°Ð)Ó6Ð6r4   )Úbuffer_sizec                ó    y r/   rY   ©r2   rk  rr  s      r3   Ú	iter_rowszDataFrame.iter_rows  s    ð %(r4   c                ó    y r/   rY   rt  s      r3   ru  zDataFrame.iter_rows  s    ð $'r4   c                ó    y r/   rY   rt  s      r3   ru  zDataFrame.iter_rows  s	    ð @Cr4   i   ©rk  rr  c               ó<    | j                   j                  ||¬«      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'}]
        rx  )r+   ru  rt  s      r3   ru  zDataFrame.iter_rows  s!    ðR ×$Ñ$×.Ñ.°UÈÐ.ÓTÐTr4   c                ó"    t        |   |i |€S )u1	  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   â
            âââââââŽâââââââŽââââââââŽââââââ
        ©rZ  rg   ©r2   ri   rj   r:   s      r3   rg   zDataFrame.with_columnsœ  s   ø ô| wÑ# UÐ:škÑ:Ð:r4   c                ó"    t        |   |i |€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         â
            âââââââââââââ
        ©rZ  rl   r|  s      r3   rl   zDataFrame.selectý  s   ø ôp w~uÐ4šÑ4Ð4r4   c                ó"    t         |   |«      S )u4  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   â
            âââââââââŽââââââŽââââââ
        ©rZ  rn   ©r2   ro   r:   s     r3   rn   zDataFrame.renamew  s   ø ôN w~gÓ&Ð&r4   c                ó"    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   â
            âââââââŽââââââŽââââââ
        ©rZ  rq   ©r2   rs   r:   s     r3   rq   zDataFrame.head   ó   ø ôX w|AÐr4   c                ó"    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   â
            âââââââŽââââââŽââââââ
        ©rZ  rv   r  s     r3   rv   zDataFrame.tailÎ  r  r4   rx   c               ó4    t        |   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 â
            âââââââ
        ry   ©rZ  rz   r   ©r2   ry   re   r:   s      r3   rz   zDataFrame.dropü  s   ø ô@ w|W WÓ-Ð=°fÑ=Ð=r4   r{   r|   c               ó(    t         |   |||¬«      S )ut  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   â
            âââââââŽââââââŽââââââ
        r|   ©rZ  r   ©r2   rb   r}   r~   r:   s       r3   r   zDataFrame.unique>  s   ø ôp w~fš4À~ÓOÐOr4   c                ó"    t        |   |i |€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.
            **constraints: Column filters; use `name = value` to filter columns by the supplied value.
                Each constraint will behave the same as `nw.col(name).eq(value)`, and will be implicitly
                joined with the other filter conditions using &.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> 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.

            >>> def agnostic_filter(df_native: IntoFrame) -> IntoFrame:
            ...     df = nw.from_native(df_native)
            ...     return df.filter(nw.col("foo") > 1).to_native()

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

            >>> agnostic_filter(df_pd)
               foo  bar ham
            1    2    7   b
            2    3    8   c
            >>> agnostic_filter(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:

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

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

            Provide multiple filters using `*args` syntax:

            >>> def agnostic_filter(df_native: IntoFrame) -> IntoFrame:
            ...     df = nw.from_native(df_native)
            ...     dframe = df.filter(
            ...         nw.col("foo") <= 2,
            ...         ~nw.col("ham").is_in(["b", "c"]),
            ...     ).to_native()
            ...     return dframe
            >>> agnostic_filter(df_pd)
               foo  bar ham
            0    1    6   a
            >>> agnostic_filter(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            âââââââŽââââââŽââââââ

            Provide multiple filters using `**kwargs` syntax:

            >>> def agnostic_filter(df_native: IntoFrame) -> IntoFrame:
            ...     df = nw.from_native(df_native)
            ...     return df.filter(foo=2, ham="b").to_native()
            >>> agnostic_filter(df_pd)
               foo  bar ham
            1    2    7   b
            >>> agnostic_filter(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 2   â 7   â b   â
            âââââââŽââââââŽââââââ
        ©rZ  r   ©r2   r   r   r:   s      r3   r   zDataFrame.filterx  s   ø ôF w~zÐ9š[Ñ9Ð9r4   ©Údrop_null_keysc               ó8    ddl m}  || g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   )r2   r  Úkeysr   s       r3   Úgroup_byzDataFrame.group_byý  s!    õV 	.átÐKg dmÒKžNÑKÐKr4   r   c               ó,    t        |   |g|¢­||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   ©rZ  r   ©r2   r   r   r   r   r:   s        r3   r   zDataFrame.sortL  s!   ø ôv w|BÐW ÑW°ZÈJÒWÐWr4   r   r   c               ó.    t         |   ||||||¬«      S )uN
  Join in SQL-like fashion.

        Arguments:
            other: 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.
                  * *left*: Returns all rows from the left table, and the matched rows from the right table.
                  * *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   ©rZ  rŠ   ©r2   r§   rš   r£   r   r   r   r:   s          r3   rŠ   zDataFrame.join  s)   ø ôT w|Øs G°hÀ2Èfð ó 
ð 	
r4   r°   r±   c          
     ó2    t         	|   ||||||||¬«      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±   ©rZ  rž   ©
r2   r§   r   r   rš   r²   r³   r   rŽ   r:   s
            r3   rž   zDataFrame.join_asof×  s5   ø ô\ wÑ ØØØØØØØØð !ó 	
ð 		
r4   c                ól    | j                  | j                  j                  «       | j                  ¬«      S )aî  Get a mask of all duplicated rows in this DataFrame.

        Returns:
            A new Series.

        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
            ]
        r8   )ré   r+   Úis_duplicatedr-   r1   s    r3   r¢  zDataFrame.is_duplicated	  s4    ð` ||Ø×!Ñ!×/Ñ/Ó1Ø++ð ó 
ð 	
r4   c                ó6    | j                   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_emptyr1   s    r3   r€  zDataFrame.is_emptyÆ	  s    ð6 ×$Ñ$×-Ñ-Ó/Ð/r4   c                ól    | j                  | j                  j                  «       | j                  ¬«      S )aè  Get a mask of all unique rows in this DataFrame.

        Returns:
            A new Series.

        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
            ]
        r8   )ré   r+   Ú	is_uniquer-   r1   s    r3   rŠ  zDataFrame.is_uniqueã	  s4    ð` ||Ø×!Ñ!×+Ñ+Ó-Ø++ð ó 
ð 	
r4   c                óT    | j                  | j                  j                  «       «      S )u[  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   â
            âââââââŽââââââŽââââââ
        )r<   r+   Ú
null_countr1   s    r3   rš  zDataFrame.null_count
  s%    ðb ×-Ñ-šd×.CÑ.C×.NÑ.NÓ.PÓQÐQr4   c                ó<    | j                   j                  ||¬«      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)
        )rW  Úcolumn)r+   r0  )r2   rW  rª  s      r3   r0  zDataFrame.itemK
  s     ð: ×$Ñ$×)Ñ)šcž&Ð)ÓAÐAr4   c                ó     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   â
            âââââââŽââââââ
        ©rZ  r«   re  s    r3   r«   zDataFrame.clonej
  s   ø ôB w}Ðr4   c                ó&    t         |   ||¬«      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­   ©rZ  r¯   ©r2   rs   r®   r:   s      r3   r¯   zDataFrame.gather_every
  s   ø ôL wÑ# a°Ð#Ó7Ð7r4   Ú_)rŒ   ÚvaluesÚaggregate_functionr~   Úsort_columnsÚ	separatorc               ód    | j                  | j                  j                  |||||||¬«      «      S )u  Create a spreadsheet-style pivot table as a DataFrame.

        Arguments:
            on: Name of the column(s) whose values will be used as the header of the
                output DataFrame.
            index: One or multiple keys to group by. If None, all remaining columns not
                specified on `on` and `values` will be used. At least one of `index` and
                `values` must be specified.
            values: One or multiple keys to group by. If None, all remaining columns not
                specified on `on` and `index` will be used. At least one of `index` and
                `values` must be specified.
            aggregate_function: Choose from:
                - None: no aggregation takes place, will raise error if multiple values
                    are in group.
                - A predefined aggregate function string, one of
                    {'min', 'max', 'first', 'last', 'sum', 'mean', 'median', 'len'}
            maintain_order: Sort the grouped keys so that the output order is predictable.
            sort_columns: Sort the transposed columns by name. Default is by order of
                discovery.
            separator: Used as separator/delimiter in generated column names in case of
                multiple `values` columns.

        Examples:
            >>> import narwhals as nw
            >>> import pandas as pd
            >>> import polars as pl
            >>> data = {
            ...     "ix": [1, 1, 2, 2, 1, 2],
            ...     "col": ["a", "a", "a", "a", "b", "b"],
            ...     "foo": [0, 1, 2, 2, 7, 1],
            ...     "bar": [0, 2, 0, 0, 9, 4],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)

            Let's define a dataframe-agnostic function:

            >>> @nw.narwhalify
            ... def func(df):
            ...     return df.pivot("col", index="ix", aggregate_function="sum")

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

            >>> func(df_pd)
               ix  foo_a  foo_b  bar_a  bar_b
            0   1      1      7      2      9
            1   2      4      1      0      4
            >>> func(df_pl)
            shape: (2, 5)
            âââââââ¬ââââââââ¬ââââââââ¬ââââââââ¬ââââââââ
            â ix  â foo_a â foo_b â bar_a â bar_b â
            â --- â ---   â ---   â ---   â ---   â
            â i64 â i64   â i64   â i64   â i64   â
            âââââââªââââââââªââââââââªââââââââªââââââââ¡
            â 1   â 1     â 7     â 2     â 9     â
            â 2   â 4     â 1     â 0     â 4     â
            âââââââŽââââââââŽââââââââŽââââââââŽââââââââ
        )rš   rŒ   r±  r²  r~   r³  rŽ  )r<   r+   Úpivot)r2   rš   rŒ   r±  r²  r~   r³  rŽ  s           r3   r¶  zDataFrame.pivotµ
  sF    ðP ×-Ñ-Ø×!Ñ!×'Ñ'ØØØØ#5Ø-Ø)Ø#ð (ó ó

ð 
	
r4   c                ó6    | j                   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  r1   s    r3   r  zDataFrame.to_arrow	  s    ðB ×$Ñ$×-Ñ-Ó/Ð/r4   )ÚfractionÚwith_replacementÚseedc               ó^    | j                  | j                  j                  ||||¬«      «      S )up  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.
        )rs   rž  r¹  rº  )r<   r+   Úsample)r2   rs   rž  r¹  rº  s        r3   rŒ  zDataFrame.sample,  s<    ðj ×-Ñ-Ø×!Ñ!×(Ñ(ØhÐ9IÐPTð )ó ó
ð 	
r4   ©rŒ   rœ   rŸ   c               ó*    t         |   ||||¬«      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»   ©rZ  r¿   ©r2   rš   rŒ   rœ   rŸ   r:   s        r3   r¿   zDataFrame.unpivotg  s%   ø ôd wØšmÈ
ð ó 
ð 	
r4   )rÇ   ztype[Series[Any]])rÇ   ztype[LazyFrame[Any]]©r;   r   r9   r,   rÇ   ÚNone©rÇ   r%   rÈ   )NN)rý   r   rû   zbool | NonerÇ   ú
np.ndarray©rÇ   rM   r/   )r  zobject | NonerÇ   rà   )rÇ   zLazyFrame[Any])rÇ   r(   )rÇ   zpd.DataFrame)r"  zstr | Path | BytesIO | NonerÇ   r   )r"  zstr | Path | BytesIOrÇ   rÂ  )rÇ   rÄ  )rÇ   ztuple[int, int])r_   rM   rÇ   úSeries[Any])Úb)r,  r$   rÇ   zint | float)r0  ztuple[Sequence[int], slice]rÇ   r   )r0  z#tuple[Sequence[int], Sequence[int]]rÇ   r   )r0  ztuple[slice, Sequence[int]]rÇ   r   )r0  ztuple[Sequence[int], str]rÇ   rÆ  )r0  ztuple[slice, str]rÇ   rÆ  )r0  z#tuple[Sequence[int], Sequence[str]]rÇ   r   )r0  ztuple[slice, Sequence[str]]rÇ   r   )r0  ztuple[Sequence[int], int]rÇ   rÆ  )r0  ztuple[slice, int]rÇ   rÆ  )r0  zSequence[int]rÇ   r   )r0  rM   rÇ   rÆ  )r0  zSequence[str]rÇ   r   )r0  rG  rÇ   r   )r0  ztuple[slice, slice]rÇ   r   )r0  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]rÇ   zSeries[Any] | Self)rK  rM   rÇ   r   )rN  úLiteral[True]rÇ   zdict[str, Series[Any]])rN  úLiteral[False]rÇ   zdict[str, list[Any]])rN  r   rÇ   z-dict[str, Series[Any]] | dict[str, list[Any]])rŒ   rÓ   rÇ   ztuple[Any, ...]rÊ   rÍ   rË   rÌ   rÉ   ©r2   r   rÇ   r   rÏ   )rk  rÉ  rÇ   zlist[tuple[Any, ...]])rk  rÈ  rÇ   zlist[dict[str, Any]])rk  r   rÇ   z,list[tuple[Any, ...]] | list[dict[str, Any]])rk  rÉ  rr  rÓ   rÇ   zIterator[tuple[Any, ...]])rk  rÈ  rr  rÓ   rÇ   zIterator[dict[str, Any]])rk  r   rr  rÓ   rÇ   z4Iterator[tuple[Any, ...]] | Iterator[dict[str, Any]]rÐ   rÑ   ©é   rÒ   ©re   r×   ry   r   rÇ   r   rÔ   rÕ   )r  r×   r  r   rÇ   zGroupBy[Self]rÖ   rØ   rÙ   rÝ   )r2   r   rÇ   rÆ  )r2   r   rÇ   r   )r2   r   rÇ   r   )r2   r   rW  ú
int | Nonerª  zint | str | NonerÇ   r   rÚ   rÛ   rÜ   )r2   r   rš   zstr | list[str]rŒ   rÎ   r±  rÎ   r²  zMLiteral['min', 'max', 'first', 'last', 'sum', 'mean', 'median', 'len'] | Noner~   r   r³  r   rŽ  rM   rÇ   r   )r2   r   rÇ   zpa.Table)r2   r   rs   rÎ  rž  zfloat | Noner¹  r   rº  rÎ  rÇ   r   rß   );rá   râ   rã   Ú__doc__rå   ré   rì   ró   r÷   rù   rü   r  r  r  r   r  r   r$  r&  r(  r*  r-  r   r1  rL  rQ  rW  r[   rc   r]   rS   rV   re   rn  ru  rg   rl   rn   rq   rv   rz   r   r   r  r   rŠ   rž   r¢  r€  rŠ  rš  r0  r«   r¯   r¶  r  rŒ  r¿   Ú__classcell__©r:   s   @r3   rç   rç   S  s-  ø ñð òó ðð
 òó ðð&àð&ð 6ð	&ð
 
ó&ð ò5ó ð5ó4/ôAó
ôNó./Kób,3ó\.1ô`(5óT"2óH*0ðX ò#+ó ð#+óJ3
ôj*?ðX ÚIó ØIØÚQó ØQØÚIó ØIØÚNó ØNØÚFó ØFØÚQó ØQØÚIó ØIØÚNó ØNØÚFó ØFàÚ;ó Ø;àÚ8ó Ø8àÚ;ó Ø;àÚ3ó Ø3àÚAó ØAðB!ð"ðB!ð 
óB!óH#ð Ø47ÔWó ØWØÚPó ØPØð<Ø ð<à	6ò<ó ð<ð $(ñ9BØ ð9Bà	6ó9Bóv%0õP*7öX*1öX(,ðT ô"ó ð"õH"(ðH ôó ðð@ ð !&ñ$ð ð$ð 
ò	$ó ð$ð
 ð#ð ð#ð 
ò	#ó ð#ð
 ð;ð ð;ð 
6ò	;ó ð;ð ñ%7ð ð%7ð 
6ó	%7ðN à;>ñ(Ø&ð(Ø58ð(à	"ò(ó ð(ð à:=ñ'Ø%ð'Ø47ð'à	!ò'ó ð'ð à14ñCØðCØ+.ðCà	=òCó ðCð
  %žñ)UØð)UØ36ð)Uà	=ó)UðV>;Ø3ð>;ØDLð>;à	õ>;ð@x5à-ðx5ð  ðx5ð 
õ	x5õt''öR,ö\,ð\ BF÷ @>ðH *.ð8Pð 9>Ø$ñ8Pà&ð8Pð 6ð	8Pð
 ð8Pð 
õ8PðtC:ØEðC:ØVYðC:à	õC:ðL BGñMLØ(ðMLØ:>ðMLà	óMLðf -2Ø ñ;Xàð;Xð ð;Xð *ð	;Xð
 ð;Xð 
õ;Xð@ &*ØAHð	L
ð +/Ø+/ØñL
àðL
ð #ðL
ð ?ð	L
ð (ðL
ð )ðL
ð ðL
ð 
õL
ðd #Ø#ØØ*.Ø+/Ø%)Ø>Hñw
àðw
ð ð	w
ð
 ðw
ð ðw
ð (ðw
ð )ðw
ð #ðw
ð <ðw
ð 
õw
ót3
ój0ó:3
ój1RôfBõ>!öF&8ðX )-Ø)-ð Ø#Ø"ØñR
ØðR
àðR
ð &ð	R
ð
 'ðR
ððR
ð ðR
ð ðR
ð ðR
ð 
óR
óh!0ðJ ð9
ð "&Ø!&Øñ9
Øð9
àð9
ð ð	9
ð
 ð9
ð ð9
ð 
ó9
ðz &*ðT
ð )-Ø$(Ø!%ñT
ØðT
à"ðT
ð &ð	T
ð
 "ðT
ð ðT
ð 
÷T
ñ T
r4   rç   c                  ó~    e Zd ZdZed,d«       Z	 	 	 	 	 	 d-dZd.dZed/d«       Zd0dZ	d1dZ
d2dZd3 fd	Zd4d5 fdZd6d7 fdZed8 fd«       Zd9 fdZed: fd«       Z	 	 	 	 	 	 d; fdZ	 	 	 	 	 	 d; fdZd< fdZd=d> fdZd=d> fdZddd? fdZ	 d4ddd	 	 	 	 	 	 	 d@ fdZ	 	 	 	 	 	 dA fdZdd	 	 	 	 	 dBdZddd	 	 	 	 	 	 	 	 	 dC fd Z	 	 dDd
d
d!d"	 	 	 	 	 	 	 	 	 	 	 	 	 dE fd#Zd
d
d
d
d
d
d$d%	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dF fd&ZdG fd'ZdGd(ZdHdI fd)Z 	 d4d
d
d
d*	 	 	 	 	 	 	 	 	 	 	 dJ fd+Z! xZ"S )Krë   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`.
    c                ó    t         S r/   )rç   r1   s    r3   Ú
_dataframezLazyFrame._dataframeÇ  rí   r4   c               ó    || _         t        |d«      r|j                  «       | _        y dt	        |«       }t        |«      )NÚ__narwhals_lazyframe__zVExpected Polars LazyFrame or an object that implements `__narwhals_lazyframe__`, got: )r-   rð   rÖ  r+   rN   rñ   rò   s       r3   ró   zLazyFrame.__init__Ë  sF    ð Ü2Ð/Ô0Ø)+×)BÑ)BÓ)DDÕ!àjÔkoÐprÓksÐjtÐuCÜ  Ó%Ð%r4   c                ó^    d}t        |«      }dd|z  z   dz   d| dz   dz   dz   d|z  z   d	z   S )
Nz' Narwhals LazyFrame                    rÿ   r   r  r  r  r  r  r  r  r  s      r3   r  zLazyFrame.__repr__Ø  r  r4   c                ó.    | j                   j                  S )a¯  Return implementation of native frame.

        This can be useful when you need to some special-casing for
        some libraries for features outside of Narwhals' scope - for
        example, when dealing with pandas' Period Dtype.

        Returns:
            Implementation.

        Examples:
            >>> import narwhals as nw
            >>> import polars as pl
            >>> lf_native = pl.LazyFrame({"a": [1, 2, 3]})
            >>> lf = nw.from_native(lf_native)
            >>> lf.implementation
            <Implementation.POLARS: 6>
            >>> lf.implementation.is_pandas()
            False
            >>> lf.implementation.is_polars()
            True
        rõ   r1   s    r3   r÷   zLazyFrame.implementationæ  s    ð. ×$Ñ$×4Ñ4Ð4r4   c                ó    d}t        |«      )Nz%Slicing is not supported on LazyFrame)rO   rI  s      r3   r1  zLazyFrame.__getitem__ÿ  s    Ø5ÜnÐr4   c                óX    | 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   â
            âââââââŽââââââŽââââââ
        Úfullr8   )rÔ  r+   Úcollectr1   s    r3   rÜ  zLazyFrame.collect  s0    ðF Ø×!Ñ!×)Ñ)Ó+Øð ó 
ð 	
r4   c                ó    t        | 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)
            >>> lf_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(lf_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)Únarwhals_objectÚpass_throughr   r1   s    r3   r   zLazyFrame.to_native+  s    ôH šžEÔBÐBr4   c                ó*    t        |   |g|¢­i |€S )uT  Pipe function call.

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

            Let's define a dataframe-agnostic function:

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

            We can then pass either pandas or Polars:

            >>> agnostic_pipe(df_pd)
               a
            0  1
            1  2
            2  3
            >>> agnostic_pipe(lf_pl).collect()
            shape: (3, 1)
            âââââââ
            â a   â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ
        rY  r[  s       r3   r[   zLazyFrame.pipeR  s   ø ôL w|HÐ6 tÒ6švÑ6Ð6r4   Nc                ó$    t         |   |¬«      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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1.0, 2.0, None], "ba": [1.0, None, 2.0]}
            >>> df_pd = pd.DataFrame(data)
            >>> lf_pl = pl.LazyFrame(data)

            Let's define a dataframe-agnostic function:

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

            We can then pass either pandas or Polars:

            >>> agnostic_drop_nulls(df_pd)
                 a   ba
            0  1.0  1.0
            >>> agnostic_drop_nulls(lf_pl).collect()
            shape: (1, 2)
            âââââââ¬ââââââ
            â a   â ba  â
            â --- â --- â
            â f64 â f64 â
            âââââââªââââââ¡
            â 1.0 â 1.0 â
            âââââââŽââââââ
        ra   r]  r^  s     r3   rc   zLazyFrame.drop_nullsz  r_  r4   c                ó"    t         |   |«      S )u  Insert column which enumerates rows.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_with_row_index(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_row_index().to_native()

            We can then pass either pandas or Polars:

            >>> agnostic_with_row_index(df_pd)
               index  a  b
            0      0  1  4
            1      1  2  5
            2      2  3  6
            >>> agnostic_with_row_index(lf_pl).collect()
            shape: (3, 3)
            âââââââââ¬ââââââ¬ââââââ
            â index â a   â b   â
            â ---   â --- â --- â
            â u32   â i64 â i64 â
            âââââââââªââââââªââââââ¡
            â 0     â 1   â 4   â
            â 1     â 2   â 5   â
            â 2     â 3   â 6   â
            âââââââââŽââââââŽââââââ
        ra  rb  s     r3   r]   zLazyFrame.with_row_indexŠ  s   ø ôL wÑ% dÓ+Ð+r4   c                ó    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.schema  # doctest: +SKIP
            Schema({'foo': Int64, 'bar': Float64, 'ham', String})
        rd  re  s    r3   rS   zLazyFrame.schemaÎ  s   ø ô$ w~Ðr4   c                ó     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})
        rg  re  s    r3   rV   zLazyFrame.collect_schemaâ  s   ø ô" wÑ%Ó'Ð'r4   c                ó    t         |   S )aV  Get column names.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> 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:

            >>> def agnostic_columns(df_native: IntoFrame) -> list[str]:
            ...     df = nw.from_native(df_native)
            ...     return df.columns

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

            >>> agnostic_columns(df_pd)
            ['foo', 'bar', 'ham']
            >>> agnostic_columns(lf_pl)  # doctest: +SKIP
            ['foo', 'bar', 'ham']
        ri  re  s    r3   re   zLazyFrame.columnsõ  s   ø ô6 wÐr4   c                ó"    t        |   |i |€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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

            >>> def agnostic_with_columns(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.with_columns((nw.col("a") * 2).alias("2a")).to_native()

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

            >>> agnostic_with_columns(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
            >>> agnostic_with_columns(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   â
            âââââââŽâââââââŽââââââââŽââââââ
            >>> agnostic_with_columns(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{  r|  s      r3   rg   zLazyFrame.with_columns  s   ø ôZ wÑ# UÐ:škÑ:Ð:r4   c                ó"    t        |   |i |€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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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.

            >>> def agnostic_select(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select("foo").to_native()

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

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

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

            >>> def agnostic_select(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(["foo", "bar"]).to_native()
            >>>
            >>> agnostic_select(df_pd)
               foo  bar
            0    1    6
            1    2    7
            2    3    8
            >>> agnostic_select(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 6   â
            â 2   â 7   â
            â 3   â 8   â
            âââââââŽââââââ
            >>> agnostic_select(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.

            >>> def agnostic_select(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(nw.col("foo"), nw.col("bar") + 1).to_native()
            >>>
            >>> agnostic_select(df_pd)
               foo  bar
            0    1    7
            1    2    8
            2    3    9
            >>> agnostic_select(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 7   â
            â 2   â 8   â
            â 3   â 9   â
            âââââââŽââââââ
            >>> agnostic_select(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.

            >>> def agnostic_select(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.select(threshold=nw.col("foo") * 2).to_native()
            >>>
            >>> agnostic_select(df_pd)
               threshold
            0          2
            1          4
            2          6
            >>> agnostic_select(df_pl)
            shape: (3, 1)
            âââââââââââââ
            â threshold â
            â ---       â
            â i64       â
            âââââââââââââ¡
            â 2         â
            â 4         â
            â 6         â
            âââââââââââââ
            >>> agnostic_select(lf_pl).collect()
            shape: (3, 1)
            âââââââââââââ
            â threshold â
            â ---       â
            â i64       â
            âââââââââââââ¡
            â 2         â
            â 4         â
            â 6         â
            âââââââââââââ
        r~  r|  s      r3   rl   zLazyFrame.selecta  s   ø ô\ w~uÐ4šÑ4Ð4r4   c                ó"    t         |   |«      S )ub  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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

            >>> def agnostic_rename(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.rename({"foo": "apple"}).to_native()

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

            >>> agnostic_rename(df_pd)
               apple  bar ham
            0      1    6   a
            1      2    7   b
            2      3    8   c
            >>> agnostic_rename(lf_pl).collect()
            shape: (3, 3)
            âââââââââ¬ââââââ¬ââââââ
            â apple â bar â ham â
            â ---   â --- â --- â
            â i64   â i64 â str â
            âââââââââªââââââªââââââ¡
            â 1     â 6   â a   â
            â 2     â 7   â b   â
            â 3     â 8   â c   â
            âââââââââŽââââââŽââââââ
        r  r  s     r3   rn   zLazyFrame.rename  s   ø ôV w~gÓ&Ð&r4   c                ó"    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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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.

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

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

            >>> agnostic_head(df_pd)
               a  b
            0  1  7
            1  2  8
            2  3  9
            >>> agnostic_head(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 7   â
            â 2   â 8   â
            â 3   â 9   â
            âââââââŽââââââ
            >>> agnostic_head(lf_pl).collect()
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 7   â
            â 2   â 8   â
            â 3   â 9   â
            âââââââŽââââââ
        r  r  s     r3   rq   zLazyFrame.head>  ó   ø ôp w|AÐr4   c                ó"    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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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.

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

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

            >>> agnostic_tail(df_pd)
               a   b
            3  4  10
            4  5  11
            5  6  12
            >>> agnostic_tail(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 4   â 10  â
            â 5   â 11  â
            â 6   â 12  â
            âââââââŽââââââ
            >>> agnostic_tail(lf_pl).collect()
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 4   â 10  â
            â 5   â 11  â
            â 6   â 12  â
            âââââââŽââââââ
        r  r  s     r3   rv   zLazyFrame.tailx  rê  r4   Trx   c               ó4    t        |   t        |«      d|iS )uF	  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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

            >>> def agnostic_drop(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.drop("ham").to_native()

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

            >>> agnostic_drop(df_pd)
               foo  bar
            0    1  6.0
            1    2  7.0
            2    3  8.0
            >>> agnostic_drop(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.

            >>> def agnostic_drop(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.drop("foo", "ham").to_native()

            >>> agnostic_drop(df_pd)
               bar
            0  6.0
            1  7.0
            2  8.0
            >>> agnostic_drop(lf_pl).collect()
            shape: (3, 1)
            âââââââ
            â bar â
            â --- â
            â f64 â
            âââââââ¡
            â 6.0 â
            â 7.0 â
            â 8.0 â
            âââââââ
        ry   r  r  s      r3   rz   zLazyFrame.drop²  s   ø ôN w|W WÓ-Ð=°fÑ=Ð=r4   r{   Fr|   c               ó(    t         |   |||¬«      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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

            >>> def agnostic_unique(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.unique(["bar", "ham"]).to_native()

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

            >>> agnostic_unique(df_pd)
               foo bar ham
            0    1   a   b
            >>> agnostic_unique(lf_pl).collect()
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â str â str â
            âââââââªââââââªââââââ¡
            â 1   â a   â b   â
            âââââââŽââââââŽââââââ
        r|   r  r  s       r3   r   zLazyFrame.uniqueû  s   ø ô| w~fš4À~ÓOÐOr4   c                ó"    t        |   |i |€S )uK  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.
            **constraints: Column filters; use `name = value` to filter columns by the supplied value.
                Each constraint will behave the same as `nw.col(name).eq(value)`, and will be implicitly
                joined with the other filter conditions using &.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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.

            >>> def agnostic_filter(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.filter(nw.col("foo") > 1).to_native()

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

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

            Filter on multiple conditions:

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

            Provide multiple filters using `*args` syntax:

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

            Filter on an OR condition:

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

            Provide multiple filters using `**kwargs` syntax:

            >>> def agnostic_filter(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.filter(foo=2, ham="b").to_native()
            >>>
            >>> agnostic_filter(df_pd)
               foo  bar ham
            1    2    7   b
            >>> agnostic_filter(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 2   â 7   â b   â
            âââââââŽââââââŽââââââ
            >>> agnostic_filter(lf_pl).collect()
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 2   â 7   â b   â
            âââââââŽââââââŽââââââ
        r  r  s      r3   r   zLazyFrame.filter;  s   ø ôn w~zÐ9š[Ñ9Ð9r4   r  c               ó8    ddl m}  || g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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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.

            >>> def agnostic_group_by_agg(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.group_by("a").agg(nw.col("b").sum()).sort("a").to_native()

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

            >>> agnostic_group_by_agg(df_pd)
               a  b
            0  a  2
            1  b  5
            2  c  3
            >>> agnostic_group_by_agg(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â str â i64 â
            âââââââªââââââ¡
            â a   â 2   â
            â b   â 5   â
            â c   â 3   â
            âââââââŽââââââ
            >>> agnostic_group_by_agg(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.

            >>> def agnostic_group_by_agg(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return (
            ...         df.group_by(["a", "b"]).agg(nw.max("c")).sort(["a", "b"]).to_native()
            ...     )
            >>>
            >>> agnostic_group_by_agg(df_pd)
               a  b  c
            0  a  1  5
            1  b  2  4
            2  b  3  2
            3  c  3  1
            >>> agnostic_group_by_agg(df_pl)
            shape: (4, 3)
            âââââââ¬ââââââ¬ââââââ
            â a   â b   â c   â
            â --- â --- â --- â
            â str â i64 â i64 â
            âââââââªââââââªââââââ¡
            â a   â 1   â 5   â
            â b   â 2   â 4   â
            â b   â 3   â 2   â
            â c   â 3   â 1   â
            âââââââŽââââââŽââââââ
            >>> agnostic_group_by_agg(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   )r2   r  r  r   s       r3   r  zLazyFrame.group_byô  s!    õT 	2á4ÐO€'š$£-ÒOÀÑOÐOr4   r   c               ó,    t        |   |g|¢­||dS )uÿ  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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": [1, 2, None],
            ...     "b": [6.0, 5.0, 4.0],
            ...     "c": ["a", "c", "b"],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> lf_pl = pl.LazyFrame(data)

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

            >>> def agnostic_sort(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.sort("c", "a", descending=[False, True]).to_native()

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

            >>> agnostic_sort(df_pd)
                 a    b  c
            0  1.0  6.0  a
            2  NaN  4.0  b
            1  2.0  5.0  c
            >>> agnostic_sort(lf_pl).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  s        r3   r   zLazyFrame.sortb  s!   ø ô| w|BÐW ÑW°ZÈJÒWÐWr4   r   r   c               ó.    t         |   ||||||¬«      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.
                  * *left*: Returns all rows from the left table, and the matched rows from the right table.
                  * *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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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)

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

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

            >>> def agnostic_join_on_ham(
            ...     df_native: IntoFrameT,
            ...     other_native: IntoFrameT,
            ... ) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     other = nw.from_native(other_native)
            ...     return df.join(other, left_on="ham", right_on="ham").to_native()

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

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

            >>> agnostic_join_on_ham(lf_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  s          r3   rŠ   zLazyFrame.join¢  s)   ø ô` w|Øs G°hÀ2Èfð ó 
ð 	
r4   r°   r±   c          
     ó2    t         	|   ||||||||¬«      S )ue$  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
            >>> from typing import Literal
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

            >>> def agnostic_join_asof_datetime(
            ...     df_native: IntoFrameT,
            ...     other_native: IntoFrameT,
            ...     strategy: Literal["backward", "forward", "nearest"],
            ... ) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     other = nw.from_native(other_native)
            ...     return df.join_asof(other, on="datetime", strategy=strategy).to_native()

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

            >>> agnostic_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

            >>> agnostic_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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

            >>> def agnostic_join_asof_datetime_by_ticker(
            ...     df_native: IntoFrameT,
            ...     other_native: IntoFrameT,
            ... ) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     other = nw.from_native(other_native)
            ...     return df.join_asof(other, on="datetime", by="ticker").to_native()

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

            >>> agnostic_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

            >>> agnostic_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   s
            r3   rž   zLazyFrame.join_asofö  s5   ø ôz wÑ ØØØØØØØØð !ó 	
ð 		
r4   c                ó     t         |   «       S )uj  Create a copy of this DataFrame.

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

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

            >>> def agnostic_clone(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.clone().to_native()

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

            >>> agnostic_clone(lf_pl).collect()
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 3   â
            â 2   â 4   â
            âââââââŽââââââ
        r¬  re  s    r3   r«   zLazyFrame.cloneŸ  s   ø ôF w}Ðr4   c                ó    | S )a4  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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

            >>> def agnostic_lazy(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return df.lazy().to_native()

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

            >>> agnostic_lazy(df_pd)
               foo  bar ham
            0    1  6.0   a
            1    2  7.0   b
            2    3  8.0   c
            >>> agnostic_lazy(lf_pl)
            <LazyFrame ...>
        rY   r1   s    r3   r  zLazyFrame.lazyã  s
    ðB r4   c                ó&    t         |   ||¬«      S )uH  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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> 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:

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

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

            >>> agnostic_gather_every(lf_pl).collect()
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 2   â 6   â
            â 4   â 8   â
            âââââââŽââââââ
        r­   r®  r¯  s      r3   r¯   zLazyFrame.gather_every  s   ø ôP wÑ# a°Ð#Ó7Ð7r4   rœ  c               ó*    t         |   ||||¬«      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
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": ["x", "y", "z"],
            ...     "b": [1, 3, 5],
            ...     "c": [2, 4, 6],
            ... }
            >>> lf_pl = pl.LazyFrame(data)

            We define a library agnostic function:

            >>> def agnostic_unpivot(df_native: IntoFrameT) -> IntoFrameT:
            ...     df = nw.from_native(df_native)
            ...     return (
            ...         df.unpivot(on=["b", "c"], index="a").sort(["variable", "a"])
            ...     ).to_native()

            >>> agnostic_unpivot(lf_pl).collect()
            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»   r¿  rÀ  s        r3   r¿   zLazyFrame.unpivot0  s%   ø ô@ wØšmÈ
ð ó 
ð 	
r4   )rÇ   ztype[DataFrame[Any]]rÁ  rÅ  rÃ  )r0  zstr | slicerÇ   r   )rÇ   zDataFrame[Any])rÇ   r&   rÊ   r/   rÍ   rË   rÌ   rÉ   rÊ  rÏ   rÐ   rÑ   rË  rÒ   rÍ  rÔ   rÕ   )r  r×   r  r   rÇ   zLazyGroupBy[Self]rÖ   rØ   rÙ   rÝ   rÚ   rÛ   rÜ   rß   )#rá   râ   rã   rÏ  rå   rÔ  ró   r  r÷   r1  rÜ  r   r[   rc   r]   rS   rV   re   rg   rl   rn   rq   rv   rz   r   r   r  r   rŠ   rž   r«   r  r¯   r¿   rÐ  rÑ  s   @r3   rë   rë   Ÿ  sB  ø ñð òó ðð&àð&ð 6ð	&ð
 
ó&ó
ð ò5ó ð5ó0ó&
óP$CõN&7öP*1öX&,ðP ôó ðõ&(ð& ôó ðð8M;Ø3ðM;ØDLðM;à	õM;ð^n5à-ðn5ð  ðn5ð 
õ	n5õ`+'öZ8öt8ðt BF÷ G>ðV *.ð>Pð 9>Ø$ñ>Pà&ð>Pð 6ð	>Pð
 ð>Pð 
õ>Pð@w:ØEðw:ØVYðw:à	õw:ðt BGñlPØ(ðlPØ:>ðlPà	ólPðd -2Ø ñ>Xàð>Xð ð>Xð *ð	>Xð
 ð>Xð 
õ>XðF &*ØAHð	R
ð +/Ø+/ØñR
àðR
ð #ðR
ð ?ð	R
ð (ðR
ð )ðR
ð ðR
ð 
õR
ðp #Ø#ØØ*.Ø+/Ø%)Ø>HñF
àðF
ð ð	F
ð
 ðF
ð ðF
ð (ðF
ð )ðF
ð #ðF
ð <ðF
ð 
õF
õP#óJ!öF(8ðX &*ðB
ð )-Ø$(Ø!%ñB
ØðB
à"ðB
ð &ð	B
ð
 "ðB
ð ðB
ð 
÷B
ñ B
r4   rë   )7Ú
__future__r   Útypingr   r   r   r   r   r	   r
   r   r   r   r   Únarwhals.dependenciesr   r   Únarwhals.schemar   Únarwhals.translater   Únarwhals.utilsr   r   r   Úior   Úpathlibr   Útypesr   ÚnumpyÚnpÚpandasÚpdr  r  Útyping_extensionsr   r  r   r   rI   r    Únarwhals.typingr!   r"   r#   r$   r%   r&   r(   r*   rç   rë   rY   r4   r3   ú<module>r     sÁ   ðÝ "å  Ý Ý Ý Ý Ý Ý Ý Ý Ý Ý å ,Ý 0Ý "Ý (Ý "Ý 2Ý (áÝÝÝ ãÛÛÝ&å)Ý-Ý&Ý-Ý(Ý)Ý(Ý-á	 Ô	-Ù\šÔ9
ôb'ô b'ôJ	h)
	*Ñ%ô h)
ôVSt
	&Ñ!õ t
r4   