Ë
    Ôªg ã                  ó4   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 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l0m1Z1 d d l0m2Z2 d d!l0m3Z3 d d"l0m4Z4 d d#lm5Z5  ed$d%¬&«      Z6 ed'd(¬&«      Z7 G d) d*ee6   «      Z8 G d+ d,e8e7   «      Z9 G d- d.e8e6   «      Z:y)/é    )Úannotations)ÚTYPE_CHECKING)ÚAny)ÚCallable)ÚGeneric)ÚIterable)ÚIterator)ÚLiteral)ÚNoReturn)ÚSequence)ÚTypeVar)Úoverload)Úwarn)Ú
get_polars)Úis_numpy_array)ÚSchema©Ú	to_native)Úfind_stacklevel)Úflatten)Úgenerate_repr)Ú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d.d/dZd0d1dZe	d2d«       Z	 	 	 	 	 	 d3dZ	 	 	 	 	 	 d3dZd4dZd5dZd5dZd6dZ	 	 	 	 	 	 d7dZddd	 	 	 	 	 	 	 	 	 d8dZ	 	 d9dddd	 	 	 	 	 	 	 	 	 	 	 	 	 d:dZd;dZd<d=dZddddddd d!	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d>d"Z	 	 	 	 	 	 	 	 	 	 	 	 d?d#Zd@d$ZdAd%ZdBd&Zy)CÚ	BaseFramer   Ú_compliant_frameú&Literal['full', 'lazy', 'interchange']Ú_levelc                ó6    | j                   j                  «       S ©N)r.   Ú__native_namespace__©Úselfs    úG/var/www/openai/venv/lib/python3.12/site-packages/narwhals/dataframe.pyr3   zBaseFrame.__native_namespace__5   s    Ø×$Ñ$×9Ñ9Ó;Ð;ó    c                ó6    | j                   j                  «       S r2   )r.   Ú__narwhals_namespace__r4   s    r6   r9   z BaseFrame.__narwhals_namespace__8   s    Ø×$Ñ$×;Ñ;Ó=Ð=r7   c                ó<    | j                  || j                  ¬«      S )N©Úlevel)Ú	__class__r0   )r5   Údfs     r6   Ú_from_compliant_dataframez#BaseFrame._from_compliant_dataframe;   s"    à~~ØØ++ð ó 
ð 	
r7   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)r5   ÚargsÚkwargsÚvÚks        r6   Ú_flatten_and_extractzBaseFrame._flatten_and_extractB   sf    ä4;žDŽMÓB±Mšq×'Ñ'šÕ*°MÐBØ<B¿L¹LŒNÔK¹N±D°A°q!T×,Ñ,šQÓ/Ñ/žNÑ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.exprrJ   Únarwhals.seriesr#   Ú
isinstancer-   r.   Ú_compliant_seriesÚ_to_compliant_exprr9   r   ÚstrÚtypeÚ	TypeError)r5   ÚargrJ   r#   Úmsgs        r6   rA   zBaseFrame._extract_compliantH   s    Ý&Ý*äc9Ô%Ø×'Ñ'Ð'Üc6Ô"Ø×(Ñ(Ð(Üc4Ô Ø×)Ñ)š$×*EÑ*EÓ*GÓHÐHÜ<Ð#šŽCŒžS»	³NÑ(Bà1Ž$°s³)°ð =7ð 7ð ô C.Ð Ø
r7   c                ó\    t        | j                  j                  j                  «       «      S r2   )r   r.   ÚschemarB   r4   s    r6   rW   zBaseFrame.schema\   s"    äd×+Ñ+×2Ñ2×8Ñ8Ó:Ó;Ð;r7   c                ó^    t        | j                  j                  «       «      }t        |«      S r2   )Údictr.   Úcollect_schemar   )r5   Únative_schemas     r6   rZ   zBaseFrame.collect_schema`   s&    ÜT×2Ñ2×AÑAÓCÓDämÓ$Ð$r7   c                ó     || g|¢­i |€S r2   © )r5   ÚfunctionrC   rD   s       r6   ÚpipezBaseFrame.pipee   s    ÙÐ.tÒ. vÑ.Ð.r7   c                óV    | j                  | j                  j                  |«      «      S r2   )r?   r.   Úwith_row_index©r5   Únames     r6   ra   zBaseFrame.with_row_indexh   s)    Ø×-Ñ-Ø×!Ñ!×0Ñ0°Ó6ó
ð 	
r7   Nc                óX    | j                  | j                  j                  |¬«      «      S )N©Úsubset)r?   r.   Ú
drop_nulls)r5   rf   s     r6   rg   zBaseFrame.drop_nullsm   s,    Ø×-Ñ-Ø×!Ñ!×,Ñ,°FÐ,Ó;ó
ð 	
r7   c                ó.    | j                   j                  S r2   )r.   Úcolumnsr4   s    r6   ri   zBaseFrame.columnsr   s    à×$Ñ$×,Ñ,Ð,r7   c                ó     | j                   |i |€\  }}| j                   | j                  j                  |i |€«      S r2   )rG   r?   r.   Úwith_columns©r5   ÚexprsÚnamed_exprss      r6   rk   zBaseFrame.with_columnsv   sN    ð 7T×6Ñ6žÐMÀÑMÑ{Ø×-Ñ-Ø.D×!Ñ!×.Ñ.°ÐEžÑEó
ð 	
r7   c                ó     | j                   |i |€\  }}| j                   | j                  j                  |i |€«      S r2   )rG   r?   r.   Úselectrl   s      r6   rp   zBaseFrame.select~   sN    ð
 7T×6Ñ6žÐMÀÑMÑ{Ø×-Ñ-Ø(D×!Ñ!×(Ñ(š%Ð?°;Ñ?ó
ð 	
r7   c                óV    | j                  | j                  j                  |«      «      S r2   )r?   r.   Úrename)r5   Úmappings     r6   rr   zBaseFrame.rename   s$    Ø×-Ñ-šd×.CÑ.C×.JÑ.JÈ7Ó.SÓTÐTr7   c                óV    | j                  | j                  j                  |«      «      S r2   )r?   r.   Úhead©r5   Úns     r6   ru   zBaseFrame.head   ó$    Ø×-Ñ-šd×.CÑ.C×.HÑ.HÈÓ.KÓLÐLr7   c                óV    | j                  | j                  j                  |«      «      S r2   )r?   r.   Útailrv   s     r6   rz   zBaseFrame.tail   rx   r7   c               óZ    | j                  | j                  j                  ||¬«      «      S )N©Ústrict)r?   r.   Údrop)r5   r}   ri   s      r6   r~   zBaseFrame.drop   s.    Ø×-Ñ-Ø×!Ñ!×&Ñ& w°vÐ&Ó>ó
ð 	
r7   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r2   ©rN   Úbool©Ú.0Úxs     r6   Ú	<genexpr>z#BaseFrame.filter.<locals>.<genexpr>   ó   è ø Ð?±šAJq€$×'±ùó   )ÚlenrN   ÚlistÚallrG   r?   r.   Úfilter)r5   Ú
predicatesÚconstraintss      r6   r   zBaseFrame.filter   s    ô 
OqÒ Ü: a=¬$Ô/ÜÑ?°žA²Ó?Ô?à&? d×&?Ñ&?Øð'Ø*ñ'Ñ#Jð ×-Ñ-Ø(D×!Ñ!×(Ñ(š*ÐDžÑDó
ð 	
r7   F©Ú
descendingÚ
nulls_lastc               ób    | j                   | j                  j                  |g|¢­||d«      S )Nr   )r?   r.   Úsort)r5   Úbyr   r   Úmore_bys        r6   r   zBaseFrame.sort¥   sB    ð ×-Ñ-Ø&D×!Ñ!×&Ñ&ØðØñØ)3À
òó
ð 	
r7   Ú_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.   ÚjoinrA   )	r5   ÚotherÚonr¥   r   r   r   Ú_supported_joinsrU   s	            r6   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à×-Ñ-Ø×!Ñ!×&Ñ&Ø×'Ñ'šÓ.ØØØ!Øð 'ó ó
ð 	
r7   c                óT    | j                  | j                  j                  «       «      S r2   )r?   r.   Úcloner4   s    r6   r­   zBaseFrame.cloneß   s"    Ø×-Ñ-šd×.CÑ.C×.IÑ.IÓ.KÓLÐLr7   c                óZ    | j                  | j                  j                  ||¬«      «      S )N©rw   Úoffset)r?   r.   Úgather_every)r5   rw   r°   s      r6   r±   zBaseFrame.gather_everyâ   s.    Ø×-Ñ-Ø×!Ñ!×.Ñ.°ž6Ð.ÓBó
ð 	
r7   Ú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_asofrA   )r5   r©   r   r   rª   rŽ   rµ   r   r¶   Ú_supported_strategiesrU   s              r6   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ó ó	ð 	ð ×-Ñ-Ø×!Ñ!×+Ñ+Ø×'Ñ'šÓ.ØØ!ØØ!ØØ!ð ,ó ó

ð 
	
r7   c               ó^    | j                  | j                  j                  ||||¬«      «      S )N©rª   ÚindexÚvariable_nameÚ
value_name)r?   r.   Úunpivot)r5   rª   rŸ   r¿   rÀ   s        r6   rÁ   zBaseFrame.unpivot!  s<    ð ×-Ñ-Ø×!Ñ!×)Ñ)ØØØ+Ø%ð	 *ó ó
ð 	
r7   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Š   ©r5   r©   rU   s      r6   Ú__neq__zBaseFrame.__neq__2  ó    ð+ð 	ô " #Ó&Ð&r7   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      r6   Ú__eq__zBaseFrame.__eq__=  rÆ   r7   c                óZ    | j                   | j                  j                  |g|¢­ «      S r2   )r?   r.   Úexplode)r5   ri   Úmore_columnss      r6   rÊ   zBaseFrame.explodeH  s6    Ø×-Ñ-Ø)D×!Ñ!×)Ñ)Øðàòó
ð 	
r7   )r5   r   Úreturnr   )rÌ   r   )r>   r   rÌ   r   )rC   r   rD   r   rÌ   r   )rT   r   rÌ   r   ©rÌ   r   ©r^   zCallable[[Any], Self]rC   r   rD   r   rÌ   r   ©rŸ   ©rc   rQ   rÌ   r   r2   ©r5   r   rf   ústr | list[str] | NonerÌ   r   ©rÌ   z	list[str]©rm   zIntoExpr | Iterable[IntoExpr]rn   r%   rÌ   r   ©rs   zdict[str, str]rÌ   r   ©rw   ÚintrÌ   r   )ri   zIterable[str]r}   r   rÌ   r   ©r   z*IntoExpr | Iterable[IntoExpr] | list[bool]r   r   rÌ   r   ©
r   ústr | Iterable[str]r   rQ   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   rQ   rÌ   r   ©rÌ   r   ©r   ©r5   r   rw   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   ©r5   r   rª   rÒ   rŸ   rÒ   r¿   rá   rÀ   rá   rÌ   r   )r©   r   rÌ   r   )r©   ÚobjectrÌ   r   ©r5   r   ri   zstr | Sequence[str]rË   rQ   rÌ   r   ) Ú__name__Ú
__module__Ú__qualname__Ú__annotations__r3   r9   r?   rG   rA   ÚpropertyrW   rZ   r_   ra   rg   ri   rk   rp   rr   ru   rz   r~   r   r   rš   r­   r±   rº   rÁ   rÅ   rÈ   rÊ   r]   r7   r6   r-   r-   1   s@   ØÓØ2Ó2ó<ó>ó
óóð( ò<ó ð<ó%ó
/ô
ô

ð
 ò-ó ð-ð
Ø3ð
ØDLð
à	ó
ð
à-ð
ð  ð
ð 
ó	
óUóMóMó
ð

ØEð
ØVYð
à	ó
ð& -2Ø ñ
àð
ð ð
ð *ð	
ð
 ð
ð 
ó
ð  &*ØAHð	+
ð +/Ø+/Øñ+
àð+
ð #ð+
ð ?ð	+
ð (ð+
ð )ð+
ð ð+
ð 
ó+
óZMô
ð #Ø#ØØ*.Ø+/Ø%)Ø>Hñ8
àð8
ð ð	8
ð
 ð8
ð ð8
ð (ð8
ð )ð8
ð #ð8
ð <ð8
ð 
ó8
ðt
Øð
à"ð
ð &ð	
ð
 "ð
ð ð
ð 
ó
ó"	'ó	'ô
r7   r-   c                  óì    e Zd ZdZedfd«       Zedgd«       Z	 	 	 	 	 	 dhdZedid«       ZdjdZ	dkdldZ
dmd	Zdndod
ZdpdZdqdZdrdZedndsd«       Zedtd«       ZdndudZdtdZdvdZedwd«       ZdxdZdydzd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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dnd fd/Zdd fd0Zed fd1«       Zd fd2Zed fd3«       Z ed4d5dd6«       Z!edd7«       Z!edd8«       Z!d4d5	 	 	 dd9Z!ed&d:	 	 	 	 	 dd;«       Z"ed&d:	 	 	 	 	 dd<«       Z"ed&d:	 	 	 	 	 dd=«       Z"d4d>d?	 	 	 	 	 dd@Z"	 	 	 	 	 	 d fdAZ#	 	 	 	 	 	 d fdBZ$d fdCZ%dd fdDZ&dd fdEZ'd+dFd  fdGZ(	 dndHd4dI	 	 	 	 	 	 	 d¡dJZ)	 	 	 	 	 	 d¢ fdKZ*d4dL	 	 	 	 	 d£dMZ+d4d4dN	 	 	 	 	 	 	 	 	 d€ fdOZ,	 	 d¥dddPdQ	 	 	 	 	 	 	 	 	 	 	 	 	 dŠ fdRZ-dddddddSdT	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d§ fdUZ.dšdVZ/d©dWZ0dšdXZ1dªdYZ2dkd«dZZ3d¬ fd[Z4d­d® fd\Z5ddddd4d]d^	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d¯d_Z6d°d`Z7	 dndd4dda	 	 	 	 	 	 	 	 	 	 	 d±dbZ8	 dnddddc	 	 	 	 	 	 	 	 	 	 	 d² fddZ9d³ fdeZ: xZ;S )ŽÚ	DataFramea  Narwhals DataFrame, backed by a native eager dataframe.

    !!! warning
        This class is not meant to be instantiated directly - instead:

        - If the native object is a eager dataframe from one of the supported
            backend (e.g. pandas.DataFrame, polars.DataFrame, pyarrow.Table),
            you can use [`narwhals.from_native`][]:
            ```py
            narwhals.from_native(native_dataframe)
            narwhals.from_native(native_dataframe, eager_only=True)
            ```

        - If the object is a dictionary of column names and generic sequences mapping
            (e.g. `dict[str, list]`), you can create a DataFrame via
            [`narwhals.from_dict`][]:
            ```py
            narwhals.from_dict(
                data={"a": [1, 2, 3]},
                native_namespace=narwhals.get_native_namespace(another_object),
            )
            ```
    c                ó    ddl m} |S )Nr   r"   )rM   r#   )r5   r#   s     r6   Ú_serieszDataFrame._seriesj  s
    å*àr7   c                ó    t         S r2   )Ú	LazyFramer4   s    r6   Ú
_lazyframezDataFrame._lazyframep  ó    äÐr7   c               ó    || _         t        |d«      r|j                  «       | _        y dt	        |«       }t        |«      )NÚ__narwhals_dataframe__zCExpected an object which implements `__narwhals_dataframe__`, got: )r0   Úhasattrró   r.   rR   ÚAssertionError©r5   r>   r<   rU   s       r6   Ú__init__zDataFrame.__init__t  sG    ð ?DÜ2Ð/Ô0Ø)+×)BÑ)BÓ)DDÕ!àWÔX\Ð]_ÓX`ÐWaÐbCÜ  Ó%Ð%r7   c                ó.    | j                   j                  S )a×  Return implementation of native frame.

        This can be useful when you need to use special-casing 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.   Ú_implementationr4   s    r6   ÚimplementationzDataFrame.implementation  s    ð0 ×$Ñ$×4Ñ4Ð4r7   c                ó6    | j                   j                  «       S r2   )r.   Ú__len__r4   s    r6   rý   zDataFrame.__len__  s    Ø×$Ñ$×,Ñ,Ó.Ð.r7   Nc                ó<    | j                   j                  ||¬«      S )N)Úcopy)r.   Ú	__array__)r5   Údtyperÿ   s      r6   r   zDataFrame.__array__  s    Ø×$Ñ$×.Ñ.šuž4Ð.Ó@Ð@r7   c                óR    t        d| j                  «       j                  «       «      S )NzNarwhals DataFrame©r   r   Ú__repr__r4   s    r6   r  zDataFrame.__repr__¡  ó     ÜÐ1°4·>±>Ó3C×3LÑ3LÓ3NÓOÐOr7   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ÚModuleNotFoundErrorrR   r   Ú__version__Úto_arrow)r5   r  Únative_frameÚpaÚexcrU   Úpa_tables          r6   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
            >>> 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)

            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"]]
        Úlazyr;   )rð   r.   r  r4   s    r6   r  zDataFrame.lazy»  s'    ð^ t×4Ñ4×9Ñ9Ó;À6ÓJÐJr7   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
  r4   s    r6   r   zDataFrame.to_nativeì  s    ðX ×$Ñ$×2Ñ2Ð2r7   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
            >>> 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)

            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_pandasr4   s    r6   r  zDataFrame.to_pandas  s    ðX ×$Ñ$×.Ñ.Ó0Ð0r7   c                 ó    y r2   r]   ©r5   Úfiles     r6   Ú	write_csvzDataFrame.write_csvH  s    Ø36r7   c                 ó    y r2   r]   r  s     r6   r  zDataFrame.write_csvK  s    Ø=@r7   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
            >>> from narwhals.typing import IntoDataFrame
            >>> 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)

            We define a library agnostic function:

            >>> def agnostic_write_csv(df_native: IntoDataFrame) -> str:
            ...     df = nw.from_native(df_native)
            ...     return df.write_csv()

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

            >>> agnostic_write_csv(df_pd)
            'foo,bar,ham\n1,6.0,a\n2,7.0,b\n3,8.0,c\n'
            >>> agnostic_write_csv(df_pl)
            'foo,bar,ham\n1,6.0,a\n2,7.0,b\n3,8.0,c\n'
            >>> agnostic_write_csv(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.   r  r  s     r6   r  zDataFrame.write_csvN  s    ðR ×$Ñ$×.Ñ.štÓ4Ð4r7   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
            >>> from narwhals.typing import IntoDataFrame
            >>> 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)

            We define a library agnostic function:

            >>> def agnostic_write_parquet(df_native: IntoDataFrame):
            ...     df = nw.from_native(df_native)
            ...     df.write_parquet("foo.parquet")

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

            >>> agnostic_write_parquet(df_pd)  # doctest:+SKIP
            >>> agnostic_write_parquet(df_pl)  # doctest:+SKIP
            >>> agnostic_write_parquet(df_pa)  # doctest:+SKIP
        N)r.   Úwrite_parquetr  s     r6   r  zDataFrame.write_parquety  s    ðF 	×Ñ×+Ñ+šDÕ1r7   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
            >>> data = {"foo": [1, 2, 3], "bar": [6.5, 7.0, 8.5], "ham": ["a", "b", "c"]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            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_numpyr4   s    r6   r!  zDataFrame.to_numpy  s    ðR ×$Ñ$×-Ñ-Ó/Ð/r7   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
            >>> data = {"foo": [1, 2, 3, 4, 5]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            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.   Úshaper4   s    r6   r#  zDataFrame.shapeÉ  s    ðF ×$Ñ$×*Ñ*Ð*r7   c                ón    | j                  | j                  j                  |«      | j                  ¬«      S )ay  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 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_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, Polars or PyArrow 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
            ]
            >>> agnostic_get_column(df_pa)  # doctest:+ELLIPSIS
            <pyarrow.lib.ChunkedArray object at ...>
            [
              [
                1,
                2
              ]
            ]
        r;   )rí   r.   Ú
get_columnr0   rb   s     r6   r%  zDataFrame.get_columnî  s6    ðr ||Ø×!Ñ!×,Ñ,šTÓ2Ø++ð ó 
ð 	
r7   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)r5   r'  s     r6   r(  zDataFrame.estimated_size,  s    ðT ×$Ñ$×3Ñ3žÐ3Ó>Ð>r7   c                 ó    y r2   r]   ©r5   Úitems     r6   Ú__getitem__zDataFrame.__getitem__X  ó    ØFIr7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__Z  ó    ØNQr7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__\  r-  r7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__^  ó    ØKNr7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__`  ó    ØCFr7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__b  r/  r7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__d  r-  r7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__f  r2  r7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__h  r4  r7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__k  ó    Ø8;r7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__n  s    Ø58r7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__q  r:  r7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__t  s    Ø03r7   c                 ó    y r2   r]   r*  s     r6   r,  zDataFrame.__getitem__w  s    Ø>Ar7   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   Nr;   )rN   r×   Útupler   rQ   rR   rS   r   Úslicer?   r.   rí   r0   r   Úndim©r5   r+  rU   s      r6   r,  zDataFrame.__getitem__z  sy   ôz 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.Ð r7   c                ó    || j                   v S r2   )ri   )r5   Úkeys     r6   Ú__contains__zDataFrame.__contains__ý  s    ØdllÐ"Ð"r7   .©Ú	as_seriesc                ó    y r2   r]   ©r5   rI  s     r6   Úto_dictzDataFrame.to_dict   ó    ØTWr7   c                ó    y r2   r]   rK  s     r6   rL  zDataFrame.to_dict  s    ØMPr7   c                ó    y r2   r]   rK  s     r6   rL  zDataFrame.to_dict  s    ð 9<r7   Tc          	     óü    |rY| j                   j                  |¬«      j                  «       D ci c]#  \  }}|| j                  || j                  ¬«      % c}}S | j                   j                  |¬«      S c c}}w )aq  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
            >>> data = {
            ...     "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(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            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]}
        rH  r;   )r.   rL  rB   rí   r0   )r5   rI  rF  Úvalues       r6   rL  zDataFrame.to_dict  s    ñ\ ð #'×"7Ñ"7×"?Ñ"?Ø'ð #@ó #ç%'ð#ôñ
#JCð	 T\\ØØ++ð "ó ñ ð#òð ð ×$Ñ$×,Ñ,°yÐ,ÓAÐAùós   °(A8c                ó8    | j                   j                  |«      S )aa  Get values at given row.

        !!! warning
            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.

        Returns:
            A tuple of the values in the selected row.

        Notes:
            cuDF doesn't support this method.

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

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

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

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

            >>> agnostic_row(df_pd)
            (2, 5)
            >>> agnostic_row(df_pl)
            (2, 5)
            >>> agnostic_row(df_pa)
            (<pyarrow.Int64Scalar: 2>, <pyarrow.Int64Scalar: 5>)
        )r.   Úrow)r5   rŸ   s     r6   rS  zDataFrame.rowB  s    ðV ×$Ñ$×(Ñ(šÓ/Ð/r7   c                ó*    t        |   |g|¢­i |€S )u©  Pipe function call.

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

        Returns:
            The original object with the function applied.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_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, Polars or PyArrow to `agnostic_pipe`:

            >>> agnostic_pipe(df_pd)
               a
            0  1
            1  2
            2  3
            >>> agnostic_pipe(df_pl)
            shape: (3, 1)
            âââââââ
            â a   â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ
            >>> agnostic_pipe(df_pa)
            pyarrow.Table
            a: int64
            ----
            a: [[1,2,3]]
        ©Úsuperr_   ©r5   r^   rC   rD   r=   s       r6   r_   zDataFrame.pipep  s   ø ôp w|HÐ6 tÒ6švÑ6Ð6r7   c                ó$    t         |   |¬«      S )uh  Drop rows that contain null values.

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

        Returns:
            The original object with the rows removed that contained the null values.

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

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

            Let's define a dataframe-agnostic function:

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

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

            >>> agnostic_drop_nulls(df_pd)
                 a   ba
            0  1.0  1.0
            >>> agnostic_drop_nulls(df_pl)
            shape: (1, 2)
            âââââââ¬ââââââ
            â a   â ba  â
            â --- â --- â
            â f64 â f64 â
            âââââââªââââââ¡
            â 1.0 â 1.0 â
            âââââââŽââââââ
            >>> agnostic_drop_nulls(df_pa)
            pyarrow.Table
            a: double
            ba: double
            ----
            a: [[1]]
            ba: [[1]]
        re   ©rV  rg   ©r5   rf   r=   s     r6   rg   zDataFrame.drop_nullsª  s   ø ôn wÑ!šÐ!Ó0Ð0r7   c                ó"    t         |   |«      S )uÊ  Insert column which enumerates rows.

        Arguments:
            name: The name of the column as a string. The default is "index".

        Returns:
            The original object with the column added.

        Examples:
            Construct pandas as polars DataFrames:

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

            Let's define a dataframe-agnostic function:

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

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

            >>> 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   â
            âââââââââŽââââââŽââââââ
            >>> agnostic_with_row_index(df_pa)
            pyarrow.Table
            index: int64
            a: int64
            b: int64
            ----
            index: [[0,1,2]]
            a: [[1,2,3]]
            b: [[4,5,6]]
        ©rV  ra   ©r5   rc   r=   s     r6   ra   zDataFrame.with_row_indexã  s   ø ôr wÑ% dÓ+Ð+r7   c                ó    t         |   S )aa  Get an ordered mapping of column names to their data type.

        Returns:
            A Narwhals Schema object that displays the mapping of column names.

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

            We define a library agnostic function:

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

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

            >>> agnostic_schema(df_pd)
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
            >>> agnostic_schema(df_pl)
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
            >>> agnostic_schema(df_pa)
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
        ©rV  rW   ©r5   r=   s    r6   rW   zDataFrame.schema  s   ø ôN w~Ðr7   c                ó     t         |   «       S )a  Get an ordered mapping of column names to their data type.

        Returns:
            A Narwhals Schema object that displays the mapping of column names.

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

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

            >>> agnostic_collect_schema(df_pd)
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
            >>> agnostic_collect_schema(df_pl)
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
            >>> agnostic_collect_schema(df_pa)
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
        ©rV  rZ   r`  s    r6   rZ   zDataFrame.collect_schemaG  s   ø ôL wÑ%Ó'Ð'r7   c                ó    t         |   S )a>  Get column names.

        Returns:
            The column names stored in a list.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>> data = {"foo": [1, 2, 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)

            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 any supported library such as Pandas, Polars, or PyArrow
            to `agnostic_columns`:

            >>> agnostic_columns(df_pd)
            ['foo', 'bar', 'ham']
            >>> agnostic_columns(df_pl)
            ['foo', 'bar', 'ham']
            >>> agnostic_columns(df_pa)
            ['foo', 'bar', 'ham']
        ©rV  ri   r`  s    r6   ri   zDataFrame.columnso  s   ø ôD wÐr7   F©Únamedc                ó    y r2   r]   ©r5   rf  s     r6   ÚrowszDataFrame.rows  s    ØORr7   c                ó    y r2   r]   rh  s     r6   ri  zDataFrame.rows  s    ØEHr7   c                ó    y r2   r]   rh  s     r6   ri  zDataFrame.rows  rM  r7   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.

        Returns:
            The data as a list of rows.

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

            We define a library agnostic function:

            >>> def agnostic_rows(df_native: IntoDataFrame, *, named: bool):
            ...     return nw.from_native(df_native, eager_only=True).rows(named=named)

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

            >>> agnostic_rows(df_pd, named=False)
            [(1, 6.0, 'a'), (2, 7.0, 'b'), (3, 8.0, 'c')]
            >>> agnostic_rows(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'}]
            >>> agnostic_rows(df_pl, named=False)
            [(1, 6.0, 'a'), (2, 7.0, 'b'), (3, 8.0, 'c')]
            >>> agnostic_rows(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'}]
            >>> agnostic_rows(df_pa, named=False)
            [(1, 6.0, 'a'), (2, 7.0, 'b'), (3, 8.0, 'c')]
            >>> agnostic_rows(df_pa, named=True)
            [{'foo': 1, 'bar': 6.0, 'ham': 'a'}, {'foo': 2, 'bar': 7.0, 'ham': 'b'}, {'foo': 3, 'bar': 8.0, 'ham': 'c'}]
        re  )r.   ri  rh  s     r6   ri  zDataFrame.rows  s    ðZ ×$Ñ$×)Ñ)°Ð)Ó6Ð6r7   )Úbuffer_sizec                ó    y r2   r]   ©r5   rf  rm  s      r6   Ú	iter_rowszDataFrame.iter_rowsË  s    ð %(r7   c                ó    y r2   r]   ro  s      r6   rp  zDataFrame.iter_rowsÐ  s    ð $'r7   c                ó    y r2   r]   ro  s      r6   rp  zDataFrame.iter_rowsÕ  s	    ð @Cr7   i   ©rf  rm  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

        Returns:
            An iterator over the DataFrame of rows.

        Notes:
            cuDF doesn't support this method.

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

            We define a library agnostic function:

            >>> def agnostic_iter_rows(df_native: IntoDataFrame, *, named: bool):
            ...     return nw.from_native(df_native, eager_only=True).iter_rows(named=named)

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

            >>> [row for row in agnostic_iter_rows(df_pd, named=False)]
            [(1, 6.0, 'a'), (2, 7.0, 'b'), (3, 8.0, 'c')]
            >>> [row for row in agnostic_iter_rows(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 agnostic_iter_rows(df_pl, named=False)]
            [(1, 6.0, 'a'), (2, 7.0, 'b'), (3, 8.0, 'c')]
            >>> [row for row in agnostic_iter_rows(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'}]
            >>> [row for row in agnostic_iter_rows(df_pa, named=False)]
            [(1, 6.0, 'a'), (2, 7.0, 'b'), (3, 8.0, 'c')]
            >>> [row for row in agnostic_iter_rows(df_pa, named=True)]
            [{'foo': 1, 'bar': 6.0, 'ham': 'a'}, {'foo': 2, 'bar': 7.0, 'ham': 'b'}, {'foo': 3, 'bar': 8.0, 'ham': 'c'}]
        rs  )r.   rp  ro  s      r6   rp  zDataFrame.iter_rowsÚ  s!    ðf ×$Ñ$×.Ñ.°UÈÐ.ÓTÐTr7   c                ó"    t        |   |i |€S )uß  Add columns to this DataFrame.

        Added columns will replace existing columns with the same name.

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

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

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

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

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

            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:
            ...     return (
            ...         nw.from_native(df_native)
            ...         .with_columns((nw.col("a") * 2).alias("a*2"))
            ...         .to_native()
            ...     )

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

            >>> agnostic_with_columns(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
            >>> agnostic_with_columns(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   â
            âââââââŽâââââââŽââââââââŽââââââ
            >>> agnostic_with_columns(df_pa)
            pyarrow.Table
            a: int64
            b: double
            c: bool
            a*2: int64
            ----
            a: [[1,2,3,4]]
            b: [[0.5,4,10,13]]
            c: [[true,true,false,true]]
            a*2: [[2,4,6,8]]
        ©rV  rk   ©r5   rm   rn   r=   s      r6   rk   zDataFrame.with_columns  s   ø ô` wÑ# UÐ:škÑ:Ð:r7   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.

        Returns:
            The dataframe containing only the selected columns.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6, 7, 8],
            ...     "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 in which we pass the name of a
            column to select that column.

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

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

            >>> agnostic_single_select(df_pd)
               foo
            0    1
            1    2
            2    3
            >>> agnostic_single_select(df_pl)
            shape: (3, 1)
            âââââââ
            â foo â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ
            >>> agnostic_single_select(df_pa)
            pyarrow.Table
            foo: int64
            ----
            foo: [[1,2,3]]

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

            >>> def agnostic_multi_select(df_native: IntoFrameT) -> IntoFrameT:
            ...     return nw.from_native(df_native).select(["foo", "bar"]).to_native()

            >>> agnostic_multi_select(df_pd)
               foo  bar
            0    1    6
            1    2    7
            2    3    8
            >>> agnostic_multi_select(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 6   â
            â 2   â 7   â
            â 3   â 8   â
            âââââââŽââââââ
            >>> agnostic_multi_select(df_pa)
            pyarrow.Table
            foo: int64
            bar: int64
            ----
            foo: [[1,2,3]]
            bar: [[6,7,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:
            ...     return (
            ...         nw.from_native(df_native)
            ...         .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(df_pa)
            pyarrow.Table
            foo: int64
            bar: int64
            ----
            foo: [[1,2,3]]
            bar: [[7,8,9]]

            Use keyword arguments to easily name your expression inputs.

            >>> def agnostic_select_w_kwargs(df_native: IntoFrameT) -> IntoFrameT:
            ...     return (
            ...         nw.from_native(df_native)
            ...         .select(threshold=nw.col("foo") * 2)
            ...         .to_native()
            ...     )

            >>> agnostic_select_w_kwargs(df_pd)
               threshold
            0          2
            1          4
            2          6
            >>> agnostic_select_w_kwargs(df_pl)
            shape: (3, 1)
            âââââââââââââ
            â threshold â
            â ---       â
            â i64       â
            âââââââââââââ¡
            â 2         â
            â 4         â
            â 6         â
            âââââââââââââ
            >>> agnostic_select_w_kwargs(df_pa)
            pyarrow.Table
            threshold: int64
            ----
            threshold: [[2,4,6]]
        ©rV  rp   rw  s      r6   rp   zDataFrame.selecta  s   ø ô| w~uÐ4šÑ4Ð4r7   c                ó"    t         |   |«      S )u~  Rename column names.

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

        Returns:
            The dataframe with the specified columns renamed.

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

            We define a library agnostic function:

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

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

            >>> agnostic_rename(df_pd)
               apple  bar ham
            0      1    6   a
            1      2    7   b
            2      3    8   c
            >>> agnostic_rename(df_pl)
            shape: (3, 3)
            âââââââââ¬ââââââ¬ââââââ
            â apple â bar â ham â
            â ---   â --- â --- â
            â i64   â i64 â str â
            âââââââââªââââââªââââââ¡
            â 1     â 6   â a   â
            â 2     â 7   â b   â
            â 3     â 8   â c   â
            âââââââââŽââââââŽââââââ
            >>> agnostic_rename(df_pa)
            pyarrow.Table
            apple: int64
            bar: int64
            ham: string
            ----
            apple: [[1,2,3]]
            bar: [[6,7,8]]
            ham: [["a","b","c"]]
        ©rV  rr   ©r5   rs   r=   s     r6   rr   zDataFrame.rename  s   ø ôl w~gÓ&Ð&r7   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)`.

        Returns:
            A subset of the dataframe of shape (n, n_columns).

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

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

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

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

            >>> agnostic_head(df_pd)
               foo  bar ham
            0    1    6   a
            1    2    7   b
            2    3    8   c
            >>> agnostic_head(df_pl)
            shape: (3, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            â 2   â 7   â b   â
            â 3   â 8   â c   â
            âââââââŽââââââŽââââââ
            >>> agnostic_head(df_pa)
            pyarrow.Table
            foo: int64
            bar: int64
            ham: string
            ----
            foo: [[1,2,3]]
            bar: [[6,7,8]]
            ham: [["a","b","c"]]
        ©rV  ru   ©r5   rw   r=   s     r6   ru   zDataFrame.head9  ó   ø ôv w|AÐr7   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)`.

        Returns:
            A subset of the dataframe of shape (n, n_columns).

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

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

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

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

            >>> agnostic_tail(df_pd)
               foo  bar ham
            2    3    8   c
            3    4    9   d
            4    5   10   e
            >>> agnostic_tail(df_pl)
            shape: (3, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 3   â 8   â c   â
            â 4   â 9   â d   â
            â 5   â 10  â e   â
            âââââââŽââââââŽââââââ
            >>> agnostic_tail(df_pa)
            pyarrow.Table
            foo: int64
            bar: int64
            ham: string
            ----
            foo: [[3,4,5]]
            bar: [[8,9,10]]
            ham: [["c","d","e"]]
        ©rV  rz   r  s     r6   rz   zDataFrame.tailv  r  r7   r|   c               ó4    t        |   t        |«      d|iS )um
  Remove columns from the dataframe.

        Returns:
            The dataframe with the specified columns removed.

        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 pyarrow as pa
            >>> 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)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            We define a library agnostic function:

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

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

            >>> agnostic_drop(df_pd)
               foo  bar
            0    1  6.0
            1    2  7.0
            2    3  8.0
            >>> agnostic_drop(df_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â f64 â
            âââââââªââââââ¡
            â 1   â 6.0 â
            â 2   â 7.0 â
            â 3   â 8.0 â
            âââââââŽââââââ
            >>> agnostic_drop(df_pa)
            pyarrow.Table
            foo: int64
            bar: double
            ----
            foo: [[1,2,3]]
            bar: [[6,7,8]]

            Use positional arguments to drop multiple columns.

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

            >>> agnostic_drop_multi(df_pd)
               bar
            0  6.0
            1  7.0
            2  8.0
            >>> agnostic_drop_multi(df_pl)
            shape: (3, 1)
            âââââââ
            â bar â
            â --- â
            â f64 â
            âââââââ¡
            â 6.0 â
            â 7.0 â
            â 8.0 â
            âââââââ
            >>> agnostic_drop_multi(df_pa)
            pyarrow.Table
            bar: double
            ----
            bar: [[6,7,8]]

        r}   ©rV  r~   r   ©r5   r}   ri   r=   s      r6   r~   zDataFrame.drop³  s   ø ôd w|W WÓ-Ð=°fÑ=Ð=r7   Úany©ÚkeepÚmaintain_orderc               ó°    |dvrdd d| }t        |«      t        |t        «      r|g}| j                  | j                  j                  |||¬«      «      S )u:	  Drop duplicate rows from this dataframe.

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

                * 'any': Does not give any guarantee of which row is kept.
                        This allows more optimizations.
                * 'none': Don't keep duplicate rows.
                * 'first': Keep first unique row.
                * 'last': Keep last unique row.
            maintain_order: Keep the same order as the original DataFrame. This may be more
                expensive to compute.

        Returns:
            The dataframe with the duplicate rows removed.

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

            We define a library agnostic function:

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

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

            >>> agnostic_unique(df_pd)
               foo bar ham
            0    1   a   b
            >>> agnostic_unique(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â str â str â
            âââââââªââââââªââââââ¡
            â 1   â a   â b   â
            âââââââŽââââââŽââââââ
            >>> agnostic_unique(df_pa)
            pyarrow.Table
            foo: int64
            bar: string
            ham: string
            ----
            foo: [[1]]
            bar: [["a"]]
            ham: [["b"]]
        >   r  ÚlastÚnoneÚfirstz	Expected )r  r  r  r  z, got: )rf   r  r  )r§   rN   rQ   r?   r.   Úunique©r5   rf   r  r  rU   s        r6   r  zDataFrame.unique  sp    ðL Ð7Ñ7ØÐ<Ð=žWÀTÀFÐKCÜS/Ð!ÜfcÔ"ØXFØ×-Ñ-Ø×!Ñ!×(Ñ(Ø Džð )ó ó
ð 	
r7   c                ó"    t        |   |i |€S )uC  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 &.

        Returns:
            The filtered dataframe.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6, 7, 8],
            ...     "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 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 any supported library such as Pandas, Polars, or PyArrow
            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(df_pa)
            pyarrow.Table
            foo: int64
            bar: int64
            ham: string
            ----
            foo: [[2,3]]
            bar: [[7,8]]
            ham: [["b","c"]]

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

            >>> 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(df_pa)
            pyarrow.Table
            foo: int64
            bar: int64
            ham: string
            ----
            foo: [[1]]
            bar: [[6]]
            ham: [["a"]]

            >>> def agnostic_filter(df_native: IntoFrameT) -> IntoFrameT:
            ...     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   â
            âââââââŽââââââŽââââââ
            >>> agnostic_filter(df_pa)
            pyarrow.Table
            foo: int64
            bar: int64
            ham: string
            ----
            foo: [[1,3]]
            bar: [[6,8]]
            ham: [["a","c"]]

            Provide multiple filters using `*args` syntax:

            >>> def agnostic_filter(df_native: IntoFrameT) -> IntoFrameT:
            ...     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   â
            âââââââŽââââââŽââââââ
            >>> agnostic_filter(df_pa)
            pyarrow.Table
            foo: int64
            bar: int64
            ham: string
            ----
            foo: [[1]]
            bar: [[6]]
            ham: [["a"]]

            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(df_pa)
            pyarrow.Table
            foo: int64
            bar: int64
            ham: string
            ----
            foo: [[2]]
            bar: [[7]]
            ham: [["b"]]
        )rV  r   )r5   r   r   r=   s      r6   r   zDataFrame.filterX  s   ø ôj w~zÐ9š[Ñ9Ð9r7   ©Údrop_null_keysc               ó    ddl m ddlm} ddlm t        |«      }t        fd|D «       «      rd}t        |«       || g|¢­d|iS )ui  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 pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoDataFrameT
            >>> data = {
            ...     "a": ["a", "b", "a", "b", "c"],
            ...     "b": [1, 2, 1, 3, 3],
            ...     "c": [5, 4, 3, 2, 1],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            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: IntoDataFrameT) -> IntoDataFrameT:
            ...     df = nw.from_native(df_native, eager_only=True)
            ...     return df.group_by("a").agg(nw.col("b").sum()).sort("a").to_native()

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

            >>> 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(df_pa)
            pyarrow.Table
            a: string
            b: int64
            ----
            a: [["a","b","c"]]
            b: [[2,5,3]]

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

            >>> def agnostic_group_by_agg(df_native: IntoDataFrameT) -> IntoDataFrameT:
            ...     df = nw.from_native(df_native, eager_only=True)
            ...     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(df_pa)
            pyarrow.Table
            a: string
            b: int64
            c: int64
            ----
            a: [["a","b","b","c"]]
            b: [[1,2,3,3]]
            c: [[5,4,2,1]]
        r   rI   r   r"   c              3  ó:   K   | ]  }t        |f«        y ­wr2   ©rN   ©r   r   rJ   r#   s     r6   r   z%DataFrame.group_by.<locals>.<genexpr>t	  ó   øè ø Ð@±i°z!d F^×,±iùó   ú`group_by` with expression or Series keys is not (yet?) supported.

Hint: instead of `df.group_by(nw.col('a'))`, use `df.group_by('a')`.r  )	rL   rJ   Únarwhals.group_byr   rM   r#   r   r  rŠ   )r5   r  Úkeysr   Ú	flat_keysrU   rJ   r#   s         @@r6   Úgroup_byzDataFrame.group_by	  sS   ù õ@ 	'Ý-Ý*äDM	ÜÔ@±iÓ@Ô@ðWð ô & cÓ*Ð*ÙtÐGiÒGžÑGÐGr7   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.

        Returns:
            The sorted dataframe.

        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 pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>> data = {
            ...     "a": [1, 2, None],
            ...     "b": [6.0, 5.0, 4.0],
            ...     "c": ["a", "c", "b"],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(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 any supported library such as Pandas, Polars, or PyArrow
            to `agnostic_sort`:

            >>> 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(df_pl)
            shape: (3, 3)
            ââââââââ¬ââââââ¬ââââââ
            â a    â b   â c   â
            â ---  â --- â --- â
            â i64  â f64 â str â
            ââââââââªââââââªââââââ¡
            â 1    â 6.0 â a   â
            â null â 4.0 â b   â
            â 2    â 5.0 â c   â
            ââââââââŽââââââŽââââââ
            >>> agnostic_sort(df_pa)
            pyarrow.Table
            a: int64
            b: double
            c: string
            ----
            a: [[1,null,2]]
            b: [[6,4,5]]
            c: [["a","b","c"]]
        r   ©rV  r   ©r5   r   r   r   r   r=   s        r6   r   zDataFrame.sort|	  s!   ø ôV w|BÐW ÑW°ZÈJÒWÐWr7   r   r   c               ó.    t         |   ||||||¬«      S )u0  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 pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6.0, 7.0, 8.0],
            ...     "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)

            >>> df_pa = pa.table(data)
            >>> other_pa = pa.table(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 then pass any supported library such as Pandas, Polars, or PyArrow
            to `agnostic_join_on_ham`:

            >>> 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(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     â
            âââââââŽââââââŽââââââŽââââââââ
            >>> agnostic_join_on_ham(df_pa, other_pa)
            pyarrow.Table
            foo: int64
            bar: double
            ham: string
            apple: string
            ----
            foo: [[1,2]]
            bar: [[6,7]]
            ham: [["a","b"]]
            apple: [["x","y"]]
        ©r¥   r   r   rª   r   ©rV  rš   ©r5   r©   rª   r¥   r   r   r   r=   s          r6   rš   zDataFrame.joinÉ	  s)   ø ô| w|Øs G°hÀ2Èfð ó 
ð 	
r7   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
            >>> from typing import Literal
            >>> import pandas as pd
            >>> import polars as pl
            >>> import narwhals as nw
            >>> 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.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:

            >>> 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 then pass any supported library such as Pandas or Polars
            to `agnostic_join_asof_datetime`:

            >>> 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")
            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:

            >>> 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)
            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³   ©rV  rº   ©
r5   r©   r   r   rª   rŽ   rµ   r   r¶   r=   s
            r6   rº   zDataFrame.join_asof+
  s5   ø ôd wÑ ØØØØØØØØð !ó 	
ð 		
r7   c                ól    | j                  | j                  j                  «       | j                  ¬«      S )aŽ  Get a mask of all duplicated rows in this DataFrame.

        Returns:
            A new Series.

        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, 3, 1],
            ...     "b": ["x", "y", "z", "x"],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_is_duplicated(df_native: IntoDataFrame) -> IntoSeries:
            ...     df = nw.from_native(df_native, eager_only=True)
            ...     return df.is_duplicated().to_native()

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

            >>> agnostic_is_duplicated(df_pd)
            0     True
            1    False
            2    False
            3     True
            dtype: bool

            >>> agnostic_is_duplicated(df_pl)  # doctest: +NORMALIZE_WHITESPACE
            shape: (4,)
            Series: '' [bool]
            [
                true
                false
                false
                true
            ]
            >>> agnostic_is_duplicated(df_pa)  # doctest: +ELLIPSIS
            <pyarrow.lib.ChunkedArray object at ...>
            [
              [
                true,
                false,
                false,
                true
              ]
            ]
        r;   )rí   r.   Úis_duplicatedr0   r4   s    r6   r©  zDataFrame.is_duplicatedé
  s4    ðr ||Ø×!Ñ!×/Ñ/Ó1Ø++ð ó 
ð 	
r7   c                ó6    | j                   j                  «       S )aà  Check if the dataframe is empty.

        Returns:
            A boolean indicating whether the dataframe is empty (True) or not (False).

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoDataFrame

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

            >>> def agnostic_is_empty(df_native: IntoDataFrame) -> bool:
            ...     df = nw.from_native(df_native, eager_only=True)
            ...     return df.filter(nw.col("foo") > 10).is_empty()

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

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

            >>> data = {"foo": [100, 2, 3], "bar": [4, 5, 6]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)
            >>> agnostic_is_empty(df_pd), agnostic_is_empty(df_pl), agnostic_is_empty(df_pa)
            (False, False, False)
        )r.   Úis_emptyr4   s    r6   r«  zDataFrame.is_empty'  s    ðJ ×$Ñ$×-Ñ-Ó/Ð/r7   c                ól    | j                  | j                  j                  «       | j                  ¬«      S )a  Get a mask of all unique rows in this DataFrame.

        Returns:
            A new Series.

        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, 3, 1],
            ...     "b": ["x", "y", "z", "x"],
            ... }
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

            Let's define a dataframe-agnostic function:

            >>> def agnostic_is_unique(df_native: IntoDataFrame) -> IntoSeries:
            ...     df = nw.from_native(df_native, eager_only=True)
            ...     return df.is_unique().to_native()

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

            >>> agnostic_is_unique(df_pd)
            0    False
            1     True
            2     True
            3    False
            dtype: bool

            >>> agnostic_is_unique(df_pl)  # doctest: +NORMALIZE_WHITESPACE
            shape: (4,)
            Series: '' [bool]
            [
                false
                 true
                 true
                false
            ]
            >>> agnostic_is_unique(df_pa)  # doctest: +ELLIPSIS
            <pyarrow.lib.ChunkedArray object at ...>
            [
              [
                false,
                true,
                true,
                false
              ]
            ]
        r;   )rí   r.   Ú	is_uniquer0   r4   s    r6   r­  zDataFrame.is_uniqueN  s4    ðr ||Ø×!Ñ!×+Ñ+Ó-Ø++ð ó 
ð 	
r7   c                óT    | j                  | j                  j                  «       «      S )uô  Create a new DataFrame that shows the null counts per column.

        Returns:
            A dataframe of shape (1, n_columns).

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

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>> data = {
            ...     "foo": [1, None, 3],
            ...     "bar": [6, 7, None],
            ...     "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 that returns the null count of
            each columns:

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

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

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

            >>> agnostic_null_count(df_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â u32 â u32 â u32 â
            âââââââªââââââªââââââ¡
            â 1   â 1   â 0   â
            âââââââŽââââââŽââââââ

            >>> agnostic_null_count(df_pa)
            pyarrow.Table
            foo: int64
            bar: int64
            ham: int64
            ----
            foo: [[1]]
            bar: [[1]]
            ham: [[0]]
        )r?   r.   Ú
null_countr4   s    r6   r¯  zDataFrame.null_count  s%    ðx ×-Ñ-šd×.CÑ.C×.NÑ.NÓ.PÓQÐQr7   c                ó<    | j                   j                  ||¬«      S )a  Return the DataFrame as a scalar, or return the element at the given row/column.

        Arguments:
            row: The *n*-th row.
            column: The column selected via an integer or a string (column name).

        Returns:
            A scalar or the specified element in the dataframe.

        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 pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoDataFrame
            >>> data = {"a": [1, 2, 3], "b": [4, 5, 6]}
            >>> df_pd = pd.DataFrame(data)
            >>> df_pl = pl.DataFrame(data)
            >>> df_pa = pa.table(data)

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

            >>> def agnostic_item(
            ...     df_native: IntoDataFrame, row: int | None, column: int | str | None
            ... ):
            ...     df = nw.from_native(df_native, eager_only=True)
            ...     return df.item(row, column)

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

            >>> agnostic_item(df_pd, 1, 1), agnostic_item(df_pd, 2, "b")
            (np.int64(5), np.int64(6))
            >>> agnostic_item(df_pl, 1, 1), agnostic_item(df_pl, 2, "b")
            (5, 6)
            >>> agnostic_item(df_pa, 1, 1), agnostic_item(df_pa, 2, "b")
            (5, 6)
        )rS  Úcolumn)r.   r+  )r5   rS  r±  s      r6   r+  zDataFrame.itemÊ  s!    ðV ×$Ñ$×)Ñ)šcž&Ð)ÓAÐAr7   c                ó     t         |   «       S )u/  Create a copy of this DataFrame.

        Returns:
            An identical copy of the original dataframe.

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

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

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

            We can then pass any supported library such as Pandas or Polars
            to `agnostic_clone`:

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

            >>> agnostic_clone(df_pl)
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 3   â
            â 2   â 4   â
            âââââââŽââââââ
        ©rV  r­   r`  s    r6   r­   zDataFrame.clone÷  s   ø ôR w}Ðr7   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.

        Returns:
            The dataframe containing only the selected rows.

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

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

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

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

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

            >>> agnostic_gather_every(df_pl)
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 2   â 6   â
            â 4   â 8   â
            âââââââŽââââââ
            >>> agnostic_gather_every(df_pa)
            pyarrow.Table
            a: int64
            b: int64
            ----
            a: [[2,4]]
            b: [[6,8]]
        r¯   ©rV  r±   ©r5   rw   r°   r=   s      r6   r±   zDataFrame.gather_every"  s   ø ôl wÑ# a°Ð#Ó7Ð7r7   Ú_)rŸ   ÚvaluesÚaggregate_functionr  Úsort_columnsÚ	separatorc          
     óÀ    ||d}t        |«      |d}t        |t        t        «       ¬«       | 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: Has no effect and is kept around only for backwards-compatibility.
            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.

        Returns:
            A new dataframe.

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

            Let's define a dataframe-agnostic function:

            >>> def agnostic_pivot(df_native: IntoDataFrameT) -> IntoDataFrameT:
            ...     df = nw.from_native(df_native, eager_only=True)
            ...     return df.pivot("col", index="ix", aggregate_function="sum").to_native()

            We can then pass any supported library such as Pandas or Polars
            to `agnostic_pivot`:

            >>> agnostic_pivot(df_pd)
               ix  foo_a  foo_b  bar_a  bar_b
            0   1      1      7      2      9
            1   2      4      1      0      4
            >>> agnostic_pivot(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     â
            âââââââŽââââââââŽââââââââŽââââââââŽââââââââ
        z3At least one of `values` and `index` must be passedúx`maintain_order` has no effect and is only kept around for backwards-compatibility. You can safely remove this argument.©ÚmessageÚcategoryÚ
stacklevel)rª   rŸ   rž  r¹  rº  r»  )r§   r   ÚUserWarningr   r?   r.   Úpivot)	r5   rª   rŸ   rž  r¹  r  rº  r»  rU   s	            r6   rÃ  zDataFrame.pivotZ  s|    ð` >emØGCÜS/Ð!ØÐ%ð7ð ô €{ŒÓ?PÕQà×-Ñ-Ø×!Ñ!×'Ñ'ØØØØ#5Ø)Ø#ð (ó ó	
ð 		
r7   c                ó6    | j                   j                  «       S )aÄ  Convert to arrow table.

        Returns:
            A new PyArrow table.

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoDataFrame
            >>> data = {"foo": [1, 2, 3], "bar": ["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 that converts to arrow table:

            >>> def agnostic_to_arrow(df_native: IntoDataFrame) -> pa.Table:
            ...     df = nw.from_native(df_native, eager_only=True)
            ...     return df.to_arrow()

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

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

            >>> agnostic_to_arrow(df_pl)
            pyarrow.Table
            foo: int64
            bar: large_string
            ----
            foo: [[1,2,3]]
            bar: [["a","b","c"]]

            >>> agnostic_to_arrow(df_pa)
            pyarrow.Table
            foo: int64
            bar: string
            ----
            foo: [[1,2,3]]
            bar: [["a","b","c"]]
        )r.   r  r4   s    r6   r  zDataFrame.to_arrow¿  s    ðd ×$Ñ$×-Ñ-Ó/Ð/r7   )ÚfractionÚwith_replacementÚseedc               ó^    | j                  | j                  j                  ||||¬«      «      S )u  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.

        Returns:
            A new dataframe.

        Notes:
            The results may not be consistent across libraries.

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

            We define a library agnostic function:

            >>> def agnostic_sample(df_native: IntoDataFrameT) -> IntoDataFrameT:
            ...     df = nw.from_native(df_native, eager_only=True)
            ...     return df.sample(n=2, seed=123).to_native()

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

            >>> agnostic_sample(df_pd)
               a  b
            3  4  y
            0  1  x
            >>> agnostic_sample(df_pl)
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â str â
            âââââââªââââââ¡
            â 2   â y   â
            â 3   â x   â
            âââââââŽââââââ
            >>> agnostic_sample(df_pa)
            pyarrow.Table
            a: int64
            b: string
            ----
            a: [[1,3]]
            b: [["x","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.
        )rw   rÅ  rÆ  rÇ  )r?   r.   Úsample)r5   rw   rÅ  rÆ  rÇ  s        r6   rÉ  zDataFrame.sampleó  s<    ðH ×-Ñ-Ø×!Ñ!×(Ñ(ØhÐ9IÐPTð )ó ó
ð 	
r7   ©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".

        Returns:
            The unpivoted dataframe.

        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 pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>> data = {
            ...     "a": ["x", "y", "z"],
            ...     "b": [1, 3, 5],
            ...     "c": [2, 4, 6],
            ... }

            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").to_native()

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

            >>> agnostic_unpivot(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     â
            âââââââŽâââââââââââŽââââââââ

            >>> agnostic_unpivot(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

            >>> agnostic_unpivot(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œ   ©rV  rÁ   ©r5   rª   rŸ   r¿   rÀ   r=   s        r6   rÁ   zDataFrame.unpivot=  s%   ø ôn wØšmÈ
ð ó 
ð 	
r7   c                ó$    t        |   |g|¢­ S )u	  Explode the dataframe to long format by exploding the given columns.

        Notes:
            It is possible to explode multiple columns only if these columns must have
            matching element counts.

        Arguments:
            columns: Column names. The underlying columns being exploded must be of the `List` data type.
            *more_columns: Additional names of columns to explode, specified as positional arguments.

        Returns:
            New DataFrame

        Examples:
            >>> import pandas as pd
            >>> import polars as pl
            >>> import pyarrow as pa
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>> data = {
            ...     "a": ["x", "y", "z", "w"],
            ...     "lst1": [[1, 2], None, [None], []],
            ...     "lst2": [[3, None], None, [42], []],
            ... }

            We define a library agnostic function:

            >>> def agnostic_explode(df_native: IntoFrameT) -> IntoFrameT:
            ...     return (
            ...         nw.from_native(df_native)
            ...         .with_columns(nw.col("lst1", "lst2").cast(nw.List(nw.Int32())))
            ...         .explode("lst1", "lst2")
            ...         .to_native()
            ...     )

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

            >>> agnostic_explode(pd.DataFrame(data))
               a  lst1  lst2
            0  x     1     3
            0  x     2  <NA>
            1  y  <NA>  <NA>
            2  z  <NA>    42
            3  w  <NA>  <NA>
            >>> agnostic_explode(pl.DataFrame(data))
            shape: (5, 3)
            âââââââ¬âââââââ¬âââââââ
            â a   â lst1 â lst2 â
            â --- â ---  â ---  â
            â str â i32  â i32  â
            âââââââªâââââââªâââââââ¡
            â x   â 1    â 3    â
            â x   â 2    â null â
            â y   â null â null â
            â z   â null â 42   â
            â w   â null â null â
            âââââââŽâââââââŽâââââââ
        ©rV  rÊ   ©r5   ri   rË   r=   s      r6   rÊ   zDataFrame.explode  s   ø ôx wwÐ6šÒ6Ð6r7   )rÌ   ztype[Series[Any]])rÌ   ztype[LazyFrame[Any]]©r>   r   r<   r/   rÌ   ÚNone©rÌ   r(   )rÌ   r×   )NN)r  r   rÿ   úbool | NonerÌ   ú
np.ndarray©rÌ   rQ   r2   )r  zobject | NonerÌ   rã   )rÌ   zLazyFrame[Any])rÌ   r+   )rÌ   zpd.DataFrame)r  rÒ  rÌ   rQ   )r  zstr | Path | BytesIOrÌ   rÒ  )r  zstr | Path | BytesIO | NonerÌ   rá   )rÌ   rÕ  )rÌ   ztuple[int, int])rc   rQ   rÌ   úSeries[Any])Úb)r'  r'   rÌ   zint | float)r+  ztuple[Sequence[int], slice]rÌ   r   )r+  z#tuple[Sequence[int], Sequence[int]]rÌ   r   )r+  ztuple[slice, Sequence[int]]rÌ   r   )r+  ztuple[Sequence[int], str]rÌ   r×  )r+  ztuple[slice, str]rÌ   r×  )r+  z#tuple[Sequence[int], Sequence[str]]rÌ   r   )r+  ztuple[slice, Sequence[str]]rÌ   r   )r+  ztuple[Sequence[int], int]rÌ   r×  )r+  ztuple[slice, int]rÌ   r×  )r+  zSequence[int]rÌ   r   )r+  rQ   rÌ   r×  )r+  zSequence[str]rÌ   r   )r+  rB  rÌ   r   )r+  ztuple[slice, slice]rÌ   r   )r+  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)rF  rQ   rÌ   r   )rI  úLiteral[True]rÌ   zdict[str, Series[Any]])rI  úLiteral[False]rÌ   zdict[str, list[Any]])rI  r   rÌ   z-dict[str, Series[Any]] | dict[str, list[Any]])rŸ   r×   rÌ   ztuple[Any, ...]rÎ   rÑ   rÏ   rÐ   rÍ   ©r5   r   rÌ   r   rÓ   )rf  rÚ  rÌ   zlist[tuple[Any, ...]])rf  rÙ  rÌ   zlist[dict[str, Any]])rf  r   rÌ   z,list[tuple[Any, ...]] | list[dict[str, Any]])rf  rÚ  rm  r×   rÌ   zIterator[tuple[Any, ...]])rf  rÙ  rm  r×   rÌ   zIterator[dict[str, Any]])rf  r   rm  r×   rÌ   z4Iterator[tuple[Any, ...]] | Iterator[dict[str, Any]]rÔ   rÕ   ©é   rÖ   ©ri   rÚ   r}   r   rÌ   r   )rf   rÒ   r  z'Literal['any', 'first', 'last', 'none']r  r   rÌ   r   rØ   )r  rÚ   r  r   rÌ   zGroupBy[Self]rÙ   rÛ   rÜ   rà   )r5   r   rÌ   r×  )r5   r   rÌ   r   )r5   r   rÌ   r   )r5   r   rS  ú
int | Noner±  zint | str | NonerÌ   r   rÝ   rÞ   rß   )r5   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»  rQ   rÌ   r   )r5   r   rÌ   zpa.Table)r5   r   rw   rß  rÅ  zfloat | NonerÆ  r   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(  r,  rG  rL  rS  r_   rg   ra   rW   rZ   ri   ri  rp  rk   rp   rr   ru   rz   r~   r  r   r  r   rš   rº   r©  r«  r­  r¯  r+  r­   r±   rÃ  r  rÉ  rÁ   rÊ   Ú__classcell__©r=   s   @r6   rë   rë   Q  s  ø ñð0 òó ðð
 òó ðð&àð&ð 6ð	&ð
 
ó&ð ò5ó ð5ó2/ôAóPôNó./Kób,3ó\,1ð\ Û6ó Ø6àÚ@ó Ø@ô)5óV#2óJ)0ðV ò"+ó ð"+óH<
ô|*?ð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ðA!ð"ðA!ð 
óA!óF#ð Ø47ÔWó ØWØÚPó ØPØð<Ø ð<à	6ò<ó ð<ð $(ñ8BØ ð8Bà	6ó8Bót+0õ\87öt71ör9,ðv ô&ó ð&õP&(ðP ô!ó ð!ðF Ø.3ÔRó ØRàÚHó ØHàÚWó ØWð  %ñ-7Øð-7à	5ó-7ð^ à;>ñ(Ø&ð(Ø58ð(à	"ò(ó ð(ð à:=ñ'Ø%ð'Ø47ð'à	!ò'ó ð'ð à14ñCØðCØ+.ðCà	=òCó ðCð
  %žñ3UØð3UØ36ð3Uà	=ó3UðjP;Ø3ðP;ØDLðP;à	õP;ðd^5à-ð^5ð  ð^5ð 
õ	^5õ@6'öp;öz;ðz BF÷ R>ðl *.ðO
ð 9>Ø$ñO
à&ðO
ð 6ð	O
ð
 ðO
ð 
óO
ðbu:ØEðu:ØVYðu:à	õu:ðp BGñkHØ(ðkHØ:>ðkHà	ókHðb -2Ø ñKXàðKXð ðKXð *ð	KXð
 ðKXð 
õKXð` &*ØAHð	`
ð +/Ø+/Øñ`
àð`
ð #ð`
ð ?ð	`
ð (ð`
ð )ð`
ð ð`
ð 
õ`
ðL #Ø#ØØ*.Ø+/Ø%)Ø>Hñ{
àð{
ð ð	{
ð
 ð{
ð ð{
ð (ð{
ð )ð{
ð #ð{
ð <ð{
ð 
õ{
ó|<
ó|%0óN<
ó|<Rô|+BõZ)öV68ðx )-Ø)-ð Ø&*Ø"Øñc
Øðc
àðc
ð &ð	c
ð
 'ðc
ððc
ð $ðc
ð ðc
ð ðc
ð 
óc
óJ20ðl ðH
ð "&Ø!&ØñH
ØðH
àðH
ð ð	H
ð
 ðH
ð ðH
ð 
óH
ðX &*ðY
ð )-Ø$(Ø!%ñY
ØðY
à"ðY
ð &ð	Y
ð
 "ðY
ð ðY
ð 
õY
÷v<7ñ <7r7   rë   c                  ó    e Zd ZdZed-d«       Z	 	 	 	 	 	 d.dZd/dZed0d«       Zd1dZ	d2dZ
d3dZd4 fd	Zd5d6 fdZd7d8 fdZed9 fd«       Zd: 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	 d5dd
d	 	 	 	 	 	 	 dAdZ	 	 	 	 	 	 dB fdZdd	 	 	 	 	 dCdZddd	 	 	 	 	 	 	 	 	 dD fd Z	 	 dEd
d
d!d"	 	 	 	 	 	 	 	 	 	 	 	 	 dF fd#Zd
d
d
d
d
d
d$d%	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dG fd&ZdH fd'ZdHd(ZdIdJ fd)Z 	 d5d
d
d
d*	 	 	 	 	 	 	 	 	 	 	 dK fd+Z!dL fd,Z" xZ#S )Mrï   a  Narwhals LazyFrame, backed by a native lazyframe.

    !!! warning
        This class is not meant to be instantiated directly - instead use
        [`narwhals.from_native`][] with a native
        object that is a lazy dataframe from one of the supported
        backend (e.g. polars.LazyFrame, dask_expr._collection.DataFrame):
        ```py
        narwhals.from_native(native_lazyframe)
        ```
    c                ó    t         S r2   )rë   r4   s    r6   Ú
_dataframezLazyFrame._dataframeä  rñ   r7   c               ó    || _         t        |d«      r|j                  «       | _        y dt	        |«       }t        |«      )NÚ__narwhals_lazyframe__zVExpected Polars LazyFrame or an object that implements `__narwhals_lazyframe__`, got: )r0   rô   rç  r.   rR   rõ   rö   s       r6   r÷   zLazyFrame.__init__è  sF    ð Ü2Ð/Ô0Ø)+×)BÑ)BÓ)DDÕ!àjÔkoÐprÓksÐjtÐuCÜ  Ó%Ð%r7   c                óR    t        d| j                  «       j                  «       «      S )NzNarwhals LazyFramer  r4   s    r6   r  zLazyFrame.__repr__õ  r  r7   c                ó.    | j                   j                  S )a²  Return implementation of native frame.

        This can be useful when you need to use special-casing 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
            >>> import dask.dataframe as dd
            >>> lf_pl = pl.LazyFrame({"a": [1, 2, 3]})
            >>> lf_dask = dd.from_dict({"a": [1, 2, 3]}, npartitions=2)

            >>> lf = nw.from_native(lf_pl)
            >>> lf.implementation
            <Implementation.POLARS: 6>
            >>> lf.implementation.is_pandas()
            False
            >>> lf.implementation.is_polars()
            True

            >>> lf = nw.from_native(lf_dask)
            >>> lf.implementation
            <Implementation.DASK: 7>
            >>> lf.implementation.is_dask()
            True
        rù   r4   s    r6   rû   zLazyFrame.implementationø  s    ð> ×$Ñ$×4Ñ4Ð4r7   c                ó    d}t        |«      )Nz%Slicing is not supported on LazyFrame)rS   rD  s      r6   r,  zLazyFrame.__getitem__  s    Ø5ÜnÐr7   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
            >>> import dask.dataframe as dd
            >>> data = {
            ...     "a": ["a", "b", "a", "b", "b", "c"],
            ...     "b": [1, 2, 3, 4, 5, 6],
            ...     "c": [6, 5, 4, 3, 2, 1],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            >>> lf = nw.from_native(lf_pl)
            >>> lf  # doctest:+ELLIPSIS
            âââââââââââââââââââââââââââââââ
            |     Narwhals LazyFrame      |
            |-----------------------------|
            |<LazyFrame at ...
            âââââââââââââââââââââââââââââââ
            >>> 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   â
            âââââââŽââââââŽââââââ

            >>> lf = nw.from_native(lf_dask)
            >>> lf
            âââââââââââââââââââââââââââââââââââââ
            |        Narwhals LazyFrame         |
            |-----------------------------------|
            |Dask DataFrame Structure:          |
            |                    a      b      c|
            |npartitions=2                      |
            |0              string  int64  int64|
            |3                 ...    ...    ...|
            |5                 ...    ...    ...|
            |Dask Name: frompandas, 1 expression|
            |Expr=df                            |
            âââââââââââââââââââââââââââââââââââââ
            >>> df = lf.group_by("a").agg(nw.col("b", "c").sum()).collect()
            >>> df.to_native()
               a   b   c
            0  a   4  10
            1  b  11  10
            2  c   6   1
        Úfullr;   )rå  r.   Úcollectr4   s    r6   rí  zLazyFrame.collect  s0    ðv Ø×!Ñ!×)Ñ)Ó+Øð ó 
ð 	
r7   c                ó    t        | d¬«      S )uí  Convert Narwhals LazyFrame to native one.

        Returns:
            Object of class that user started with.

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>>
            >>> data = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

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

            >>> 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   â
            âââââââŽââââââŽââââââ
            >>> nw.from_native(lf_dask).to_native().compute()
               foo  bar ham
            0    1  6.0   a
            1    2  7.0   b
            2    3  8.0   c
        F)Únarwhals_objectÚpass_throughr   r4   s    r6   r   zLazyFrame.to_native]  s    ôD šžEÔBÐBr7   c                ó*    t        |   |g|¢­i |€S )u  Pipe function call.

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

        Returns:
            The original object with the function applied.

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1, 2, 3], "ba": [4, 5, 6]}
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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")).collect().to_native()

            We can then pass any supported library such as Polars or Dask to `agnostic_pipe`:

            >>> agnostic_pipe(lf_pl)
            shape: (3, 1)
            âââââââ
            â a   â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ
            >>> agnostic_pipe(lf_dask)
               a
            0  1
            1  2
            2  3
        rU  rW  s       r6   r_   zLazyFrame.pipe  s   ø ô\ w|HÐ6 tÒ6švÑ6Ð6r7   Nc                ó$    t         |   |¬«      S )u  Drop rows that contain null values.

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

        Returns:
            The original object with the rows removed that contained the null values.

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

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1.0, 2.0, None], "ba": [1.0, None, 2.0]}
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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().collect().to_native()

            We can then pass any supported library such as Polars or Dask to `agnostic_drop_nulls`:

            >>> agnostic_drop_nulls(lf_pl)
            shape: (1, 2)
            âââââââ¬ââââââ
            â a   â ba  â
            â --- â --- â
            â f64 â f64 â
            âââââââªââââââ¡
            â 1.0 â 1.0 â
            âââââââŽââââââ
            >>> agnostic_drop_nulls(lf_dask)
                 a   ba
            0  1.0  1.0
        re   rY  rZ  s     r6   rg   zLazyFrame.drop_nulls²  s   ø ô\ wÑ!šÐ!Ó0Ð0r7   c                ó"    t         |   |«      S )u{  Insert column which enumerates rows.

        Arguments:
            name: The name of the column as a string. The default is "index".

        Returns:
            The original object with the column added.

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1, 2, 3], "b": [4, 5, 6]}
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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().collect().to_native()

            We can then pass any supported library such as Polars or Dask to `agnostic_with_row_index`:

            >>> agnostic_with_row_index(lf_pl)
            shape: (3, 3)
            âââââââââ¬ââââââ¬ââââââ
            â index â a   â b   â
            â ---   â --- â --- â
            â u32   â i64 â i64 â
            âââââââââªââââââªââââââ¡
            â 0     â 1   â 4   â
            â 1     â 2   â 5   â
            â 2     â 3   â 6   â
            âââââââââŽââââââŽââââââ
            >>> agnostic_with_row_index(lf_dask)
               index  a  b
            0      0  1  4
            1      1  2  5
            2      2  3  6
        r\  r]  s     r6   ra   zLazyFrame.with_row_indexâ  s   ø ôX wÑ% dÓ+Ð+r7   c                ó    t         |   S )at  Get an ordered mapping of column names to their data type.

        Returns:
            A Narwhals Schema object that displays the mapping of column names.

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6.0, 7.0, 8.0],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            >>> lf = nw.from_native(lf_pl)
            >>> lf.schema  # doctest: +SKIP
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})

            >>> lf = nw.from_native(lf_dask)
            >>> lf.schema  # doctest: +SKIP
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
        r_  r`  s    r6   rW   zLazyFrame.schema  s   ø ô6 w~Ðr7   c                ó     t         |   «       S )ad  Get an ordered mapping of column names to their data type.

        Returns:
            A Narwhals Schema object that displays the mapping of column names.

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6.0, 7.0, 8.0],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            >>> lf = nw.from_native(lf_pl)
            >>> lf.collect_schema()
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})

            >>> lf = nw.from_native(lf_dask)
            >>> lf.collect_schema()
            Schema({'foo': Int64, 'bar': Float64, 'ham': String})
        rb  r`  s    r6   rZ   zLazyFrame.collect_schema-  s   ø ô4 wÑ%Ó'Ð'r7   c                ó    t         |   S )aÍ  Get column names.

        Returns:
            The column names stored in a list.

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrame
            >>>
            >>> data = {"foo": [1, 2, 3], "bar": [6.0, 7.0, 8.0], "ham": ["a", "b", "c"]}
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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 any supported library such as Polars or Dask to `agnostic_columns`:

            >>> agnostic_columns(lf_pl)  # doctest: +SKIP
            ['foo', 'bar', 'ham']
            >>> agnostic_columns(lf_dask)
            ['foo', 'bar', 'ham']
        rd  r`  s    r6   ri   zLazyFrame.columnsI  s   ø ô< wÐr7   c                ó"    t        |   |i |€S )ug
  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 polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": [1, 2, 3, 4],
            ...     "b": [0.5, 4, 10, 13],
            ...     "c": [True, True, False, True],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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")).collect().to_native()
            ...     )

            We can then pass any supported library such as Polars or Dask to `agnostic_with_columns`:

            >>> agnostic_with_columns(lf_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_dask)
               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
        rv  rw  s      r6   rk   zLazyFrame.with_columnsi  s   ø ôD wÑ# UÐ:škÑ:Ð:r7   c                ó"    t        |   |i |€S )uP  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.

        Returns:
            The LazyFrame containing only the selected columns.

        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 polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6, 7, 8],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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").collect().to_native()

            We can then pass any supported library such as Polars or Dask to `agnostic_select`:

            >>> agnostic_select(lf_pl)
            shape: (3, 1)
            âââââââ
            â foo â
            â --- â
            â i64 â
            âââââââ¡
            â 1   â
            â 2   â
            â 3   â
            âââââââ
            >>> agnostic_select(lf_dask)
               foo
            0    1
            1    2
            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"]).collect().to_native()

            >>> agnostic_select(lf_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 6   â
            â 2   â 7   â
            â 3   â 8   â
            âââââââŽââââââ
            >>> agnostic_select(lf_dask)
               foo  bar
            0    1    6
            1    2    7
            2    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).collect().to_native()

            >>> agnostic_select(lf_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 7   â
            â 2   â 8   â
            â 3   â 9   â
            âââââââŽââââââ
            >>> agnostic_select(lf_dask)
               foo  bar
            0    1    7
            1    2    8
            2    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).collect().to_native()

            >>> agnostic_select(lf_pl)
            shape: (3, 1)
            âââââââââââââ
            â threshold â
            â ---       â
            â i64       â
            âââââââââââââ¡
            â 2         â
            â 4         â
            â 6         â
            âââââââââââââ
            >>> agnostic_select(lf_dask)
               threshold
            0          2
            1          4
            2          6
        ry  rw  s      r6   rp   zLazyFrame.select­  s   ø ôH w~uÐ4šÑ4Ð4r7   c                ó"    t         |   |«      S )uí  Rename column names.

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

        Returns:
            The LazyFrame with the specified columns renamed.

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"foo": [1, 2, 3], "bar": [6, 7, 8], "ham": ["a", "b", "c"]}
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            We define a library agnostic function:

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

            We can then pass any supported library such as Polars or Dask to `agnostic_rename`:

            >>> agnostic_rename(lf_pl)
            shape: (3, 3)
            âââââââââ¬ââââââ¬ââââââ
            â apple â bar â ham â
            â ---   â --- â --- â
            â i64   â i64 â str â
            âââââââââªââââââªââââââ¡
            â 1     â 6   â a   â
            â 2     â 7   â b   â
            â 3     â 8   â c   â
            âââââââââŽââââââŽââââââ
            >>> agnostic_rename(lf_dask)
               apple  bar ham
            0      1    6   a
            1      2    7   b
            2      3    8   c
        r{  r|  s     r6   rr   zLazyFrame.rename3  s   ø ô\ w~gÓ&Ð&r7   c                ó"    t         |   |«      S )uë  Get the first `n` rows.

        Arguments:
            n: Number of rows to return.

        Returns:
            A subset of the LazyFrame of shape (n, n_columns).

        Examples:
            >>> import narwhals as nw
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": [1, 2, 3, 4, 5, 6],
            ...     "b": [7, 8, 9, 10, 11, 12],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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).collect().to_native()

            We can then pass any supported library such as Polars or Dask to `agnostic_head`:

            >>> agnostic_head(lf_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 7   â
            â 2   â 8   â
            â 3   â 9   â
            âââââââŽââââââ
            >>> agnostic_head(lf_dask)
               a  b
            0  1  7
            1  2  8
            2  3  9
        r~  r  s     r6   ru   zLazyFrame.headc  s   ø ô^ w|AÐr7   c                ó"    t         |   |«      S )uf  Get the last `n` rows.

        Arguments:
            n: Number of rows to return.

        Returns:
            A subset of the LazyFrame of shape (n, n_columns).

        Notes:
            `LazyFrame.tail` is not supported for the Dask backend with multiple
            partitions.

        Examples:
            >>> import narwhals as nw
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": [1, 2, 3, 4, 5, 6],
            ...     "b": [7, 8, 9, 10, 11, 12],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=1)

            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).collect().to_native()

            We can then pass any supported library such as Polars or Dask to `agnostic_tail`:

            >>> agnostic_tail(lf_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 4   â 10  â
            â 5   â 11  â
            â 6   â 12  â
            âââââââŽââââââ
            >>> agnostic_tail(lf_dask)
               a   b
            3  4  10
            4  5  11
            5  6  12
        r  r  s     r6   rz   zLazyFrame.tail  s   ø ôf w|AÐr7   Tr|   c               ó4    t        |   t        |«      d|iS )uÑ	  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.

        Returns:
            The LazyFrame with the specified columns removed.

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

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

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> 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"]}
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            We define a library agnostic function:

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

            We can then pass any supported library such as Polars or Dask to `agnostic_drop`:

            >>> agnostic_drop(lf_pl)
            shape: (3, 2)
            âââââââ¬ââââââ
            â foo â bar â
            â --- â --- â
            â i64 â f64 â
            âââââââªââââââ¡
            â 1   â 6.0 â
            â 2   â 7.0 â
            â 3   â 8.0 â
            âââââââŽââââââ
            >>> agnostic_drop(lf_dask)
               foo  bar
            0    1  6.0
            1    2  7.0
            2    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").collect().to_native()

            >>> agnostic_drop(lf_pl)
            shape: (3, 1)
            âââââââ
            â bar â
            â --- â
            â f64 â
            âââââââ¡
            â 6.0 â
            â 7.0 â
            â 8.0 â
            âââââââ
            >>> agnostic_drop(lf_dask)
               bar
            0  6.0
            1  7.0
            2  8.0
        r}   r  r  s      r6   r~   zLazyFrame.dropÉ  s   ø ôT w|W WÓ-Ð=°fÑ=Ð=r7   r  r  c               ó   |dvrd| d}t        |«      |rd}t        |«      |d}t        |t        t        «       ¬«       t	        |t
        «      r|g}| j                  | j                  j                  ||¬«      «      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', '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.
            maintain_order: Has no effect and is kept around only for backwards-compatibility.

        Returns:
            The LazyFrame with unique rows.

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> 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"],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            We define a library agnostic function:

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

            We can then pass any supported library such as Polars or Dask to `agnostic_unique`:

            >>> agnostic_unique(lf_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â str â str â
            âââââââªââââââªââââââ¡
            â 1   â a   â b   â
            âââââââŽââââââŽââââââ
            >>> agnostic_unique(lf_dask)
               foo bar ham
            0    1   a   b
        >   r  r  z}narwhals.LazyFrame makes no assumptions about row order, so only 'any' and 'none' are supported for `keep` in `unique`. Got: r€   z<`maintain_order=True` is not supported for LazyFrame.unique.rœ  rŸ  )rf   r  )	r§   r   rÂ  r   rN   rQ   r?   r.   r  r  s        r6   r  zLazyFrame.unique  s¢    ðt Ñ&ðOØOSÈfÐTUðWð ô S/Ð!ÙØPCÜS/Ð!ØÐ%ð7ð ô €{ŒÓ?PÕQÜfcÔ"ØXFØ×-Ñ-Ø×!Ñ!×(Ñ(°žTÐ(ÓBó
ð 	
r7   c                ó¬    t        |«      dk(  r7t        |d   t        «      r$t        d |d   D «       «      r|sd}t	        |«      t        |   |i |€S )u  Filter the rows in the LazyFrame based on a predicate expression.

        The original order of the remaining rows is preserved.

        Arguments:
            *predicates: Expression that evaluates to a boolean Series. Can
                also be a (single!) boolean list.
            **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 &.

        Returns:
            The filtered LazyFrame.

        Examples:
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "foo": [1, 2, 3],
            ...     "bar": [6, 7, 8],
            ...     "ham": ["a", "b", "c"],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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).collect().to_native()

            We can then pass any supported library such as Polars or Dask to `agnostic_filter`:

            >>> agnostic_filter(lf_pl)
            shape: (2, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 2   â 7   â b   â
            â 3   â 8   â c   â
            âââââââŽââââââŽââââââ
            >>> agnostic_filter(lf_dask)
               foo  bar ham
            1    2    7   b
            2    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"))
            ...         .collect()
            ...         .to_native()
            ...     )

            >>> agnostic_filter(lf_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            âââââââŽââââââŽââââââ
            >>> agnostic_filter(lf_dask)
               foo  bar ham
            0    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",
            ...         )
            ...         .collect()
            ...         .to_native()
            ...     )

            >>> agnostic_filter(lf_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            âââââââŽââââââŽââââââ
            >>> agnostic_filter(lf_dask)
               foo  bar ham
            0    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"))
            ...         .collect()
            ...         .to_native()
            ...     )

            >>> agnostic_filter(lf_pl)
            shape: (2, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 1   â 6   â a   â
            â 3   â 8   â c   â
            âââââââŽââââââŽââââââ
            >>> agnostic_filter(lf_dask)
               foo  bar ham
            0    1    6   a
            2    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").collect().to_native()

            >>> agnostic_filter(lf_pl)
            shape: (1, 3)
            âââââââ¬ââââââ¬ââââââ
            â foo â bar â ham â
            â --- â --- â --- â
            â i64 â i64 â str â
            âââââââªââââââªââââââ¡
            â 2   â 7   â b   â
            âââââââŽââââââŽââââââ
            >>> agnostic_filter(lf_dask)
               foo  bar ham
            1    2    7   b
        r   r   c              3  ó<   K   | ]  }t        |t        «        y ­wr2   r   r   s     r6   r   z#LazyFrame.filter.<locals>.<genexpr>û  r   r   zX`LazyFrame.filter` is not supported with Python boolean masks - use expressions instead.)r   rN   r   r   rS   rV  r   )r5   r   r   rU   r=   s       r6   r   zLazyFrame.filterd  sX   ø ôj 
OqÒ Ü: a=¬$Ô/ÜÑ?°žA²Ó?Ô?ÙàlCÜC.Ð äw~zÐ9š[Ñ9Ð9r7   Fr  c               ó    ddl m ddlm} ddlm t        |«      }t        fd|D «       «      rd}t        |«       || g|¢­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.

        Returns:
            Object which can be used to perform aggregations.

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

            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": ["a", "b", "a", "b", "c"],
            ...     "b": [1, 2, 1, 3, 3],
            ...     "c": [5, 4, 3, 2, 1],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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")
            ...         .collect()
            ...         .to_native()
            ...     )

            We can then pass any supported library such as Polars or Dask to `agnostic_group_by_agg`:

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

            >>> agnostic_group_by_agg(lf_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_dask)
               a  b  c
            0  a  1  5
            1  b  2  4
            2  b  3  2
            3  c  3  1
        r   rI   r    r"   c              3  ó:   K   | ]  }t        |f«        y ­wr2   r  r  s     r6   r   z%LazyFrame.group_by.<locals>.<genexpr>g  r  r  r  r  )	rL   rJ   r  r!   rM   r#   r   r  rŠ   )r5   r  r  r!   r  rU   rJ   r#   s         @@r6   r  zLazyFrame.group_by  sS   ù õ~ 	'Ý1Ý*äDM	ÜÔ@±iÓ@Ô@ðWð ô & cÓ*Ð*Ù4ÐK )ÒKžNÑKÐKr7   r   c               ó,    t        |   |g|¢­||dS )ul	  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.

        Returns:
            The sorted LazyFrame.

        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 polars as pl
            >>> import dask.dataframe as dd
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": [1, 2, None],
            ...     "b": [6.0, 5.0, 4.0],
            ...     "c": ["a", "c", "b"],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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]).collect().to_native()

            We can then pass any supported library such as Polars or Dask to `agnostic_sort`:

            >>> agnostic_sort(lf_pl)
            shape: (3, 3)
            ââââââââ¬ââââââ¬ââââââ
            â a    â b   â c   â
            â ---  â --- â --- â
            â i64  â f64 â str â
            ââââââââªââââââªââââââ¡
            â 1    â 6.0 â a   â
            â null â 4.0 â b   â
            â 2    â 5.0 â c   â
            ââââââââŽââââââŽââââââ
            >>> agnostic_sort(lf_dask)
                 a    b  c
            0  1.0  6.0  a
            2  NaN  4.0  b
            1  2.0  5.0  c
        r   r  r   s        r6   r   zLazyFrame.sorto  s!   ø ôB w|BÐW ÑW°ZÈJÒWÐWr7   r   r   c               ó.    t         |   ||||||¬«      S )uc  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 polars as pl
            >>> import dask.dataframe as dd
            >>> 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"],
            ... }

            >>> lf_pl = pl.LazyFrame(data)
            >>> other_pl = pl.LazyFrame(data_other)
            >>> lf_dask = dd.from_dict(data, npartitions=2)
            >>> other_dask = dd.from_dict(data_other, npartitions=2)

            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")
            ...         .sort("ham")
            ...         .collect()
            ...         .to_native()
            ...     )

            We can then pass any supported library such as Polars or Dask to `agnostic_join_on_ham`:

            >>> agnostic_join_on_ham(lf_pl, other_pl)
            shape: (2, 4)
            âââââââ¬ââââââ¬ââââââ¬ââââââââ
            â foo â bar â ham â apple â
            â --- â --- â --- â ---   â
            â i64 â f64 â str â str   â
            âââââââªââââââªââââââªââââââââ¡
            â 1   â 6.0 â a   â x     â
            â 2   â 7.0 â b   â y     â
            âââââââŽââââââŽââââââŽââââââââ
            >>> agnostic_join_on_ham(lf_dask, other_dask)
               foo  bar ham apple
            0    1  6.0   a     x
            0    2  7.0   b     y
        r¢  r£  r€  s          r6   rš   zLazyFrame.join²  s)   ø ôf w|Øs G°hÀ2Èfð ó 
ð 	
r7   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 LazyFrame.

        Examples:
            >>> from datetime import datetime
            >>> import narwhals as nw
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> 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_pl = pl.LazyFrame(data_gdp)
            >>> population_pl = pl.LazyFrame(data_population)
            >>> gdp_dask = dd.from_dict(data_gdp, npartitions=2)
            >>> population_dask = dd.from_dict(data_population, npartitions=2)

            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.sort("datetime")
            ...         .join_asof(other, on="datetime", strategy=strategy)
            ...         .collect()
            ...         .to_native()
            ...     )

            We can then pass any supported library such as Polars or Dask to `agnostic_join_asof_datetime`:

            >>> agnostic_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 â
            âââââââââââââââââââââââŽâââââââââââââŽâââââââ
            >>> agnostic_join_asof_datetime(population_dask, gdp_dask, strategy="backward")
                datetime  population   gdp
            0 2016-03-01       82.19  4164
            1 2018-08-01       82.66  4566
            0 2019-01-01       83.12  4696

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

            >>> from datetime import datetime
            >>> import narwhals as nw
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> 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, 49),
            ...         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_pl = pl.LazyFrame(data_quotes)
            >>> trades_pl = pl.LazyFrame(data_trades)
            >>> quotes_dask = dd.from_dict(data_quotes, npartitions=2)
            >>> trades_dask = dd.from_dict(data_trades, npartitions=2)

            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.sort("datetime", "ticker")
            ...         .join_asof(other, on="datetime", by="ticker")
            ...         .sort("datetime", "ticker")
            ...         .collect()
            ...         .to_native()
            ...     )

            We can then pass any supported library such as Polars or Dask to `agnostic_join_asof_datetime_by_ticker`:

            >>> agnostic_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 â AAPL   â 98.0   â 100      â null  â null   â
            â 2016-05-25 13:30:00.000048 â GOOG   â 720.77 â 100      â 720.5 â 720.93 â
            â 2016-05-25 13:30:00.000049 â GOOG   â 720.92 â 100      â 720.5 â 720.93 â
            ââââââââââââââââââââââââââââââŽâââââââââŽâââââââââŽâââââââââââŽââââââââŽâââââââââ
            >>> agnostic_join_asof_datetime_by_ticker(trades_dask, quotes_dask)
                                datetime ticker   price  quantity     bid     ask
            0 2016-05-25 13:30:00.000023   MSFT   51.95        75   51.95   51.96
            0 2016-05-25 13:30:00.000038   MSFT   51.95       155   51.97   51.98
            1 2016-05-25 13:30:00.000048   AAPL   98.00       100     NaN     NaN
            2 2016-05-25 13:30:00.000048   GOOG  720.77       100  720.50  720.93
            3 2016-05-25 13:30:00.000049   GOOG  720.92       100  720.50  720.93
        r³   rŠ  r§  s
            r6   rº   zLazyFrame.join_asof	  s5   ø ôH wÑ ØØØØØØØØð !ó 	
ð 		
r7   c                ó     t         |   «       S )u[  Create a copy of this DataFrame.

        Returns:
            An identical copy of the original LazyFrame.

        Examples:
            >>> import narwhals as nw
            >>> import polars as pl
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1, 2], "b": [3, 4]}
            >>> 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().collect().to_native()

            We can then pass any supported library such as Polars to `agnostic_clone`:

            >>> agnostic_clone(lf_pl)
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 1   â 3   â
            â 2   â 4   â
            âââââââŽââââââ
        r³  r`  s    r6   r­   zLazyFrame.cloneØ  s   ø ôB w}Ðr7   c                ó    | S )a_  Lazify the DataFrame (if possible).

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

        Returns:
            A LazyFrame.

        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 ...>
        r]   r4   s    r6   r  zLazyFrame.lazyû  s
    ðH r7   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.

        Returns:
            The LazyFrame containing only the selected rows.

        Examples:
            >>> import narwhals as nw
            >>> import polars as pl
            >>> import dask.dataframe as dd
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            Let's define a dataframe-agnostic function in which we 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).collect().to_native()

            We can then pass any supported library such as Polars or Dask to `agnostic_gather_every`:

            >>> agnostic_gather_every(lf_pl)
            shape: (2, 2)
            âââââââ¬ââââââ
            â a   â b   â
            â --- â --- â
            â i64 â i64 â
            âââââââªââââââ¡
            â 2   â 6   â
            â 4   â 8   â
            âââââââŽââââââ
            >>> agnostic_gather_every(lf_dask)
               a  b
            1  2  6
            3  4  8
        r¯   rµ  r¶  s      r6   r±   zLazyFrame.gather_every!  s   ø ôX wÑ# a°Ð#Ó7Ð7r7   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".

        Returns:
            The unpivoted LazyFrame.

        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
            >>> import dask.dataframe as dd
            >>> from narwhals.typing import IntoFrameT
            >>>
            >>> data = {
            ...     "a": ["x", "y", "z"],
            ...     "b": [1, 3, 5],
            ...     "c": [2, 4, 6],
            ... }
            >>> lf_pl = pl.LazyFrame(data)
            >>> lf_dask = dd.from_dict(data, npartitions=2)

            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"]))
            ...         .collect()
            ...         .to_native()
            ...     )

            We can then pass any supported library such as Polars or Dask to `agnostic_unpivot`:

            >>> agnostic_unpivot(lf_pl)
            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     â
            âââââââŽâââââââââââŽââââââââ
            >>> agnostic_unpivot(lf_dask)
               a variable  value
            0  x        b      1
            1  y        b      3
            0  z        b      5
            2  x        c      2
            3  y        c      4
            1  z        c      6
        rœ   rÌ  rÍ  s        r6   rÁ   zLazyFrame.unpivotO  s%   ø ôb wØšmÈ
ð ó 
ð 	
r7   c                ó$    t        |   |g|¢­ S )u×  Explode the dataframe to long format by exploding the given columns.

        Notes:
            It is possible to explode multiple columns only if these columns have
            matching element counts.

        Arguments:
            columns: Column names. The underlying columns being exploded must be of the `List` data type.
            *more_columns: Additional names of columns to explode, specified as positional arguments.

        Returns:
            New LazyFrame

        Examples:
            >>> import narwhals as nw
            >>> from narwhals.typing import IntoFrameT
            >>> import polars as pl
            >>> data = {
            ...     "a": ["x", "y", "z", "w"],
            ...     "lst1": [[1, 2], None, [None], []],
            ...     "lst2": [[3, None], None, [42], []],
            ... }

            We define a library agnostic function:

            >>> def agnostic_explode(df_native: IntoFrameT) -> IntoFrameT:
            ...     return (
            ...         nw.from_native(df_native)
            ...         .with_columns(nw.col("lst1", "lst2").cast(nw.List(nw.Int32())))
            ...         .explode("lst1", "lst2")
            ...         .collect()
            ...         .to_native()
            ...     )

            We can then pass any supported library such as Polars to `agnostic_explode`:

            >>> agnostic_explode(pl.LazyFrame(data))
            shape: (5, 3)
            âââââââ¬âââââââ¬âââââââ
            â a   â lst1 â lst2 â
            â --- â ---  â ---  â
            â str â i32  â i32  â
            âââââââªâââââââªâââââââ¡
            â x   â 1    â 3    â
            â x   â 2    â null â
            â y   â null â null â
            â z   â null â 42   â
            â w   â null â null â
            âââââââŽâââââââŽâââââââ
        rÏ  rÐ  s      r6   rÊ   zLazyFrame.explode€  s   ø ôf wwÐ6šÒ6Ð6r7   )rÌ   ztype[DataFrame[Any]]rÑ  rÖ  rÓ  )r+  zstr | slicerÌ   r   )rÌ   zDataFrame[Any])rÌ   r)   rÎ   r2   rÑ   rÏ   rÐ   rÍ   rÛ  rÓ   rÔ   rÕ   rÜ  rÖ   rÞ  )rf   rÒ   r  zLiteral['any', 'none']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  rû   r,  rí  r   r_   rg   ra   rW   rZ   ri   rk   rp   rr   ru   rz   r~   r  r   r  r   rš   rº   r­   r  r±   rÁ   rÊ   rá  râ  s   @r6   rï   rï   ×  sO  ø ñ
ð òó ðð&àð&ð 6ð	&ð
 
ó&óPð ò5ó ð5ó@ó>
ó@"CõJ.7ö`.1ö`,,ð\ ôó ðõ8(ð8 ôó ðð>B;Ø3ðB;ØDLðB;à	õB;ðHD5à-ðD5ð  ðD5ð 
õ	D5õL.'ö`/öb3ðj BF÷ J>ð\ *.ðM
ð (-Ø&*ñM
à&ðM
ð %ð	M
ð
 $ðM
ð 
óM
ð^]:ØEð]:ØVYð]:à	õ]:ð@ BGñjLØ(ðjLØ:>ðjLà	ójLð` -2Ø ñAXàðAXð ðAXð *ð	AXð
 ðAXð 
õAXðL &*ØAHð	U
ð +/Ø+/ØñU
àðU
ð #ðU
ð ?ð	U
ð (ðU
ð )ðU
ð ðU
ð 
õU
ðv #Ø#ØØ*.Ø+/Ø%)Ø>HñM
àðM
ð ð	M
ð
 ðM
ð ðM
ð (ðM
ð )ðM
ð #ðM
ð <ðM
ð 
õM
õ^!óF$öL,8ð` &*ðS
ð )-Ø$(Ø!%ñS
ØðS
à"ðS
ð &ð	S
ð
 "ðS
ð ðS
ð 
õS
÷j37ñ 37r7   rï   );Ú
__future__r   Útypingr   r   r   r   r   r	   r
   r   r   r   r   Úwarningsr   Únarwhals.dependenciesr   r   Únarwhals.schemar   Únarwhals.translater   Únarwhals.utilsr   r   r   r   r   Úior   Úpathlibr   Útypesr   ÚnumpyÚnpÚpandasÚpdr  r  Útyping_extensionsr   r  r   r!   rM   r#   Únarwhals.typingr$   r%   r&   r'   r(   r)   r+   r-   rë   rï   r]   r7   r6   Ú<module>r     sÊ   ðÝ "å  Ý Ý Ý Ý Ý Ý Ý Ý Ý Ý Ý å ,Ý 0Ý "Ý (Ý *Ý "Ý (Ý 2Ý (áÝÝÝ ãÛÛÝ&å)Ý-Ý&Ý-Ý(Ý)Ý(Ý-á	 Ô	-Ù\šÔ9
ô]
ô ]
ô@	C27	*Ñ%ô C27ôLd@7	&Ñ!õ @7r7   