U
     )3gM                     @  s  d dl mZ d dlZd dlZd dlmZ d dlmZ d dlmZ d dlmZ d dlm	Z	 d dlm
Z
 d d	lmZ d d
lmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ e
deeef dZerd dlmZ d dlmZ d dlmZ d dlmZ d dlmZ G dd de	Z ddddddddZ!dHd d!d"d#d$d%d&d'Z"dId d!d"d#d(d$d)d*d+Z#dJdd,d-d.d/d0d1d2d3Z$dKdd,d-d.d/d(d0d4d5d6Z%dd#d0d7d8d9Z&d:d;d<d=Z'd:d;d>d?Z(d@d;dAdBZ)dCdDdEdFdGZ*dS )L    )annotationsN)TYPE_CHECKING)Any)Iterable)Literal)Protocol)TypeVar)Union)	DataFrame)	LazyFrame)from_native)Implementation)parse_version)validate_lazinessFrameT)bound)
ModuleType)DType)SchemaSeries)DTypesc                   @  s   e Zd ZddddddZdS )ArrowStreamExportableNzobject | Noneobject)requested_schemareturnc                 C  s   d S )N )selfr   r   r   6/tmp/pip-unpacked-wheel-hfsjijke/narwhals/functions.py__arrow_c_stream__#   s    z(ArrowStreamExportable.__arrow_c_stream__)N)__name__
__module____qualname__r   r   r   r   r   r   "   s    r   verticalhowzIterable[FrameT]z#Literal[('horizontal', 'vertical')])itemsr%   r   c                C  sb   |dkrd}t || s$d}t|t| } t|  | d }| }||jdd | D |dS )u_  
    Concatenate multiple DataFrames, LazyFrames into a single entity.

    Arguments:
        items: DataFrames, LazyFrames to concatenate.

        how: {'vertical', 'horizontal'}
            * vertical: Stacks Series from DataFrames vertically and fills with `null`
              if the lengths don't match.
            * horizontal: Stacks Series from DataFrames horizontally and fills with `null`
              if the lengths don't match.

    Returns:
        A new DataFrame, Lazyframe resulting from the concatenation.

    Raises:
        NotImplementedError: The items to concatenate should either all be eager, or all lazy

    Examples:

        Let's take an example of vertical concatenation:

        >>> import pandas as pd
        >>> import polars as pl
        >>> import narwhals as nw
        >>> data_1 = {"a": [1, 2, 3], "b": [4, 5, 6]}
        >>> data_2 = {"a": [5, 2], "b": [1, 4]}

        >>> df_pd_1 = pd.DataFrame(data_1)
        >>> df_pd_2 = pd.DataFrame(data_2)
        >>> df_pl_1 = pl.DataFrame(data_1)
        >>> df_pl_2 = pl.DataFrame(data_2)

        Let's define a dataframe-agnostic function:

        >>> @nw.narwhalify
        ... def func(df1, df2):
        ...     return nw.concat([df1, df2], how="vertical")

        >>> func(df_pd_1, df_pd_2)
           a  b
        0  1  4
        1  2  5
        2  3  6
        0  5  1
        1  2  4
        >>> func(df_pl_1, df_pl_2)
        shape: (5, 2)
        ┌─────┬─────┐
        │ a   ┆ b   │
        │ --- ┆ --- │
        │ i64 ┆ i64 │
        ╞═════╪═════╡
        │ 1   ┆ 4   │
        │ 2   ┆ 5   │
        │ 3   ┆ 6   │
        │ 5   ┆ 1   │
        │ 2   ┆ 4   │
        └─────┴─────┘

        Let's look at case a for horizontal concatenation:

        >>> import pandas as pd
        >>> import polars as pl
        >>> import narwhals as nw
        >>> data_1 = {"a": [1, 2, 3], "b": [4, 5, 6]}
        >>> data_2 = {"c": [5, 2], "d": [1, 4]}

        >>> df_pd_1 = pd.DataFrame(data_1)
        >>> df_pd_2 = pd.DataFrame(data_2)
        >>> df_pl_1 = pl.DataFrame(data_1)
        >>> df_pl_2 = pl.DataFrame(data_2)

        Defining a dataframe-agnostic function:

        >>> @nw.narwhalify
        ... def func(df1, df2):
        ...     return nw.concat([df1, df2], how="horizontal")

        >>> func(df_pd_1, df_pd_2)
           a  b    c    d
        0  1  4  5.0  1.0
        1  2  5  2.0  4.0
        2  3  6  NaN  NaN

        >>> func(df_pl_1, df_pl_2)
        shape: (3, 4)
        ┌─────┬─────┬──────┬──────┐
        │ a   ┆ b   ┆ c    ┆ d    │
        │ --- ┆ --- ┆ ---  ┆ ---  │
        │ i64 ┆ i64 ┆ i64  ┆ i64  │
        ╞═════╪═════╪══════╪══════╡
        │ 1   ┆ 4   ┆ 5    ┆ 1    │
        │ 2   ┆ 5   ┆ 2    ┆ 4    │
        │ 3   ┆ 6   ┆ null ┆ null │
        └─────┴─────┴──────┴──────┘

    )Z
horizontalr#   z9Only horizontal and vertical concatenations are supportedzNo items to concatenater   c                 S  s   g | ]
}|j qS r   )Z_compliant_frame).0Zdfr   r   r   
<listcomp>   s     zconcat.<locals>.<listcomp>r$   )NotImplementedError
ValueErrorlistr   Z__narwhals_namespace__Z_from_compliant_dataframeconcat)r&   r%   msgZ
first_itemZplxr   r   r   r,   (   s    hr,   strr   zDType | type[DType] | Noner   r   )namevaluesdtypenative_namespacer   c                C  s   ddl m} t| ||||dS )a  
    Instantiate Narwhals Series from raw data.

    Arguments:
        name: Name of resulting Series.
        values: Values of make Series from.
        dtype: (Narwhals) dtype. If not provided, the native library
            may auto-infer it from `values`.
        native_namespace: The native library to use for DataFrame creation.

    Returns:
        A new Series

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

        Let's define a dataframe-agnostic function:

        >>> @nw.narwhalify
        ... def func(df):
        ...     values = [4, 1, 2]
        ...     native_namespace = nw.get_native_namespace(df)
        ...     return nw.new_series("c", values, nw.Int32, native_namespace=native_namespace)

        Let's see what happens when passing pandas / Polars input:

        >>> func(pd.DataFrame(data))
        0    4
        1    1
        2    2
        Name: c, dtype: int32
        >>> func(pl.DataFrame(data))  # doctest: +NORMALIZE_WHITESPACE
        shape: (3,)
        Series: 'c' [i32]
        [
           4
           1
           2
        ]
    r   dtypesr2   r4   )narwhalsr4   _new_series_impl)r/   r0   r1   r2   r4   r   r   r   
new_series   s    2r8   r   )r/   r0   r1   r2   r4   r   c             
   C  s:  t |}|t jkrB|r0ddlm} |||d}|j| ||d}n|t jt jt jhkr|rddl	m} t
|j}	||d ||	|}|j|| |d}n|t jkr|rddlm}
 |
||d}|j|g|d}n^|t jkrd}t|nFz|| ||}W n2 tk
r& } zd}t||W 5 d }~X Y nX t|d	d
| S )Nr   narwhals_to_native_dtyper3   )r/   r0   r1   )r/   r1   )typezGDask support in Narwhals is lazy-only, so `new_series` is not supportedz@Unknown namespace is expected to implement `Series` constructor.T)Zseries_only)r   from_native_namespacePOLARSnarwhals._polars.utilsr:   r   PANDASMODINCUDFnarwhals._pandas_like.utilsr   __version__PYARROWnarwhals._arrow.utilsZchunked_arrayZDASKr)   r8   AttributeErrorr   alias)r/   r0   r1   r2   r4   implementationpolars_narwhals_to_native_dtypeZnative_series$pandas_like_narwhals_to_native_dtypebackend_versionarrow_narwhals_to_native_dtyper-   er   r   r   r7      sH    


    


r7   )r2   zdict[str, Any]z dict[str, DType] | Schema | NonezModuleType | NonezDataFrame[Any])dataschemar2   r   c                C  s   ddl m} t| |||dS )u  
    Instantiate DataFrame from dictionary.

    Notes:
        For pandas-like dataframes, conversion to schema is applied after dataframe
        creation.

    Arguments:
        data: Dictionary to create DataFrame from.
        schema: The DataFrame schema as Schema or dict of {name: type}.
        native_namespace: The native library to use for DataFrame creation. Only
            necessary if inputs are not Narwhals Series.

    Returns:
        A new DataFrame

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

        Let's create a new dataframe of the same class as the dataframe we started with, from a dict of new data:

        >>> @nw.narwhalify
        ... def func(df):
        ...     new_data = {"c": [5, 2], "d": [1, 4]}
        ...     native_namespace = nw.get_native_namespace(df)
        ...     return nw.from_dict(new_data, native_namespace=native_namespace)

        Let's see what happens when passing Pandas, Polars or PyArrow input:

        >>> func(pd.DataFrame(data))
           c  d
        0  5  1
        1  2  4
        >>> func(pl.DataFrame(data))
        shape: (2, 2)
        ┌─────┬─────┐
        │ c   ┆ d   │
        │ --- ┆ --- │
        │ i64 ┆ i64 │
        ╞═════╪═════╡
        │ 5   ┆ 1   │
        │ 2   ┆ 4   │
        └─────┴─────┘
        >>> func(pa.table(data))
        pyarrow.Table
        c: int64
        d: int64
        ----
        c: [[5,2]]
        d: [[1,4]]
    r   r3   r5   )r6   r4   _from_dict_impl)rN   rO   r2   r4   r   r   r   	from_dict  s    =rQ   )rN   rO   r2   r4   r   c          	   
     s  ddl m} ddlm | s(d}t||d krv|  D ]}t||r8| } q`q8d}t|fdd| 	 D } t
|t
jkrrddlm fd	d	 D |j| d
}nt
jt
jt
jhkr,|j| }rddlm t|jfdd|j	 D |}nt
jkrxrhddlm  | fdd	 D |j| d
}nBz|| }W n2 tk
r } zd}t||W 5 d }~X Y nX t|ddS )Nr   r   	to_nativez0from_dict cannot be called with empty dictionaryzpCalling `from_dict` without `native_namespace` is only supported if all input values are already Narwhals Seriesc                   s   i | ]\}}| |d dqS )T)Zpass_throughr   )r'   keyvaluerR   r   r   
<dictcomp>r  s      z#_from_dict_impl.<locals>.<dictcomp>r9   c                   s   i | ]\}}|| d qS )r3   r   r'   r/   r1   )r4   rI   r   r   rV   {  s    )rO   c              	     s&   i | ]\}}|| | qS r   r   )r'   r/   Znative_type)rK   r4   rH   rJ   rO   r   r   rV     s        c                   s   g | ]\}}| |fqS r   r   rW   )rL   r4   r   r   r(     s   z#_from_dict_impl.<locals>.<listcomp>z@Unknown namespace is expected to implement `from_dict` function.TZ
eager_only)narwhals.seriesr   narwhals.translaterS   r*   r0   
isinstanceZ__native_namespace__	TypeErrorr&   r   r<   r=   r>   r:   rQ   r?   r@   rA   r
   rB   r   rC   r4   ZastyperD   rE   rO   tablerF   r   )	rN   rO   r2   r4   r   r-   valnative_framerM   r   )rL   rK   r4   rH   rJ   rI   rO   rS   r   rP   ]  sb    



rP   )r_   r2   r   c          	   
   C  s  t | ds"dt|  d}t|t|}|tjkrRt|jdkrR|| } nZ|tj	tj
tjtjhkrRzddl}W n6 tk
r } zd| }t||W 5 d}~X Y nX t|jdk rd| }t|d|| }|tj	kr| } n`|tj
krdd	lm} ||} n>|tjkr,|j|} n$|tjkrD||} nd
}t|nZ|tjkrj|| } nBz|| } W n2 tk
r } zd}t||W 5 d}~X Y nX t| ddS )aB  
    Construct a DataFrame from an object which supports the PyCapsule Interface.

    Arguments:
        native_frame: Object which implements `__arrow_c_stream__`.
        native_namespace: The native library to use for DataFrame creation.

    Returns:
        A new DataFrame

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

        Let's define a dataframe-agnostic function which creates a PyArrow
        Table.

        >>> @nw.narwhalify
        ... def func(df):
        ...     return nw.from_arrow(df, native_namespace=pa)

        Let's see what happens when passing pandas / Polars input:

        >>> func(pd.DataFrame(data))  # doctest: +SKIP
        pyarrow.Table
        a: int64
        b: int64
        ----
        a: [[1,2,3]]
        b: [[4,5,6]]
        >>> func(pl.DataFrame(data))  # doctest: +SKIP
        pyarrow.Table
        a: int64
        b: int64
        ----
        a: [[1,2,3]]
        b: [[4,5,6]]
    r   zGiven object of type z% does not support PyCapsule interface)      r   Nz@PyArrow>=14.0.0 is required for `from_arrow` for object of type )   r   )
from_arrowzCcongratulations, you entered unrecheable code - please report a bugzuUnknown namespace is expected to implement `DataFrame` class which accepts object which supports PyCapsule Interface.TrX   )hasattrr;   r\   r   r<   r=   r   rC   r
   r?   r@   rA   pyarrowModuleNotFoundErrorr]   Z	to_pandasZmodin.pandas.utilsrc   AssertionErrorrD   rF   r   )	r_   r2   r-   rH   paexcZtblrc   rM   r   r   r   rc     sX    ,









rc   zdict[str, str])r   c                  C  s2   t jdd} d| fdt jfdt ff}t|S )zdSystem information

    Returns system and Python version information

    Copied from sklearn

    
 python
executablemachine)sysversionreplacerm   platformdict)rl   Zblobr   r   r   _get_sys_info  s    
rt   c               	   C  sl   d} ddl m} d|i}ddlm} ddlm} | D ]2}z||||< W q4 |k
rd   d||< Y q4X q4|S )	aN  Overview of the installed version of main dependencies

    This function does not import the modules to collect the version numbers
    but instead relies on standard Python package metadata.

    Returns version information on relevant Python libraries

    This function and show_versions were copied from sklearn and adapted

    )ZpandasZpolarsZcudfZmodinre   Znumpyr`   )rC   r6   r   )PackageNotFoundError)rp    )rv   rC   importlib.metadataru   rp   )depsrC   	deps_inforu   rp   modnamer   r   r   _get_deps_info"  s    	 r{   Nonec                  C  sl   t  } t }td |  D ]\}}t|dd|  qtd | D ]\}}t|dd|  qJdS )z
    Print useful debugging information

    Examples:

        >>> from narwhals import show_versions
        >>> show_versions()  # doctest: +SKIP
    z
System:z>10z: z
Python dependencies:z>13N)rt   r{   printr&   )Zsys_infory   kstatr   r   r   show_versionsG  s    
r   z(DataFrame[Any] | LazyFrame[Any] | Seriesz Literal[('full', 'interchange')])objr   c                 C  s   | j S )z
    Level of support Narwhals has for current object.

    This can be one of:

    - 'full': full Narwhals API support
    - 'metadata': only metadata operations are supported (`df.schema`)
    )_level)r   r   r   r   	get_level]  s    r   )N)N)N)N)+
__future__r   rr   ro   typingr   r   r   r   r   r   r	   Znarwhals.dataframer
   r   rZ   r   Znarwhals.utilsr   r   r   r   typesr   Znarwhals.dtypesr   Znarwhals.schemar   rY   r   Znarwhals.typingr   r   r,   r8   r7   rQ   rP   rc   rt   r{   r   r   r   r   r   r   <module>   sT   	z @ < I Qa%