"""
This type stub file was generated by pyright.
"""

import numpy as np
from typing import Callable, Generic, Hashable, Iterator, Sequence, TYPE_CHECKING, final
from pandas._typing import ArrayLike, NDFrameT, Shape, npt
from pandas.util._decorators import cache_readonly
from pandas.core.frame import DataFrame
from pandas.core.groupby import grouper
from pandas.core.indexes.api import Index
from pandas.core.series import Series
from pandas.core.generic import NDFrame

"""
Provide classes to perform the groupby aggregate operations.

These are not exposed to the user and provide implementations of the grouping
operations, primarily in cython. These classes (BaseGrouper and BinGrouper)
are contained *in* the SeriesGroupBy and DataFrameGroupBy objects.
"""
if TYPE_CHECKING:
    ...
class WrappedCythonOp:
    """
    Dispatch logic for functions defined in _libs.groupby

    Parameters
    ----------
    kind: str
        Whether the operation is an aggregate or transform.
    how: str
        Operation name, e.g. "mean".
    has_dropped_na: bool
        True precisely when dropna=True and the grouper contains a null value.
    """
    cast_blocklist = ...
    def __init__(self, kind: str, how: str, has_dropped_na: bool) -> None:
        ...
    
    _CYTHON_FUNCTIONS = ...
    _MASKED_CYTHON_FUNCTIONS = ...
    _cython_arity = ...
    def uses_mask(self) -> bool:
        ...
    
    @final
    def cython_operation(self, *, values: ArrayLike, axis: int, min_count: int = ..., comp_ids: np.ndarray, ngroups: int, **kwargs) -> ArrayLike:
        """
        Call our cython function, with appropriate pre- and post- processing.
        """
        ...
    


class BaseGrouper:
    """
    This is an internal Grouper class, which actually holds
    the generated groups

    Parameters
    ----------
    axis : Index
    groupings : Sequence[Grouping]
        all the grouping instances to handle in this grouper
        for example for grouper list to groupby, need to pass the list
    sort : bool, default True
        whether this grouper will give sorted result or not
    group_keys : bool, default True
    mutated : bool, default False
    indexer : np.ndarray[np.intp], optional
        the indexer created by Grouper
        some groupers (TimeGrouper) will sort its axis and its
        group_info is also sorted, so need the indexer to reorder

    """
    axis: Index
    def __init__(self, axis: Index, groupings: Sequence[grouper.Grouping], sort: bool = ..., group_keys: bool = ..., mutated: bool = ..., indexer: npt.NDArray[np.intp] | None = ..., dropna: bool = ...) -> None:
        ...
    
    @property
    def groupings(self) -> list[grouper.Grouping]:
        ...
    
    @property
    def shape(self) -> Shape:
        ...
    
    def __iter__(self) -> Iterator[Hashable]:
        ...
    
    @property
    def nkeys(self) -> int:
        ...
    
    def get_iterator(self, data: NDFrameT, axis: int = ...) -> Iterator[tuple[Hashable, NDFrameT]]:
        """
        Groupby iterator

        Returns
        -------
        Generator yielding sequence of (name, subsetted object)
        for each group
        """
        ...
    
    @final
    @cache_readonly
    def group_keys_seq(self): # -> Index | list[tuple[Unknown, ...]]:
        ...
    
    @final
    def apply(self, f: Callable, data: DataFrame | Series, axis: int = ...) -> tuple[list, bool]:
        ...
    
    @cache_readonly
    def indices(self) -> dict[Hashable, npt.NDArray[np.intp]]:
        """dict {group name -> group indices}"""
        ...
    
    @final
    def result_ilocs(self) -> npt.NDArray[np.intp]:
        """
        Get the original integer locations of result_index in the input.
        """
        ...
    
    @final
    @property
    def codes(self) -> list[npt.NDArray[np.signedinteger]]:
        ...
    
    @property
    def levels(self) -> list[Index]:
        ...
    
    @property
    def names(self) -> list[Hashable]:
        ...
    
    @final
    def size(self) -> Series:
        """
        Compute group sizes.
        """
        ...
    
    @cache_readonly
    def groups(self) -> dict[Hashable, np.ndarray]:
        """dict {group name -> group labels}"""
        ...
    
    @final
    @cache_readonly
    def is_monotonic(self) -> bool:
        ...
    
    @final
    @cache_readonly
    def has_dropped_na(self) -> bool:
        """
        Whether grouper has null value(s) that are dropped.
        """
        ...
    
    @cache_readonly
    def group_info(self) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp], int]:
        ...
    
    @final
    @cache_readonly
    def codes_info(self) -> npt.NDArray[np.intp]:
        ...
    
    @final
    @cache_readonly
    def ngroups(self) -> int:
        ...
    
    @property
    def reconstructed_codes(self) -> list[npt.NDArray[np.intp]]:
        ...
    
    @cache_readonly
    def result_index(self) -> Index:
        ...
    
    @final
    def get_group_levels(self) -> list[ArrayLike]:
        ...
    
    @final
    def agg_series(self, obj: Series, func: Callable, preserve_dtype: bool = ...) -> ArrayLike:
        """
        Parameters
        ----------
        obj : Series
        func : function taking a Series and returning a scalar-like
        preserve_dtype : bool
            Whether the aggregation is known to be dtype-preserving.

        Returns
        -------
        np.ndarray or ExtensionArray
        """
        ...
    


class BinGrouper(BaseGrouper):
    """
    This is an internal Grouper class

    Parameters
    ----------
    bins : the split index of binlabels to group the item of axis
    binlabels : the label list
    mutated : bool, default False
    indexer : np.ndarray[np.intp]

    Examples
    --------
    bins: [2, 4, 6, 8, 10]
    binlabels: DatetimeIndex(['2005-01-01', '2005-01-03',
        '2005-01-05', '2005-01-07', '2005-01-09'],
        dtype='datetime64[ns]', freq='2D')

    the group_info, which contains the label of each item in grouped
    axis, the index of label in label list, group number, is

    (array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4]), array([0, 1, 2, 3, 4]), 5)

    means that, the grouped axis has 10 items, can be grouped into 5
    labels, the first and second items belong to the first label, the
    third and forth items belong to the second label, and so on

    """
    bins: npt.NDArray[np.int64]
    binlabels: Index
    mutated: bool
    def __init__(self, bins, binlabels, mutated: bool = ..., indexer=...) -> None:
        ...
    
    @cache_readonly
    def groups(self): # -> dict[Any, Any]:
        """dict {group name -> group labels}"""
        ...
    
    @property
    def nkeys(self) -> int:
        ...
    
    def get_iterator(self, data: NDFrame, axis: int = ...): # -> Generator[tuple[Any, Unknown] | tuple[Any | Index | Unknown | ExtensionArray, Unknown], None, None]:
        """
        Groupby iterator

        Returns
        -------
        Generator yielding sequence of (name, subsetted object)
        for each group
        """
        ...
    
    @cache_readonly
    def indices(self): # -> defaultdict[Unknown, list[Unknown]]:
        ...
    
    @cache_readonly
    def group_info(self) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp], int]:
        ...
    
    @cache_readonly
    def reconstructed_codes(self) -> list[np.ndarray]:
        ...
    
    @cache_readonly
    def result_index(self) -> Index:
        ...
    
    @property
    def levels(self) -> list[Index]:
        ...
    
    @property
    def names(self) -> list[Hashable]:
        ...
    
    @property
    def groupings(self) -> list[grouper.Grouping]:
        ...
    


class DataSplitter(Generic[NDFrameT]):
    def __init__(self, data: NDFrameT, labels: npt.NDArray[np.intp], ngroups: int, axis: int = ...) -> None:
        ...
    
    @cache_readonly
    def slabels(self) -> npt.NDArray[np.intp]:
        ...
    
    def __iter__(self): # -> Generator[NDFrame, None, None]:
        ...
    
    @cache_readonly
    def sorted_data(self) -> NDFrameT:
        ...
    


class SeriesSplitter(DataSplitter):
    ...


class FrameSplitter(DataSplitter):
    ...


def get_splitter(data: NDFrame, labels: np.ndarray, ngroups: int, axis: int = ...) -> DataSplitter:
    ...

