U
    Cvfw                     @  sr  d dl mZ d dlZd dlZd dlmZmZmZmZm	Z	m
Z
mZmZmZmZmZmZ d dlZd dlZd dlmZmZmZmZ d dlmZmZ d dlmZ d dlm Z m!Z! d dl"m#Z#m$Z$ d d	l%m&Z& d d
l'm(Z( d dl)m*Z*m+Z+m,Z, d dl-m.Z. d dl/m0Z0 d dl1m2Z2m3Z3m4Z4 d dl5m6Z6m7Z7m8Z8m9Z9m:Z: d dl;m<Z<m=Z= ervd dl>m?Z? d dl@mAZA d dlBmCZC d dl5mDZD eZEdd ZFdBdddddZGdd ZHdd  ZId!d" ZJd#d$ ZKG d%d& d&ZLed'ed(d)eLf d*ZMd'd+d,d-d.d/ZNd'dd0d1d2ZOd3d4 ZPG d5d6 d6ee4 ZQd7d8 ZRG d9d: d:eQd( e ZSG d;d< d<eSee#ZTG d=d> d>eQd? e!ZUG d@dA dAeUee$ZVdS )C    )annotationsN)TYPE_CHECKINGAnyCallableGenericHashableIteratorLiteralMappingSequenceTypeVarUnioncast)dtypesduck_array_opsnputilsops)DataArrayGroupByAggregationsDatasetGroupByAggregations)align)DataArrayGroupbyArithmeticDatasetGroupbyArithmetic)ImplementsArrayReduceImplementsDatasetReduce)concat)format_array_flat)create_default_index_implicitfilter_indexes_from_coordssafe_cast_to_index)_get_keep_attrs)integer_types)DimsQuantileMethodsT_Xarray)either_dict_or_kwargshashable	is_scalarmaybe_wrap_arraypeek_at)IndexVariableVariable)	ArrayLike	DataArrayDataset)Frozenc                   sF   | dk	rBt | r| g} t fdd| D rBtd| d dd S )N.c                 3  s   | ]}| kV  qd S N .0dim
dimensionsr2   7/tmp/pip-unpacked-wheel-h316xyqg/xarray/core/groupby.py	<genexpr>A   s     z$check_reduce_dims.<locals>.<genexpr>cannot reduce over dimensions zH. expected either '...' to reduce over all dimensions or one or more of .)r&   any
ValueError)Zreduce_dimsr7   r2   r6   r8   check_reduce_dims<   s    r>   Tboolz-tuple[np.ndarray | pd.Index, list[list[int]]])sortreturnc                 C  sl   t j| |d\}}t|t jr&| j|_dd tt|D }t|D ]\}}|dkrD|| | qD||fS )a   Group an array by its unique values.

    Parameters
    ----------
    ar : array-like
        Input array. This will be flattened if it is not already 1-D.
    sort : bool, default: True
        Whether or not to sort unique values.

    Returns
    -------
    values : np.ndarray
        Sorted, unique values as returned by `np.unique`.
    indices : list of lists of int
        Each element provides the integer indices in `ar` with values given by
        the corresponding value in `unique_values`.
    r@   c                 S  s   g | ]}g qS r2   r2   )r4   _r2   r2   r8   
<listcomp>_   s     z'unique_value_groups.<locals>.<listcomp>r   )	pdZ	factorize
isinstance
MultiIndexnamesrangelen	enumerateappend)arr@   Zinversevaluesgroupsngr2   r2   r8   unique_value_groupsH   s    rR   c                   s   ddl m} ddlm} t |rV|dd  j D  fdd j D  j}nBt |r|t	
 j fdd j D g  j jd}nt|S )	Nr   r,   r.   c                 S  s   i | ]\}}|t |jqS r2   )r   get_fill_valuedtyper4   kvr2   r2   r8   
<dictcomp>m   s    z_dummy_copy.<locals>.<dictcomp>c                   s(   i | ] \}}| j kr|t|jqS r2   dimsr   rS   rT   rU   
xarray_objr2   r8   rX   q   s   
 c                   s(   i | ] \}}| j kr|t|jqS r2   rY   rU   r[   r2   r8   rX   {   s   
 )rZ   nameattrs)xarray.core.dataarrayr-   xarray.core.datasetr/   rF   	data_varsitemscoordsr^   r   rS   rT   r]   AssertionError)r\   r-   r/   resr2   r[   r8   _dummy_copyg   s0    




rf   c                 C  s   | dkp| d kS N   r2   )objr2   r2   r8   _is_one_or_none   s    rj   c                 C  s   g }t d}| D ]j}t|t s,td||rl|j|jkrlt|jrlt|jrlt |j|j|j}||d< q|| |}q|S )z0Consolidate adjacent slices in a list of slices.Nzlist element is not a slice: )slicerF   r=   stopstartrj   steprL   )ZslicesresultZ
last_sliceZslice_r2   r2   r8   _consolidate_slices   s$    



rq   c                 C  sL   | sdS t | d tr<t| } | tdkr.dS dd | D } tt| S )a  Like inverse_permutation, but also handles slices.

    Parameters
    ----------
    positions : list of ndarray or slice
        If slice objects, all are assumed to be slices.

    Returns
    -------
    np.ndarray of indices or None, if no permutation is necessary.
    Nr   c                 S  s    g | ]}t |j|j|jqS r2   )nparangern   rm   ro   )r4   slr2   r2   r8   rD      s     z0_inverse_permutation_indices.<locals>.<listcomp>)rF   rl   rq   r   Zinverse_permutationrr   Zconcatenate)	positionsr2   r2   r8   _inverse_permutation_indices   s    rv   c                   @  s   e Zd ZdZdZddddddZed	d
ddZedd
ddZedd
ddZ	edd
ddZ
edd
ddZdd ZdS )_DummyGroupzhClass for keeping track of grouped dimensions without coordinates.

    Should not be user visible.
    )r]   rc   sizer#   r   None)ri   r]   rA   c                 C  s   || _ || _|j| | _d S r1   )r]   rc   sizesrx   )selfri   r]   rc   r2   r2   r8   __init__   s    z_DummyGroup.__init__ztuple[Hashable]rA   c                 C  s   | j fS r1   r]   r{   r2   r2   r8   rZ      s    z_DummyGroup.dimsz
Literal[1]c                 C  s   dS rg   r2   r   r2   r2   r8   ndim   s    z_DummyGroup.ndimrI   c                 C  s
   t | jS r1   rI   rx   r   r2   r2   r8   rN      s    z_DummyGroup.valuesc                 C  s
   t | jS r1   r   r   r2   r2   r8   data   s    z_DummyGroup.dataz
tuple[int]c                 C  s   | j fS r1   )rx   r   r2   r2   r8   shape   s    z_DummyGroup.shapec                 C  s   t |tr|d }| j| S Nr   )rF   tuplerN   r{   keyr2   r2   r8   __getitem__   s    
z_DummyGroup.__getitem__N)__name__
__module____qualname____doc__	__slots__r|   propertyrZ   r   rN   r   r   r   r2   r2   r2   r8   rw      s   rw   T_Groupr-   r)   )boundr#   z9tuple[T_Group, T_Xarray, Hashable | None, list[Hashable]])groupri   rA   c                   s   ddl m} t ttfs$ jdkr0 |d g fS t |r j}ddtt	| } fdd jD } 
||i }|
||i}tt||||fS tdt d	d S )
Nr   r,   rh   Zstacked_rC   c                   s   g | ]}| j kr|qS r2   )rc   r3   r   r2   r8   rD      s     
 z_ensure_1d.<locals>.<listcomp>z;group must be DataArray, IndexVariable or _DummyGroup, got r;   )r_   r-   rF   r)   rw   r   rZ   joinmapstrstackcopyr   r   	TypeErrortype)r   ri   r-   Z	orig_dimsstacked_diminserted_dimsZnewgroupZnewobjr2   r   r8   
_ensure_1d   s    
r   )r   rA   c                 C  s"   t | trdS t| }|jo |jS )NT)rF   rw   r   	is_uniqueis_monotonic_increasing)r   indexr2   r2   r8   _unique_and_monotonic   s    
r   c                 C  sL   t | jtjtjfo.t |jtjo.t|jdk}|rB|j| j |_d| _dS )a  
    (copied from pandas)
    if loffset is set, offset the result index

    This is NOT an idempotent routine, it will be applied
    exactly once to the result.

    Parameters
    ----------
    result : Series or DataFrame
        the result of resample
    r   N)	rF   ZloffsetrE   Z
DateOffsetdatetime	timedeltar   ZDatetimeIndexrJ   )grouperrp   Zneeds_offsetr2   r2   r8   _apply_loffset  s    r   c                   @  s  e Zd ZU dZdZded< dYddd	d
dd	dddddZeddddZdZddddddddZ	d[dddddddd dd	d	ddd!d"d#Z
ed$dd%d&Zd'dd(d)d*Zd+dd,d-Zd.dd/d0Zd1dd2d3Zd4d5 Zd6dd7d8Zd9d: Zd\d;d<Zd=d> Zd?d@ Zd]ddddAdBdCZdddDdEdFZd^dHddIdddJddKdLdMZejfdddNdOZdPdQ Zd_dddRdSdTZd`dddRdUdVZdadWdXZdS )bGroupBya
  A object that implements the split-apply-combine pattern.

    Modeled after `pandas.GroupBy`. The `GroupBy` object can be iterated over
    (unique_value, grouped_array) pairs, but the main way to interact with a
    groupby object are with the `apply` or `reduce` methods. You can also
    directly call numpy methods like `mean` or `std`.

    You should create a GroupBy object by using the `DataArray.groupby` or
    `Dataset.groupby` methods.

    See Also
    --------
    Dataset.groupby
    DataArray.groupby
    )_full_index_inserted_dims_group
_group_dim_group_indices_groups_obj_restore_coord_dims_stacked_dim_unique_coord_dims_sizes_squeeze_original_obj_original_group_binsr#   r   FNTz$Hashable | DataArray | IndexVariabler?   zpd.Grouper | NonezArrayLike | NonezMapping[Any, Any] | Nonery   )ri   r   squeezer   binsrestore_coord_dims
cut_kwargsrA   c                 C  s6  |dkri }ddl m} |dk	r0|dk	r0tdt||tfst|sVtd|d|| }t|dkrzt|j d|j|j	kr|j|j
krt||j|j	}t|dddkrd	|_|| _|| _|| _t||\}}}	}
|j
\}|j| }|j|krtd
d}|dk	rpt| r(tdtj|j|f|ddi\}}t|jd }||t|dd|d}|j}|dk	rt|}|jstd| ||\}}|jtj }dd t!|dd |dd D t"|d dg }t|j|j#}n|j
|jfkr<t$|r<|s*dd t%|jD }nt&|j}|}nrt||rr| ' rr|j(|) dd}|*|}t|}|dkot|tj+ }t,||d\}}t|j|}t|dkr|dk	rtd|ntd|| _-|| _.|| _/|| _0|| _1|	| _2|
| _3|| _4|| _5|| _|| _6d| _7d| _8d| _9dS )a  Create a GroupBy object

        Parameters
        ----------
        obj : Dataset or DataArray
            Object to group.
        group : Hashable, DataArray or Index
            Array with the group values or name of the variable.
        squeeze : bool, default: False
            If "group" is a coordinate of object, `squeeze` controls whether
            the subarrays have a dimension of length 1 along that coordinate or
            if the dimension is squeezed out.
        grouper : pandas.Grouper, optional
            Used for grouping values along the `group` array.
        bins : array-like, optional
            If `bins` is specified, the groups will be discretized into the
            specified bins by `pandas.cut`.
        restore_coord_dims : bool, default: True
            If True, also restore the dimension order of multi-dimensional
            coordinates.
        cut_kwargs : dict-like, optional
            Extra keyword arguments to pass to `pandas.cut`

        Nr   r,   z'can't specify both `grouper` and `bins`z]`group` must be an xarray.DataArray or the name of an xarray variable or dimension. Received z	 instead.z must not be emptyr]   r   zZthe group variable's length does not match the length of this variable along its dimensionzAll bin edges are NaN.ZretbinsTr   rc   r~   z&index must be monotonic for resamplingc                 S  s   g | ]\}}t ||qS r2   rl   )r4   ijr2   r2   r8   rD     s     z$GroupBy.__init__.<locals>.<listcomp>rk   rh   c                 S  s   g | ]}t ||d  qS )rh   r   )r4   r   r2   r2   r8   rD     s     ZdroprB   z.None of the data falls within bins with edges zEFailed to group data. Are you grouping by a variable that is all NaN?):r_   r-   r   rF   r)   r%   rJ   r=   r]   rc   rZ   rw   getattrr   r   r   r   rz   rx   r   isnullallrE   ZcutrN   r   
categoriesr   r   _get_index_and_itemsZastyperr   Zint64ziprl   r   r   rI   rs   r<   whereZnotnulldropnarG   rR   r   r   r   r   r   r   r   r   r   r   r   r   r   )r{   ri   r   r   r   r   r   r   r-   r   r   Z	group_dimZexpected_size
full_indexZbinnedZnew_dim_namer   first_itemsZsbinsZgroup_indicesZunique_coordZgroup_as_indexr@   Zunique_valuesr2   r2   r8   r|   E  s    "



"
 

zGroupBy.__init__Frozen[Hashable, int]r}   c                 C  s,   | j dkr&| j| j| jd ij| _ | j S )zOrdered mapping from dimension names to lengths.

        Immutable.

        See Also
        --------
        DataArray.sizes
        Dataset.sizes
        Nr   )r   r   iselr   r   rz   r   r2   r2   r8   rz     s
    
zGroupBy.sizesr2   r   tuple[Any, ...]bool | Noner   funcargsshortcutkwargsrA   c                 K  s
   t  d S r1   NotImplementedErrorr{   r   r   r   r   r2   r2   r8   r     s    zGroupBy.mapaxis
keep_attrskeepdimsr   Callable[..., Any]r!   int | Sequence[int] | Noner   r5   r   r   r   r   r   rA   c                K  s
   t  d S r1   r   )r{   r   r5   r   r   r   r   r   r2   r2   r8   reduce  s    zGroupBy.reducez'dict[GroupKey, slice | int | list[int]]c                 C  s&   | j dkr tt| jj| j| _ | j S )zo
        Mapping from group labels to indices. The indices can be used to index the underlying object.
        N)r   dictr   r   rN   r   r   r2   r2   r8   rO     s    
zGroupBy.groupsGroupKey)r   rA   c                 C  s   | j | j| j| iS )zU
        Get DataArray or Dataset corresponding to a particular group label.
        )r   r   r   rO   r   r2   r2   r8   r     s    zGroupBy.__getitem__intc                 C  s   | j jS r1   )r   rx   r   r2   r2   r8   __len__  s    zGroupBy.__len__z#Iterator[tuple[GroupKey, T_Xarray]]c                 C  s   t | jj|  S r1   )r   r   rN   _iter_groupedr   r2   r2   r8   __iter__  s    zGroupBy.__iter__r   c              
   C  s.   d | jj| jj| jjdt| jd S )Nz1{}, grouped over {!r}
{!r} groups with labels {}.z,    )	format	__class__r   r   r]   rx   r   r   splitr   r2   r2   r8   __repr__  s    zGroupBy.__repr__c                 C  sp   ddl m} tt|j|}t||r6||}n|	|
 }t|| |j}|  rh| }||fS )Nr   )CFTimeGrouper)Zxarray.core.resample_cftimer   rE   ZSeriesrr   rs   rx   rF   r   groupbyfirstr   r   r   r<   r   )r{   r   r   r   sr   r   r2   r2   r8   r   !  s    

zGroupBy._get_index_and_itemszIterator[T_Xarray]c                 c  s$   | j D ]}| j| j|iV  qdS )z'Iterate over each element in this groupN)r   r   r   r   )r{   indicesr2   r2   r8   r   /  s    
zGroupBy._iter_groupedc                 C  sP   | j |jkr| j}| j}n
| j}d }|j\}t|tr:d }t|d|}|||fS )Nvariable)r   rZ   r   r   r   rF   rw   r   )r{   applied_examplecoordru   r5   r2   r2   r8   _infer_concat_args4  s    
zGroupBy._infer_concat_argsc                   s  ddl m} ddlm} |s  n
 fdd}| jd krJ| j}| j}|j}	n | | j	}| | j
}| jf}	t|tr||j }|}
n| j}
t|
|s|| j}
|j}t|||fstd||jkrtd|d|jD ]4}|| jdkr|| |||j| i||< qt||
d	d
\}}|||i}|||}|jdkrt|jt|j D ]:}t|| jt|jk rZ|| jdd|||< qZt||rt||rt|D ]6}|	D ]*}||| jkr|| |d||< qq|S )Nr   r,   r.   c                   s
    || S r1   r2   )xyfr2   r8   <lambda>E      z$GroupBy._binary_op.<locals>.<lambda>zYGroupBy objects only support binary ops when the other argument is a Dataset or DataArrayzKincompatible dimensions for a grouped binary operation: the group variable z) is not a dimension on the other argumentouter)r   rh   Tr   .)r_   r-   r`   r/   r   r   r   rZ   _maybe_unstackr   r   r   rF   rw   r]   r   r   r=   rc   r   	drop_varsZexpand_dimsrz   r   selsetxindexesZreset_coordsZbroadcast_like	transpose)r{   otherr   Z	reflexiver-   r/   rQ   ri   r   rZ   r   r]   varrC   expandedrp   dr2   r   r8   
_binary_opA  sT    








zGroupBy._binary_opc                 C  s6   | j dk	r2| jj|jkr2| jj| j i}|jf |}|S )zOur index contained empty groups (e.g., from a resampling). If we
        reduced on that dimension, we want to restore the full index.
        N)r   r   r]   rZ   Zreindex)r{   combinedZindexersr2   r2   r8   _maybe_restore_empty_groups  s    z#GroupBy._maybe_restore_empty_groupsc                 C  sX   | j dk	rT| j |jkrT|| j }| jD ]}||jkr(|j|= q(t|jt|j|_|S )zVThis gets called if we are applying on an array with a
        multidimensional group.N)r   rZ   Zunstackr   rc   r   _indexesr   )r{   ri   r5   r2   r2   r8   r     s    


zGroupBy._maybe_unstack)r5   r   r   c                   s  ddl m} ddlm} j}|dkr0tdd}|dd |d	d}|rbd
d |j	 D }ni }|dksz|j
jkrj
j|jkr|jj
j }	|	jrjrtdj
j|dd  dkrtjtrވjj nj t }
t tr* |jkr  |jkr jdkr  f}
j   t|tr>|f}n2|dkrP j}n |dkrhtjj}nt|}t fdd|D rtd| djdk	r tjf}d}|d dkrd|ks|d dkrtj|d< d|d< |dtj njjf}d}|j| f||||d|}|j|
dd}|	 D ]N\}t fdd|D rHj!" jfj |j# j fj$ ||< qHjdk	rj%dk	st&j%|j
j< tj'|rj(j'jkr|)j
jd}|S )zAAdaptor function that translates our groupby API to that of flox.r   )xarray_reducer.   NTdefaultmethodzsplit-reducenumeric_onlyc                 S  s2   i | ]*\}}t |jt js|jt jks||qS r2   )rr   Z
issubdtyperT   numberZbool_)r4   r]   r   r2   r2   r8   rX     s
     z(GroupBy._flox_reduce.<locals>.<dictcomp>r:   r   .c                 3  s$   | ]}| j ko|jj kV  qd S r1   )rZ   r   r4   r   )r   r{   r2   r8   r9     s    z'GroupBy._flox_reduce.<locals>.<genexpr>r;   )Tr   countZ
fill_valuerh   Z	min_countF)r5   expected_groupsisbinr   ignore)errorsc                 3  s   | ]}| j kV  qd S r1   )rZ   r  )r   r2   r8   r9     s     )*Zflox.xarrayr  r`   r/   r   r   
setdefaultpopra   rb   r   r]   r   indexesr   r   r=   rF   r   rw   r   r   rZ   r  r   r<   rr   arraynanr   rN   r   r   r   Zset_dimsrz   r   r   rd   r   r   r   )r{   r5   r   r   r  r/   ri   r	  Znon_numericr   Zunindexed_dimsZ
parsed_dimr  r  rp   r]   r2   )r   r{   r   r8   _flox_reduce  s    


$






 zGroupBy._flox_reduce)valuerA   c                 C  s   t | |S )a  Fill missing values in this object by group.

        This operation follows the normal broadcasting and alignment rules that
        xarray uses for binary arithmetic, except the result is aligned to this
        object (``join='left'``) instead of aligned to the intersection of
        index coordinates (``join='inner'``).

        Parameters
        ----------
        value
            Used to fill all matching missing values by group. Needs
            to be of a valid type for the wrapped object's fillna
            method.

        Returns
        -------
        same type as the grouped object

        See Also
        --------
        Dataset.fillna
        DataArray.fillna
        )r   fillna)r{   r  r2   r2   r8   r    s    zGroupBy.fillnalinearr+   r"   zQuantileMethods | None)qr5   r  r   skipnainterpolationrA   c              
   C  s0   |dkr| j f}| j| jjjd||||||dS )a{  Compute the qth quantile over each array in the groups and
        concatenate them together into a new array.

        Parameters
        ----------
        q : float or sequence of float
            Quantile to compute, which must be between 0 and 1
            inclusive.
        dim : str or Iterable of Hashable, optional
            Dimension(s) over which to apply quantile.
            Defaults to the grouped dimension.
        method : str, default: "linear"
            This optional parameter specifies the interpolation method to use when the
            desired quantile lies between two data points. The options sorted by their R
            type as summarized in the H&F paper [1]_ are:

                1. "inverted_cdf" (*)
                2. "averaged_inverted_cdf" (*)
                3. "closest_observation" (*)
                4. "interpolated_inverted_cdf" (*)
                5. "hazen" (*)
                6. "weibull" (*)
                7. "linear"  (default)
                8. "median_unbiased" (*)
                9. "normal_unbiased" (*)

            The first three methods are discontiuous.  The following discontinuous
            variations of the default "linear" (7.) option are also available:

                * "lower"
                * "higher"
                * "midpoint"
                * "nearest"

            See :py:func:`numpy.quantile` or [1]_ for details. Methods marked with
            an asterix require numpy version 1.22 or newer. The "method" argument was
            previously called "interpolation", renamed in accordance with numpy
            version 1.22.0.
        keep_attrs : bool or None, default: None
            If True, the dataarray's attributes (`attrs`) will be copied from
            the original object to the new one.  If False, the new
            object will be returned without attributes.
        skipna : bool or None, default: None
            If True, skip missing values (as marked by NaN). By default, only
            skips missing values for float dtypes; other dtypes either do not
            have a sentinel missing value (int) or skipna=True has not been
            implemented (object, datetime64 or timedelta64).

        Returns
        -------
        quantiles : Variable
            If `q` is a single quantile, then the result is a
            scalar. If multiple percentiles are given, first axis of
            the result corresponds to the quantile. In either case a
            quantile dimension is added to the return array. The other
            dimensions are the dimensions that remain after the
            reduction of the array.

        See Also
        --------
        numpy.nanquantile, numpy.quantile, pandas.Series.quantile, Dataset.quantile
        DataArray.quantile

        Examples
        --------
        >>> da = xr.DataArray(
        ...     [[1.3, 8.4, 0.7, 6.9], [0.7, 4.2, 9.4, 1.5], [6.5, 7.3, 2.6, 1.9]],
        ...     coords={"x": [0, 0, 1], "y": [1, 1, 2, 2]},
        ...     dims=("x", "y"),
        ... )
        >>> ds = xr.Dataset({"a": da})
        >>> da.groupby("x").quantile(0)
        <xarray.DataArray (x: 2, y: 4)>
        array([[0.7, 4.2, 0.7, 1.5],
               [6.5, 7.3, 2.6, 1.9]])
        Coordinates:
          * y         (y) int64 1 1 2 2
            quantile  float64 0.0
          * x         (x) int64 0 1
        >>> ds.groupby("y").quantile(0, dim=...)
        <xarray.Dataset>
        Dimensions:   (y: 2)
        Coordinates:
            quantile  float64 0.0
          * y         (y) int64 1 2
        Data variables:
            a         (y) float64 0.7 0.7
        >>> da.groupby("x").quantile([0, 0.5, 1])
        <xarray.DataArray (x: 2, y: 4, quantile: 3)>
        array([[[0.7 , 1.  , 1.3 ],
                [4.2 , 6.3 , 8.4 ],
                [0.7 , 5.05, 9.4 ],
                [1.5 , 4.2 , 6.9 ]],
        <BLANKLINE>
               [[6.5 , 6.5 , 6.5 ],
                [7.3 , 7.3 , 7.3 ],
                [2.6 , 2.6 , 2.6 ],
                [1.9 , 1.9 , 1.9 ]]])
        Coordinates:
          * y         (y) int64 1 1 2 2
          * quantile  (quantile) float64 0.0 0.5 1.0
          * x         (x) int64 0 1
        >>> ds.groupby("y").quantile([0, 0.5, 1], dim=...)
        <xarray.Dataset>
        Dimensions:   (y: 2, quantile: 3)
        Coordinates:
          * quantile  (quantile) float64 0.0 0.5 1.0
          * y         (y) int64 1 2
        Data variables:
            a         (y, quantile) float64 0.7 5.35 8.4 0.7 2.25 9.4

        References
        ----------
        .. [1] R. J. Hyndman and Y. Fan,
           "Sample quantiles in statistical packages,"
           The American Statistician, 50(4), pp. 361-365, 1996
        NF)r   r  r5   r  r   r  r  )r   r   r   r   quantile)r{   r  r5   r  r   r  r  r2   r2   r8   r  1  s    ~zGroupBy.quantilec                 C  s   t | ||S )a   Return elements from `self` or `other` depending on `cond`.

        Parameters
        ----------
        cond : DataArray or Dataset
            Locations at which to preserve this objects values. dtypes have to be `bool`
        other : scalar, DataArray or Dataset, optional
            Value to use for locations in this object where ``cond`` is False.
            By default, inserts missing values.

        Returns
        -------
        same type as the grouped object

        See Also
        --------
        Dataset.where
        )r   Zwhere_method)r{   Zcondr   r2   r2   r8   r     s    zGroupBy.wherec                 C  s>   t | jd tr| jS |d kr(tdd}| j|| jg||dS )Nr   Tr  )r5   r  r   )rF   r   r    r   r   r   r   )r{   opr  r   r2   r2   r8   _first_or_last  s    
   zGroupBy._first_or_last)r  r   c                 C  s   |  tj||S )z@Return the first element of each group along the group dimension)r  r   r   r{   r  r   r2   r2   r8   r     s    zGroupBy.firstc                 C  s   |  tj||S )z?Return the last element of each group along the group dimension)r  r   lastr   r2   r2   r8   r!    s    zGroupBy.lastc                   s   t | d |  fddS )zAssign coordinates by group.

        See Also
        --------
        Dataset.assign_coords
        Dataset.swap_dims
        assign_coordsc                   s   | j f  S r1   )r"  dscoords_kwargsr2   r8   r     r   z'GroupBy.assign_coords.<locals>.<lambda>)r$   r   )r{   rc   r&  r2   r%  r8   r"    s    zGroupBy.assign_coords)FNNTN)r2   N)N)F)N)Nr  NNN)NN)NN)N) r   r   r   r   r   __annotations__r|   r   rz   r   r   rO   r   r   r   r   r   r   r   r  r  r   r  r  r  r   ZNAr   r  r   r!  r"  r2   r2   r2   r8   r     sd   
          	
A	        r   c                 C  s6   t |}|d ks"t|| j| kr&| S | ||i S d S r1   )rv   rJ   rz   )r\   r5   ru   orderr2   r2   r8   _maybe_reorder  s    r)  c                   @  s   e Zd ZU dZdZded< eddddZd	d
 Zd'ddZ	dddddZ
d(ddddddddZd)ddZd*ddZd+dddddd d!d"dd#d#ddd$d%d&ZdS ),DataArrayGroupByBasez8GroupBy object specialized to grouping DataArray objectsr2   ztuple[Hashable, ...] | Noner   ztuple[Hashable, ...]r}   c                 C  s,   | j d kr&| j| j| jd ij| _ | j S r   r   r   r   r   r   rZ   r   r2   r2   r8   rZ      s    
zDataArrayGroupByBase.dimsc                 c  s(   | j j}| jD ]}|| j|i V  qdS )zWFast version of `_iter_grouped` that yields Variables without
        metadata
        N)r   r   r   r   )r{   r   r   r2   r2   r8   _iter_grouped_shortcut  s    
z+DataArrayGroupByBase._iter_grouped_shortcutNc                 C  s(   t j||dd}t|||}| j|S )NTr   )r*   r   r)  r   Z_replace_maybe_drop_dims)r{   appliedr5   ru   stackedZ	reorderedr2   r2   r8   _concat_shortcut  s    z%DataArrayGroupByBase._concat_shortcutr-   )r/  rA   c                   s,    fdd}t |j|d}|j|d jiS )Nc                   s8   |  j jkr j j\} |  jjkr0 j| }nd}|S )Ng    .A)r   r]   rZ   r   Zget_axis_num)Z	dimensionr   r   r2   r8   lookup_order  s    
z=DataArrayGroupByBase._restore_dim_order.<locals>.lookup_order)r   Ztranspose_coords)sortedrZ   r   r   )r{   r/  r1  Z	new_orderr2   r   r8   _restore_dim_order  s    	z'DataArrayGroupByBase._restore_dim_orderzCallable[..., DataArray]r   r   r   r   c                   s8   |r|   n|  } fdd|D }| j||dS )a  Apply a function to each array in the group and concatenate them
        together into a new array.

        `func` is called like `func(ar, *args, **kwargs)` for each array `ar`
        in this group.

        Apply uses heuristics (like `pandas.GroupBy.apply`) to figure out how
        to stack together the array. The rule is:

        1. If the dimension along which the group coordinate is defined is
           still in the first grouped array after applying `func`, then stack
           over this dimension.
        2. Otherwise, stack over the new dimension given by name of this
           grouping (the argument to the `groupby` function).

        Parameters
        ----------
        func : callable
            Callable to apply to each array.
        shortcut : bool, optional
            Whether or not to shortcut evaluation under the assumptions that:

            (1) The action of `func` does not depend on any of the array
                metadata (attributes or coordinates) but only on the data and
                dimensions.
            (2) The action of `func` creates arrays with homogeneous metadata,
                that is, with the same dimensions and attributes.

            If these conditions are satisfied `shortcut` provides significant
            speedup. This should be the case for many common groupby operations
            (e.g., applying numpy ufuncs).
        *args : tuple, optional
            Positional arguments passed to `func`.
        **kwargs
            Used to call `func(ar, **kwargs)` for each array `ar`.

        Returns
        -------
        applied : DataArray
            The result of splitting, applying and combining this array.
        c                 3  s$   | ]}t ||f V  qd S r1   )r'   )r4   Zarrr   r   r   r2   r8   r9   X  s     z+DataArrayGroupByBase.map.<locals>.<genexpr>r-  )r,  r   _combine)r{   r   r   r   r   Zgroupedr.  r2   r4  r8   r   '  s    0zDataArrayGroupByBase.mapFc                 K  s(   t jdtdd | j|f||d|S )z
        Backward compatible implementation of ``map``

        See Also
        --------
        DataArrayGroupBy.map
        NGroupBy.apply may be deprecated in the future. Using GroupBy.map is encouraged   
stacklevelr   r   warningswarnPendingDeprecationWarningr   )r{   r   r   r   r   r2   r2   r8   apply[  s    zDataArrayGroupByBase.applyc           
        s   t |\}}| |\}}}|r0| |||}nt||}t|||}t|t| jr`| |}|dk	r||j	krt
|\ } fdd|D }	||	|}| |}| |}|S )0Recombine the applied objects like the original.Nc                   s   i | ]
}| qS r2   r2   r4   rV   r   r2   r8   rX   z  s      z1DataArrayGroupByBase._combine.<locals>.<dictcomp>)r(   r   r0  r   r)  rF   r   r   r3  rZ   r   _overwrite_indexesr  r   )
r{   r.  r   r   r   r5   ru   r  
index_varsr  r2   rB  r8   r5  j  s    



zDataArrayGroupByBase._combineTr   r   r!   r   r?   r   c          	        sZ   dkr| j gdkr"tddddd fdd}t| j | j||dS )	a  Reduce the items in this group by applying `func` along some
        dimension(s).

        Parameters
        ----------
        func : callable
            Function which can be called in the form
            `func(x, axis=axis, **kwargs)` to return the result of collapsing
            an np.ndarray over an integer valued axis.
        dim : "...", str, Iterable of Hashable or None, optional
            Dimension(s) over which to apply `func`. If None, apply over the
            groupby dimension, if "..." apply over all dimensions.
        axis : int or sequence of int, optional
            Axis(es) over which to apply `func`. Only one of the 'dimension'
            and 'axis' arguments can be supplied. If neither are supplied, then
            `func` is calculated over all dimension for each group item.
        keep_attrs : bool, optional
            If True, the datasets's attributes (`attrs`) will be copied from
            the original object to the new one.  If False (default), the new
            object will be returned without attributes.
        **kwargs : dict
            Additional keyword arguments passed on to `func`.

        Returns
        -------
        reduced : Array
            Array with summarized data and the indicated dimension(s)
            removed.
        NTr  r-   )rM   rA   c                   s   | j f  dS N)r   r5   r   r   r   r   )rM   r   r5   r   r   r   r   r2   r8   reduce_array  s    z1DataArrayGroupByBase.reduce.<locals>.reduce_arrayr-  r   r   r>   rZ   r   )	r{   r   r5   r   r   r   r   r   rH  r2   rG  r8   r     s    (

zDataArrayGroupByBase.reduce)N)r2   N)Fr2   )F)N)r   r   r   r   r   r'  r   rZ   r,  r0  r3  r   r?  r5  r   r2   r2   r2   r8   r*    s(   

  4

 r*  c                   @  s   e Zd ZdZdS )DataArrayGroupByr2   Nr   r   r   r   r2   r2   r2   r8   rJ    s   rJ  c                   @  s   e Zd ZU dZded< eddddZd"d	d
ddddddZd#ddZdd Z	d$ddddddddddddddddZ
dddd d!ZdS )%DatasetGroupByBaser2   zFrozen[Hashable, int] | Noner   r   r}   c                 C  s,   | j d kr&| j| j| jd ij| _ | j S r   r+  r   r2   r2   r8   rZ     s    
zDatasetGroupByBase.dimsNzCallable[..., Dataset]r   r   r   r/   r   c                   s$    fdd|   D }| |S )a@  Apply a function to each Dataset in the group and concatenate them
        together into a new Dataset.

        `func` is called like `func(ds, *args, **kwargs)` for each dataset `ds`
        in this group.

        Apply uses heuristics (like `pandas.GroupBy.apply`) to figure out how
        to stack together the datasets. The rule is:

        1. If the dimension along which the group coordinate is defined is
           still in the first grouped item after applying `func`, then stack
           over this dimension.
        2. Otherwise, stack over the new dimension given by name of this
           grouping (the argument to the `groupby` function).

        Parameters
        ----------
        func : callable
            Callable to apply to each sub-dataset.
        args : tuple, optional
            Positional arguments to pass to `func`.
        **kwargs
            Used to call `func(ds, **kwargs)` for each sub-dataset `ar`.

        Returns
        -------
        applied : Dataset
            The result of splitting, applying and combining this dataset.
        c                 3  s   | ]}|f V  qd S r1   r2   )r4   r$  r4  r2   r8   r9     s     z)DatasetGroupByBase.map.<locals>.<genexpr>)r   r5  )r{   r   r   r   r   r.  r2   r4  r8   r     s    %zDatasetGroupByBase.mapc                 K  s(   t jdtdd | j|f||d|S )z}
        Backward compatible implementation of ``map``

        See Also
        --------
        DatasetGroupBy.map
        r6  r7  r8  r:  r;  r   r2   r2   r8   r?    s    	zDatasetGroupByBase.applyc           	        s   t |\}}| |\}}}t||}t|||}|dk	rn||jkrnt|\ } fdd|D }|||}| |}| |}|S )r@  Nc                   s   i | ]
}| qS r2   r2   rA  rB  r2   r8   rX     s      z/DatasetGroupByBase._combine.<locals>.<dictcomp>)	r(   r   r   r)  rZ   r   rC  r  r   )	r{   r.  r   r   r5   ru   r  rD  r  r2   rB  r8   r5  
  s    


zDatasetGroupByBase._combineFTr   r   r!   r   r?   r   c          	        sV   dkr| j gdkr"tddddd fdd}t| j | |S )a  Reduce the items in this group by applying `func` along some
        dimension(s).

        Parameters
        ----------
        func : callable
            Function which can be called in the form
            `func(x, axis=axis, **kwargs)` to return the result of collapsing
            an np.ndarray over an integer valued axis.
        dim : ..., str, Iterable of Hashable or None, optional
            Dimension(s) over which to apply `func`. By default apply over the
            groupby dimension, with "..." apply over all dimensions.
        axis : int or sequence of int, optional
            Axis(es) over which to apply `func`. Only one of the 'dimension'
            and 'axis' arguments can be supplied. If neither are supplied, then
            `func` is calculated over all dimension for each group item.
        keep_attrs : bool, optional
            If True, the datasets's attributes (`attrs`) will be copied from
            the original object to the new one.  If False (default), the new
            object will be returned without attributes.
        **kwargs : dict
            Additional keyword arguments passed on to `func`.

        Returns
        -------
        reduced : Dataset
            Array with summarized data and the indicated dimension(s)
            removed.
        NTr  r/   )r$  rA   c                   s   | j f  dS rE  rF  r#  rG  r2   r8   reduce_datasetG  s    z1DatasetGroupByBase.reduce.<locals>.reduce_datasetrI  )	r{   r   r5   r   r   r   r   r   rM  r2   rG  r8   r     s    (

zDatasetGroupByBase.reduce)r   rA   c                   s   |   fddS )zbAssign data variables by group.

        See Also
        --------
        Dataset.assign
        c                   s   | j f  S r1   )assignr#  r   r2   r8   r   \  r   z+DatasetGroupByBase.assign.<locals>.<lambda>)r   )r{   r   r2   rO  r8   rN  U  s    zDatasetGroupByBase.assign)r2   N)r2   N)N)r   r   r   r   r'  r   rZ   r   r?  r5  r   rN  r2   r2   r2   r8   rL    s"   
	  (
  <rL  r/   c                   @  s   e Zd ZdZdS )DatasetGroupByr2   NrK  r2   r2   r2   r8   rP  `  s   rP  )T)W
__future__r   r   r<  typingr   r   r   r   r   r   r	   r
   r   r   r   r   Znumpyrr   ZpandasrE   Zxarray.corer   r   r   r   Zxarray.core._aggregationsr   r   Zxarray.core.alignmentr   Zxarray.core.arithmeticr   r   Zxarray.core.commonr   r   Zxarray.core.concatr   Zxarray.core.formattingr   Zxarray.core.indexesr   r   r   Zxarray.core.optionsr   Zxarray.core.pycompatr    Zxarray.core.typesr!   r"   r#   Zxarray.core.utilsr$   r%   r&   r'   r(   Zxarray.core.variabler)   r*   Znumpy.typingr+   r_   r-   r`   r/   r0   r   r>   rR   rf   rj   rq   rv   rw   r   r   r   r   r   r)  r*  rJ  rL  rP  r2   r2   r2   r8   <module>   sr   8 "'     W	 E
 
