From 4db4f0a2cbad57f6932ee54c9963af46fc3a4d63 Mon Sep 17 00:00:00 2001 From: pwwang <1188067+pwwang@users.noreply.github.com> Date: Tue, 15 Mar 2022 18:39:20 -0500 Subject: [PATCH] 0.6.3 (#91) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Allow `base.c()` to handle groupby data * πŸš‘ Allow `base.diff()` to work with groupby data * ✨ Allow `forcats.fct_inorder()` to work with groupby data * Add SeriesGroupBy as available type for forcats verbs * πŸš‘ Fix `base.diff()` not keep empty groups ✨Allow `base.rep()`'s arguments `length` and `each` to work with grouped data ✨Allow `base.c()` to work with grouped data πŸ› Fix recycling non-ordered grouped data πŸ› Force `&/|` operators to return boolean data πŸš‘ Make `dplyr.n()` return groupoed data 🩹 Fix `dplyr.count()/tally()`'s warning about the new name πŸ› Make `dplyr.slice()` work better with rows/indices from grouped data * ✨ Add `datar.attrgetter()`, `datar.pd_str()`, `datar.pd_cat()` and `datar.pd_dt()` * πŸš‘ Fix `base.c()` with grouped data * πŸ“ Update docs for `datar.datar` * πŸ”– 0.6.3 * Update readme.ipynb --- datar/__init__.py | 2 +- datar/base/__init__.py | 2 +- datar/base/funs.py | 25 +- datar/base/rep.py | 165 ++++++++ datar/base/seq.py | 122 +++--- datar/core/broadcast.py | 52 ++- datar/core/operator.py | 17 +- datar/core/utils.py | 16 + datar/datar/__init__.py | 2 +- datar/datar/funcs.py | 100 ++++- datar/dplyr/context.py | 16 +- datar/dplyr/count_tally.py | 4 +- datar/dplyr/dslice.py | 89 +++-- datar/dplyr/group_data.py | 4 +- datar/forcats/lvl_order.py | 19 +- datar/forcats/utils.py | 3 + docs/CHANGELOG.md | 15 + docs/notebooks/datar.ipynb | 498 +++++++++++++++++++++++- docs/notebooks/readme.ipynb | 54 +-- docs/reference-maps/datar.md | 24 +- pyproject.toml | 2 +- tests/base/test_funs.py | 9 +- tests/base/test_rep.py | 80 ++++ tests/base/test_seq.py | 37 +- tests/core/test_broadcast.py | 4 + tests/core/test_operator.py | 7 +- tests/core/test_utils.py | 10 + tests/dplyr/test_across.py | 2 +- tests/dplyr/test_empty_groups.py | 81 ++-- tests/dplyr/test_slice.py | 42 +- tests/forcats/test_forcats_lvl_order.py | 78 ++-- tests/test_datar.py | 61 ++- 32 files changed, 1339 insertions(+), 303 deletions(-) create mode 100644 datar/base/rep.py create mode 100644 tests/base/test_rep.py diff --git a/datar/__init__.py b/datar/__init__.py index 3b3d9061..ea32249b 100644 --- a/datar/__init__.py +++ b/datar/__init__.py @@ -30,7 +30,7 @@ ) __all__ = ("f", "get_versions") -__version__ = "0.6.2" +__version__ = "0.6.3" def get_versions(prnt: bool = True) -> _VersionsTuple: diff --git a/datar/base/__init__.py b/datar/base/__init__.py index 6d041b46..397cc0b0 100644 --- a/datar/base/__init__.py +++ b/datar/base/__init__.py @@ -80,12 +80,12 @@ from .na import NA, NaN, any_na, is_na, Inf, is_finite, is_infinite, is_nan from .null import NULL, as_null, is_null from .random import set_seed +from .rep import rep from .seq import ( c, length, lengths, order, - rep, rev, sample, seq, diff --git a/datar/base/funs.py b/datar/base/funs.py index b32428cb..6aabfc83 100644 --- a/datar/base/funs.py +++ b/datar/base/funs.py @@ -6,8 +6,9 @@ import itertools import numpy as np -import pandas +import pandas as pd from pandas.api.types import is_scalar +from pandas.core.groupby import SeriesGroupBy from pipda import register_func from ..core.middlewares import WithDataEnv @@ -56,7 +57,7 @@ def cut( if labels is None: ordered_result = True - return pandas.cut( + return pd.cut( x, breaks, labels=labels, @@ -67,7 +68,7 @@ def cut( ) -@func_factory("agg", "x") +@func_factory("apply", "x") def diff(x, lag: int = 1, differences: int = 1): """Calculates suitably lagged and iterated differences. @@ -94,11 +95,29 @@ def diff(x, lag: int = 1, differences: int = 1): If `differences > 1`, the rule applies `differences` times on `x` """ x = x.values + if lag * differences >= x.size: + return np.array([], dtype=x.dtype) + for _ in range(differences): x = x[lag:] - x[:-lag] return x +def _diff_sgb_post(out, x, lag=1, differences=1): + """Post process diff on SeriesGroupBy object""" + non_na_out = out[out.transform(len) > 0] + non_na_out = non_na_out.explode() + grouping = pd.Categorical(non_na_out.index, categories=out.index.unique()) + return ( + non_na_out.explode() + .reset_index(drop=True) + .groupby(grouping, observed=False) + ) + + +diff.register(SeriesGroupBy, func=None, post=_diff_sgb_post) + + @register_func(None, context=Context.EVAL) def identity(x): """Return whatever passed in diff --git a/datar/base/rep.py b/datar/base/rep.py new file mode 100644 index 00000000..04f92568 --- /dev/null +++ b/datar/base/rep.py @@ -0,0 +1,165 @@ +from functools import singledispatch + +import numpy as np +import pandas as pd +from pandas import DataFrame, Series, Categorical +from pandas.api.types import is_scalar, is_integer +from pandas.core.groupby import SeriesGroupBy +from pipda import register_func + +from ..core.contexts import Context +from ..core.tibble import TibbleGrouped, reconstruct_tibble +from ..core.utils import ensure_nparray, logger + + +def _rep(x, times, length, each): + """Repeat sequence x""" + x = ensure_nparray(x) + times = ensure_nparray(times) + length = ensure_nparray(length) + each = ensure_nparray(each) + if times.size == 1: + times = times[0] + if length.size >= 1: + if length.size > 1: + logger.warning( + "In rep(...) : first element used of 'length' argument" + ) + length = length[0] + if each.size == 1: + each = each[0] + + if not is_scalar(times): + if times.size != x.size: + raise ValueError( + "Invalid times argument, expect length " + f"{x.size}, got {times.size}" + ) + + if not is_integer(each) or each != 1: + raise ValueError( + "Unexpected each argument when times is an iterable." + ) + + if is_integer(times) and is_scalar(times): + x = np.tile(np.repeat(x, each), times) + else: + x = np.repeat(x, times) + + if length is None: + return x + + repeats = length // x.size + 1 + x = np.tile(x, repeats) + + return x[:length] + + +@singledispatch +def _rep_dispatched(x, times, length, each): + """Repeat sequence x""" + times_sgb = isinstance(times, SeriesGroupBy) + length_sgb = isinstance(length, SeriesGroupBy) + each_sgb = isinstance(each, SeriesGroupBy) + values = {} + if times_sgb: + values["times"] = times + if length_sgb: + values["length"] = length + if each_sgb: + values["each"] = each + + if values: + from ..tibble import tibble + df = tibble(**values) + out = df._datar["grouped"].apply( + lambda subdf: _rep( + x, + times=subdf["times"] if times_sgb else times, + length=subdf["length"] if length_sgb else length, + each=subdf["each"] if each_sgb else each, + ) + ) + non_na_out = out[out.transform(len) > 0] + non_na_out = non_na_out.explode() + grouping = Categorical(non_na_out.index, categories=out.index.unique()) + return ( + non_na_out.explode() + .reset_index(drop=True) + .groupby(grouping, observed=False) + ) + + return _rep(x, times, length, each) + + +@_rep_dispatched.register(Series) +def _(x, times, length, each): + return _rep_dispatched.dispatch(object)(x.values, times, length, each) + + +@_rep_dispatched.register(SeriesGroupBy) +def _(x, times, length, each): + from ..tibble import tibble + df = tibble(x=x) + times_sgb = isinstance(times, SeriesGroupBy) + length_sgb = isinstance(length, SeriesGroupBy) + each_sgb = isinstance(each, SeriesGroupBy) + if times_sgb: + df["times"] = times + if length_sgb: + df["length"] = length + if each_sgb: + df["each"] = each + + out = df._datar["grouped"].apply( + lambda subdf: _rep( + subdf["x"], + times=subdf["times"] if times_sgb else times, + length=subdf["length"] if length_sgb else length, + each=subdf["each"] if each_sgb else each, + ) + ).explode().astype(x.obj.dtype) + grouping = out.index + return out.reset_index(drop=True).groupby(grouping) + + +@_rep_dispatched.register(DataFrame) +def _(x, times, length, each): + if not is_integer(each) or each != 1: + raise ValueError( + "`each` has to be 1 to replicate a data frame." + ) + + out = pd.concat([x] * times, ignore_index=True) + if length is not None: + out = out.iloc[:length, :] + + return out + + +@_rep_dispatched.register(TibbleGrouped) +def _(x, times, length, each): + out = _rep_dispatched.dispatch(DataFrame)(x, times, length, each) + return reconstruct_tibble(x, out) + + +@register_func(None, context=Context.EVAL) +def rep( + x, + times=1, + length=None, + each=1, +): + """replicates the values in x + + Args: + x: a vector or scaler + times: number of times to repeat each element if of length len(x), + or to repeat the whole vector if of length 1 + length: non-negative integer. The desired length of the output vector + each: non-negative integer. Each element of x is repeated each times. + + Returns: + An array of repeated elements in x. + """ + return _rep_dispatched(x, times, length, each) diff --git a/datar/base/seq.py b/datar/base/seq.py index 6cd8d044..97a11385 100644 --- a/datar/base/seq.py +++ b/datar/base/seq.py @@ -1,14 +1,14 @@ import numpy as np -from pandas import Series -from pandas.api.types import is_scalar, is_integer +from pandas import DataFrame, Series +from pandas.api.types import is_scalar from pandas.core.groupby import SeriesGroupBy, GroupBy from pipda import register_func -from ..core.utils import ensure_nparray, logger, regcall +from ..core.utils import logger, regcall from ..core.factory import func_factory from ..core.contexts import Context from ..core.collections import Collection -from ..core.tibble import TibbleGrouped, reconstruct_tibble +from ..core.tibble import TibbleGrouped @register_func(None, context=Context.EVAL) @@ -80,77 +80,6 @@ def seq( return np.array([from_ + n * by for n in range(int(length_out))]) -@register_func(None, context=Context.UNSET) -def c(*elems): - """Mimic R's concatenation. Named one is not supported yet - All elements passed in will be flattened. - - Args: - *elems: The elements - - Returns: - A collection of elements - """ - return Collection(*elems) - - -@func_factory("apply", "x") -def rep( - x, - times=1, - length=None, - each=1 -): - """replicates the values in x - - Args: - x: a vector or scaler - times: number of times to repeat each element if of length len(x), - or to repeat the whole vector if of length 1 - length: non-negative integer. The desired length of the output vector - each: non-negative integer. Each element of x is repeated each times. - - Returns: - A list of repeated elements in x. - """ - x = ensure_nparray(x) - if not is_scalar(times): - if len(times) != len(x): - raise ValueError( - "Invalid times argument, expect length " - f"{len(times)}, got {len(x)}" - ) - if each != 1: - raise ValueError( - "Unexpected each argument when times is an iterable." - ) - - if is_integer(times) and is_scalar(times): - x = np.tile(x.repeat(each), times) - else: - x = x.repeat(times) - if length is None: - return x - - repeats = length // len(x) + 1 - x = np.tile(x, repeats) - return x[:length] - - -rep.register( - SeriesGroupBy, - func=None, - post=lambda out, x, *args, **kwargs: out.explode().astype(x.obj.dtype) -) - - -rep.register( - TibbleGrouped, - func=None, - post=lambda out, x, *args, **kwargs: reconstruct_tibble(x, out) -) - - @func_factory("agg", "x") def length(x): """Get length of elements""" @@ -321,3 +250,46 @@ def match_dummy(xx, tab): return Series(match_dummy(x, table), index=x.index) return match_dummy(x, table) + + +@register_func(None, context=Context.UNSET) +def c(*elems): + """Mimic R's concatenation. Named one is not supported yet + All elements passed in will be flattened. + + Args: + *elems: The elements + + Returns: + A collection of elements + """ + if not any(isinstance(elem, SeriesGroupBy) for elem in elems): + return Collection(*elems) + + from ..tibble import tibble + + values = [] + for elem in elems: + if isinstance(elem, SeriesGroupBy): + values.append(elem.agg(list)) + elif is_scalar(elem): + values.append(elem) + else: + values.extend(elem) + + df = tibble(*values) + # pandas 1.3.0 expand list into columns after aggregation + # pandas 1.3.2 has this fixed + # https://github.com/pandas-dev/pandas/issues/42727 + out = df.agg( + lambda row: Collection(*row), + axis=1, + ) + if isinstance(out, DataFrame): + # pandas < 1.3.2 + out = Series(out.values.tolist(), index=out.index, dtype=object) + + out = out.explode().convert_dtypes() + grouping = out.index + out = out.reset_index(drop=True).groupby(grouping) + return out diff --git a/datar/core/broadcast.py b/datar/core/broadcast.py index 2bbe3e8c..6ed093d3 100644 --- a/datar/core/broadcast.py +++ b/datar/core/broadcast.py @@ -34,7 +34,7 @@ from pandas.api.types import is_list_like from .tibble import Tibble, TibbleGrouped, TibbleRowwise -from .utils import name_of, regcall +from .utils import name_of, regcall, dict_get if TYPE_CHECKING: from pandas import Grouper @@ -65,7 +65,7 @@ def _regroup(x: GroupBy, new_sizes: Union[int, np.ndarray]) -> GroupBy: return x.obj.take(indices).groupby(grouped.grouper) -def _agg_result_compatible(index: "Index", grouper: "Grouper") -> bool: +def _agg_result_compatible(index: Index, grouper: "Grouper") -> bool: """Check index of an aggregated result is compatible with a grouper""" if index.names != grouper.names: return False @@ -102,9 +102,29 @@ def _grouper_compatible(grouper1: "Grouper", grouper2: "Grouper") -> bool: # also check the size size1 = grouper1.size() size2 = grouper2.size() + size2 = size2.reindex(size1.index).values + size1 = size1.values return ((size1 == 1) | (size2 == 1) | (size1 == size2)).all() +def _realign_indexes(value: GroupBy, grouper: "Grouper"): + """Realign indexes of a value to a grouper""" + v_new_indices = [] + g_indices = [] + for key in value.grouper.result_index: + v_ind = dict_get(value.grouper.indices, key) + g_ind = dict_get(grouper.indices, key) + if v_ind.size == 1 and g_ind.size > 1: + v_new_indices.extend(v_ind.repeat(g_ind.size)) + else: + v_new_indices.extend(v_ind) + g_indices.extend(g_ind) + + value = value.obj.take(v_new_indices) + sorted_indices = np.argsort(g_indices) + return value.take(sorted_indices).values + + @singledispatch def _broadcast_base( value, @@ -210,7 +230,6 @@ def _( name = name or name_of(value) or str(value) if isinstance(base, GroupBy): - if not _grouper_compatible(value.grouper, base.grouper): raise ValueError(f"`{name}` has an incompatible grouper.") @@ -360,7 +379,7 @@ def _( @singledispatch def broadcast_to( value, - index: "Index", + index: Index, grouper: "Grouper" = None, ) -> Series: """Broastcast value to expected dimension, the result is a series with @@ -455,7 +474,7 @@ def broadcast_to( @broadcast_to.register(Categorical) def _( value: Categorical, - index: "Index", + index: Index, grouper: "Grouper" = None, ) -> Series: """Broadcast categorical data""" @@ -487,7 +506,7 @@ def _( @broadcast_to.register(NDFrame) def _( value: NDFrame, - index: "Index", + index: Index, grouper: "Grouper" = None, ) -> Union[Tibble, Series]: """Broadcast series/dataframe""" @@ -546,7 +565,7 @@ def _( @broadcast_to.register(GroupBy) def _( value: GroupBy, - index: "Index", + index: Index, grouper: "Grouper" = None, ) -> Union[Series, Tibble]: """Broadcast pandas grouped object""" @@ -557,15 +576,26 @@ def _( # Compatibility has been checked in _broadcast_base if isinstance(value, SeriesGroupBy): - return Series(value.obj, index=index, name=value.obj.name) + if np.array_equal(grouper.group_info[0], value.grouper.group_info[0]): + return Series(value.obj.values, index=index, name=value.obj.name) + + # broadcast size-one groups and + # realign the index + revalue = _realign_indexes(value, grouper) + return Series(revalue, index=index, name=value.obj.name) + + if np.array_equal(grouper.group_info[0], value.grouper.group_info[0]): + return Tibble(value.obj.values, index=index, columns=value.obj.columns) - return Tibble(value.obj, index=index) + # realign the index + revalue = _realign_indexes(value, grouper) + return Tibble(revalue, index=index, columns=value.obj.columns) @broadcast_to.register(TibbleGrouped) def _( value: TibbleGrouped, - index: "Index", + index: Index, grouper: "Grouper" = None, ) -> Tibble: """Broadcast TibbleGrouped object""" @@ -577,7 +607,7 @@ def _( @singledispatch -def _get_index_grouper(value) -> Tuple["Index", "Grouper"]: +def _get_index_grouper(value) -> Tuple[Index, "Grouper"]: return None, None diff --git a/datar/core/operator.py b/datar/core/operator.py index eb4b80ef..dd659f67 100644 --- a/datar/core/operator.py +++ b/datar/core/operator.py @@ -11,18 +11,18 @@ from .collections import Collection, Inverted, Negated, Intersect -def _binop(op, left, right, fill_false=False): +def _binop(op, left, right, boolean=False): left, right, grouper, is_rowwise = broadcast2(left, right) - if fill_false: + if boolean: if isinstance(left, Series): - left = left.fillna(False) + left = left.fillna(False).astype(bool) else: - left = Series(left).fillna(False).values + left = Series(left).fillna(False).astype(bool).values if isinstance(right, Series): - right = right.fillna(False) + right = right.fillna(False).astype(bool) else: - right = Series(right).fillna(False).values + right = Series(right).fillna(False).astype(bool).values out = op(left, right) if grouper: @@ -56,6 +56,7 @@ def _op_invert(self, operand: Any) -> Any: """Interpretation for ~x""" if isinstance(operand, (slice, Sequence)): return Inverted(operand) + return self._arithmetize1(operand, "invert") def _op_neg(self, operand: Any) -> Any: @@ -84,7 +85,7 @@ def _op_and_(self, left: Any, right: Any) -> Any: # induce an intersect with Collection return Intersect(left, right) - return _binop(operator.and_, left, right, fill_false=True) + return _binop(operator.and_, left, right, boolean=True) def _op_or_(self, left: Any, right: Any) -> Any: """Mimic the & operator in R. @@ -102,7 +103,7 @@ def _op_or_(self, left: Any, right: Any) -> Any: # or union? return Collection(left, right) - return _binop(operator.or_, left, right, fill_false=True) + return _binop(operator.or_, left, right, boolean=True) # def _op_eq( # self, left: Any, right: Any diff --git a/datar/core/utils.py b/datar/core/utils.py index 426367bf..ee28d3ba 100644 --- a/datar/core/utils.py +++ b/datar/core/utils.py @@ -6,6 +6,7 @@ from functools import singledispatch import numpy as np +import pandas as pd from pandas import DataFrame, Series from pandas.api.types import is_scalar from pandas.core.groupby import SeriesGroupBy @@ -155,3 +156,18 @@ def apply_dtypes(df: DataFrame, dtypes) -> None: for col in df: if col.startswith(f"{column}$"): df[col] = df[col].astype(dtype) + + +def dict_get(d, key, default=sys): + """Get value from dict in case nan is in the key""" + try: + return d[key] + except KeyError: + if pd.isnull(key): + for k, v in d.items(): + if pd.isnull(k): + return v + + if default is sys: + raise + return default diff --git a/datar/datar/__init__.py b/datar/datar/__init__.py index 382a6f1d..c3ed71a3 100644 --- a/datar/datar/__init__.py +++ b/datar/datar/__init__.py @@ -1,4 +1,4 @@ """Specific verbs/funcs from this package""" from .verbs import get, flatten -from .funcs import itemgetter +from .funcs import itemgetter, attrgetter, pd_str, pd_cat, pd_dt diff --git a/datar/datar/funcs.py b/datar/datar/funcs.py index 1ec59424..d2e5a68b 100644 --- a/datar/datar/funcs.py +++ b/datar/datar/funcs.py @@ -1,10 +1,12 @@ """Basic functions""" from pandas import Series from pandas.core.groupby import SeriesGroupBy -from pipda import evaluate_expr +from pipda import evaluate_expr, register_func + from ..core.factory import func_factory from ..core.contexts import Context from ..core.collections import Collection +from ..core.utils import regcall @func_factory("apply", "x") @@ -42,3 +44,99 @@ def itemgetter(x, subscr, __args_raw=None): post=lambda out, x, subscr, __args_raw=None: out.explode().astype(x.obj.dtype) ) + + +class _MethodAccessor: + """Method holder for `_Accessor` objects""" + + def __init__(self, accessor, method): + self.accessor = accessor + self.method = method + + def __call__(self, *args, **kwds): + out = self.accessor.sgb.apply( + lambda x: getattr( + getattr(x, self.accessor.name), + self.method + )(*args, **kwds) + ) + + try: + return out.groupby(self.accessor.sgb.grouper) + except (AttributeError, ValueError, TypeError): # pragma: no cover + return out + + +class _Accessor: + """Accessor for special columns, such as `.str`, `.cat` and `.dt`, etc + + This is used for SeriesGroupBy object, since `sgb.str` cannot be evaluated + immediately. + """ + def __init__(self, sgb: SeriesGroupBy, name: str): + self.sgb = sgb + self.name = name + + def __getitem__(self, key): + return _MethodAccessor(self, "__getitem__")(key) + + def __getattr__(self, name): + # See if name is a method + accessor = getattr(Series, self.name) # Series.str + attr_or_method = getattr(accessor, name, None) + + if callable(attr_or_method): + # x.str.lower() + return _MethodAccessor(self, name) + + # x.cat.categories + out = self.sgb.apply( + lambda x: getattr(getattr(x, self.name), name) + ) + + try: + return out.groupby(self.sgb.grouper) + except (AttributeError, ValueError, TypeError): # pragma: no cover + return out + + +@func_factory("agg", "x") +def attrgetter(x, attr): + """Attrgetter as a function for verb + + This is helpful when we want to access to an accessor + (ie. CategoricalAccessor) from a SeriesGroupBy object + """ + return getattr(x, attr) + + +@attrgetter.register(SeriesGroupBy, meta=False) +def _(x, attr): + return _Accessor(x, attr) + + +@register_func(None, context=Context.EVAL) +def pd_str(x): + """Pandas' str accessor for a Series (x.str) + + This is helpful when x is a SeriesGroupBy object + """ + return regcall(attrgetter, x, "str") + + +@register_func(None, context=Context.EVAL) +def pd_cat(x): + """Pandas' cat accessor for a Series (x.cat) + + This is helpful when x is a SeriesGroupBy object + """ + return regcall(attrgetter, x, "cat") + + +@register_func(None, context=Context.EVAL) +def pd_dt(x): + """Pandas' dt accessor for a Series (x.dt) + + This is helpful when x is a SeriesGroupBy object + """ + return regcall(attrgetter, x, "dt") diff --git a/datar/dplyr/context.py b/datar/dplyr/context.py index 31a8cfb7..b0c38fcd 100644 --- a/datar/dplyr/context.py +++ b/datar/dplyr/context.py @@ -8,7 +8,7 @@ from ..core.tibble import Tibble, TibbleGrouped from ..core.middlewares import CurColumn -from ..core.utils import regcall +from ..core.utils import dict_get, regcall from ..base import setdiff from .group_data import group_data, group_keys @@ -26,7 +26,17 @@ def n(_data, _context=None): @n.register(TibbleGrouped) def _(_data, _context=None): _data = _context.meta.get("input_data", _data) - return _data._datar["grouped"].grouper.size() + grouped = _data._datar["grouped"] + + out = grouped.grouper.size().to_frame().reset_index() + out = out.groupby( + grouped.grouper.names, + sort=grouped.sort, + observed=grouped.observed, + dropna=grouped.dropna, + )[0] + + return out @register_func(DataFrame, verb_arg_only=True) @@ -43,7 +53,7 @@ def _(_data, _context=None): grouped = _data._datar["grouped"] return Series( [ - grouped.obj.loc[grouped.grouper.groups[key], :] + grouped.obj.loc[dict_get(grouped.grouper.groups, key), :] for key in grouped.grouper.result_index ], name="cur_data_all", diff --git a/datar/dplyr/count_tally.py b/datar/dplyr/count_tally.py index 64dfbb06..3cab1fb4 100644 --- a/datar/dplyr/count_tally.py +++ b/datar/dplyr/count_tally.py @@ -183,7 +183,6 @@ def _tally_n(wt): # If it's Expression, will return a Function object # Otherwise, sum of wt return Function(sum_, (wt, ), {"na_rm": True}, dataarg=False) - # return sum_(wt, na_rm=True, __calling_env=CallingEnvs.PIPING) def _check_name(name, invars): @@ -194,7 +193,8 @@ def _check_name(name, invars): if name != "n": logger.warning( "Storing counts in `%s`, as `n` already present in input. " - 'Use `name="new_name" to pick a new name.`' + 'Use `name="new_name" to pick a new name.`', + name, ) elif not isinstance(name, str): raise ValueError("`name` must be a single string.") diff --git a/datar/dplyr/dslice.py b/datar/dplyr/dslice.py index b90cf952..1c17851a 100644 --- a/datar/dplyr/dslice.py +++ b/datar/dplyr/dslice.py @@ -3,21 +3,27 @@ https://github.com/tidyverse/dplyr/blob/master/R/slice.R """ import builtins -from typing import Any, Iterable, Union +from typing import TYPE_CHECKING, Any, Iterable, Mapping, Union import numpy as np import pandas as pd from pandas import DataFrame +from pandas.api.types import is_integer +from pandas.core.groupby import SeriesGroupBy # from pandas.api.types import is_integer from pipda import register_verb, Expression +from datar.core.collections import Collection + from ..core.broadcast import _ungroup from ..core.contexts import Context -from ..core.collections import Collection -from ..core.utils import logger, regcall +from ..core.utils import dict_get, logger, regcall from ..core.tibble import Tibble, TibbleGrouped, TibbleRowwise +if TYPE_CHECKING: + from pandas import Index + @register_verb(DataFrame, context=Context.SELECT) def slice( @@ -71,27 +77,11 @@ def _( logger.warning("`slice()` doesn't support `_preserve` argument yet.") grouped = _data._datar["grouped"] - gsizes = grouped.grouper.size() - indices = [ - grouped.grouper.indices[key].take( - _sanitize_rows(rows, gsizes.loc[key]) - ) - for key in grouped.grouper.result_index - # grouped.grouper.indices[key] gets empty [] when it's an empty group - if grouped.grouper.indices[key].size > 0 - ] - if indices: - indices = np.concatenate( - [ - grouped.grouper.indices[key].take( - _sanitize_rows(rows, gsizes.loc[key]) - ) - for key in grouped.grouper.result_index - # grouped.grouper.indices[key] gets empty [] - # when it's an empty group - if grouped.grouper.indices[key].size > 0 - ] - ) + indices = _sanitize_rows( + rows, + grouped.grouper.indices, + grouped.grouper.result_index, + ) return _data.take(indices) @@ -345,21 +335,38 @@ def _n_from_prop( return min(n, total) -def _sanitize_rows(rows: Iterable, nrow: int) -> np.ndarray: +def _sanitize_rows( + rows: Iterable, + indices: Union[int, Mapping] = None, + result_index: "Index" = None, +) -> np.ndarray: """Sanitize rows passed to slice""" - rows = Collection(*rows, pool=nrow) - if rows.error: - raise rows.error from None - - # invalid_type_rows = [ - # row - # for row in rows.unmatched - # if not is_integer(row) or pd.isnull(row) - # ] - # if invalid_type_rows: - # raise TypeError( - # "`slice()` expressions should return indices, got " - # f"{type(invalid_type_rows[0])}" - # ) - - return np.array(rows, dtype=int) + from ..base import c + + if is_integer(indices): + rows = Collection(*rows, pool=indices) + if rows.error: + raise rows.error from None + return np.array(rows, dtype=int) + + out = [] + if any(isinstance(row, SeriesGroupBy) for row in rows): + rows = c(*rows) + for key in result_index: + idx = dict_get(indices, key) + if idx.size == 0: + continue + + gidx = dict_get(rows.grouper.indices, key) + out.extend(idx.take(rows.obj.take(gidx))) + else: + for key in result_index: + idx = dict_get(indices, key) + if idx.size == 0: + continue + grows = Collection(*rows, pool=idx.size) + if grows.error: + raise grows.error from None + out.extend(idx.take(grows)) + + return np.array(out, dtype=int) diff --git a/datar/dplyr/group_data.py b/datar/dplyr/group_data.py index 25b8f996..475bf581 100644 --- a/datar/dplyr/group_data.py +++ b/datar/dplyr/group_data.py @@ -7,7 +7,7 @@ from pipda.utils import CallingEnvs from ..core.tibble import Tibble, TibbleGrouped, TibbleRowwise -from ..core.utils import regcall +from ..core.utils import dict_get, regcall @register_verb(DataFrame) @@ -86,7 +86,7 @@ def _(_data: GroupBy) -> List[List[int]]: """Get row indices for each group""" grouper = _data.grouper return [ - list(grouper.indices[group_key]) + list(dict_get(grouper.indices, group_key)) for group_key in grouper.result_index ] diff --git a/datar/forcats/lvl_order.py b/datar/forcats/lvl_order.py index 73c66e94..0733316a 100644 --- a/datar/forcats/lvl_order.py +++ b/datar/forcats/lvl_order.py @@ -4,6 +4,7 @@ import pandas as pd from pandas import Categorical, DataFrame, Series from pandas.api.types import is_scalar +from pandas.core.groupby import SeriesGroupBy from pipda import register_func, register_verb from pipda.utils import CallingEnvs, functype @@ -93,11 +94,19 @@ def fct_inorder(_f, ordered: bool = None) -> Categorical: Returns: The factor with levels reordered """ - _f = check_factor(_f) - dups = regcall(duplicated, _f) - idx = regcall(as_integer, _f)[~dups] - idx = idx[~pd.isnull(_f[~dups])] - return regcall(lvls_reorder, _f, idx, ordered=ordered) + is_sgb = isinstance(_f, SeriesGroupBy) + _f1 = _f.obj if is_sgb else _f + + _f1 = check_factor(_f1) + dups = regcall(duplicated, _f1) + idx = regcall(as_integer, _f1)[~dups] + idx = idx[~pd.isnull(_f1[~dups])] + out = regcall(lvls_reorder, _f1, idx, ordered=ordered) + + if not is_sgb: + return out + + return Series(out, _f.obj.index).groupby(_f.grouper) as_factor = fct_inorder diff --git a/datar/forcats/utils.py b/datar/forcats/utils.py index 86b12b01..e154fdd1 100644 --- a/datar/forcats/utils.py +++ b/datar/forcats/utils.py @@ -2,9 +2,12 @@ import numpy as np from pandas import Categorical, Series, Index from pandas.api.types import is_scalar, is_categorical_dtype +from pandas.core.groupby import SeriesGroupBy + ForcatsRegType = ( Series, + SeriesGroupBy, Categorical, Index, list, diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 21841a39..dc72e971 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,3 +1,18 @@ +## 0.6.3 + +- ✨ Allow `base.c()` to handle groupby data +- πŸš‘ Allow `base.diff()` to work with groupby data +- ✨ Allow `forcats.fct_inorder()` to work with groupby data +- ✨ Allow `base.rep()`'s arguments `length` and `each` to work with grouped data +- ✨ Allow `base.c()` to work with grouped data +- πŸ› Force `&/|` operators to return boolean data +- πŸš‘ Fix `base.diff()` not keep empty groups +- πŸ› Fix recycling non-ordered grouped data +- 🩹 Fix `dplyr.count()/tally()`'s warning about the new name +- πŸš‘ Make `dplyr.n()` return groupoed data +- πŸ› Make `dplyr.slice()` work better with rows/indices from grouped data +- ✨ Add `datar.attrgetter()`, `datar.pd_str()`, `datar.pd_cat()` and `datar.pd_dt()` + ## 0.6.2 - πŸš‘ Fix #87 boolean operator losing index diff --git a/docs/notebooks/datar.ipynb b/docs/notebooks/datar.ipynb index bc6a6a49..df8c1bee 100644 --- a/docs/notebooks/datar.ipynb +++ b/docs/notebooks/datar.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 15, "id": "5ddd5613", "metadata": { "execution": { @@ -124,6 +124,111 @@ }, "metadata": {}, "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "### # attrgetter " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "##### Attrgetter as a function for verb\n", + "\n", + "This is helpful when we want to access to an accessor \n", + "(ie. CategoricalAccessor) from a SeriesGroupBy object \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "### # pd_str " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "##### Pandas' str accessor for a Series (x.str)\n", + "\n", + "This is helpful when x is a SeriesGroupBy object \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "### # pd_cat " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "##### Pandas' cat accessor for a Series (x.cat)\n", + "\n", + "This is helpful when x is a SeriesGroupBy object \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "### # pd_dt " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "##### Pandas' dt accessor for a Series (x.dt)\n", + "\n", + "This is helpful when x is a SeriesGroupBy object \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ @@ -132,12 +237,22 @@ "import numpy\n", "from datar import f\n", "from datar.datasets import iris\n", + "from datar.base import as_date, factor\n", "from datar.datar import *\n", - "from datar.dplyr import mutate\n", + "from datar.dplyr import mutate, group_by\n", "from datar.tibble import tibble\n", "\n", "%run nb_helpers.py\n", - "nb_header(get, flatten, itemgetter, book='datar')" + "nb_header(\n", + " get, \n", + " flatten, \n", + " itemgetter, \n", + " attrgetter, \n", + " pd_str, \n", + " pd_cat, \n", + " pd_dt, \n", + " book='datar',\n", + ")" ] }, { @@ -534,7 +649,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "id": "62ed1ae9", "metadata": { "execution": { @@ -551,7 +666,7 @@ "[1, 3, 2, 4]" ] }, - "execution_count": 9, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -563,7 +678,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 8, "id": "94649970", "metadata": { "execution": { @@ -634,7 +749,7 @@ "1 2 4 c e" ] }, - "execution_count": 10, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -644,6 +759,375 @@ "# df >> mutate(a=arr[f.x], b=arr[f.y]) # Error\n", "df >> mutate(a=itemgetter(arr, f.x), b=itemgetter(arr, f.y))" ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "8056429c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
xa
<object><object>
0abcABC
1defDEF
\n", + "
\n" + ], + "text/plain": [ + " x a\n", + " \n", + "0 abc ABC\n", + "1 def DEF" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = tibble(x=[\"abc\", \"def\"])\n", + "df >> mutate(a=attrgetter(f.x, 'str').upper())" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "9b1726ad", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
xa
<object><object>
0abcABC
1defDEF
\n", + "
\n" + ], + "text/plain": [ + " x a\n", + " \n", + "0 abc ABC\n", + "1 def DEF" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# or\n", + "# df >> mutate(a=pd_str(f.x).upper())\n", + "# or\n", + "df >> mutate(a=f.x.str.upper())" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "05d65cc8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
xga
<object><int64><object>
0abc1ab
1def2de
\n", + "
\n", + "

TibbleGrouped: g (n=2)" + ], + "text/plain": [ + " x g a\n", + " \n", + "0 abc 1 ab\n", + "1 def 2 de\n", + "[TibbleGrouped: g (n=2)]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# but when df is grouped\n", + "gf = df >> group_by(g=[1, 2])\n", + "# pd_str(gf.x)[:2].obj\n", + "gf >> mutate(a=pd_str(gf.x)[:2])" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "081a9d1e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
xgmonth
<datetime64[ns]><int64><int64>
02022-01-0111
12022-12-02212
\n", + "
\n", + "

TibbleGrouped: g (n=2)" + ], + "text/plain": [ + " x g month\n", + " \n", + "0 2022-01-01 1 1\n", + "1 2022-12-02 2 12\n", + "[TibbleGrouped: g (n=2)]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gf = (\n", + " tibble(x=[\"2022-01-01\", \"2022-12-02\"])\n", + " >> mutate(x=as_date(f.x, format=\"%Y-%m-%d\"))\n", + " >> group_by(g=[1, 2])\n", + ")\n", + "gf >> mutate(month=pd_dt(gf.x).month)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "b2aaa7f0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
xgcodes
<category><int64><int8>
0110
1221
\n", + "
\n", + "

TibbleGrouped: g (n=2)" + ], + "text/plain": [ + " x g codes\n", + " \n", + "0 1 1 0\n", + "1 2 2 1\n", + "[TibbleGrouped: g (n=2)]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gf = (\n", + " tibble(x=factor([1, 2], levels=[1, 2, 3]))\n", + " >> group_by(g=[1, 2])\n", + ")\n", + "gf >> mutate(codes=pd_cat(gf.x).codes)" + ] } ], "metadata": { diff --git a/docs/notebooks/readme.ipynb b/docs/notebooks/readme.ipynb index 8a2efa93..ae3c5e2b 100644 --- a/docs/notebooks/readme.ipynb +++ b/docs/notebooks/readme.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "0bf6a031", "metadata": { "execution": { @@ -17,7 +17,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "[2021-07-16 15:28:15][datar][WARNING] Builtin name \"filter\" has been overriden by datar.\n" + "[2022-03-15 16:25:24][datar][WARNING] Builtin name \"filter\" has been overriden by datar.\n" ] } ], @@ -31,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "58de6152", "metadata": { "execution": { @@ -65,7 +65,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "14e1e1e7", "metadata": { "execution": { @@ -80,12 +80,12 @@ "name": "stdout", "output_type": "stream", "text": [ - " x y z\n", - " \n", - "0 0 zero 0\n", - "1 1 one 0\n", - "2 2 two 1\n", - "3 3 three 1\n" + " x y z\n", + " \n", + "0 0 zero 0.0\n", + "1 1 one 0.0\n", + "2 2 two 1.0\n", + "3 3 three 1.0\n" ] } ], @@ -95,7 +95,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "eeedac45", "metadata": { "execution": { @@ -112,8 +112,8 @@ "text": [ " x y\n", " \n", - "0 2 two\n", - "1 3 three\n" + "2 2 two\n", + "3 3 three\n" ] } ], @@ -123,7 +123,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "4dbd113a", "metadata": { "execution": { @@ -138,10 +138,10 @@ "name": "stdout", "output_type": "stream", "text": [ - " x y z\n", - " \n", - "0 2 two 1\n", - "1 3 three 1\n" + " x y z\n", + " \n", + "2 2 two 1.0\n", + "3 3 three 1.0\n" ] } ], @@ -151,7 +151,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "f0577dbd", "metadata": { "execution": { @@ -164,7 +164,7 @@ "outputs": [ { "data": { - "image/png": "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5l13jeqCe4B2S05U79RkixMBkeWE1GSdFPy+aDRNraisPswzAKDtbdiwQWPGjFFOTo5SU1PVq1cv/eUvf5F04OUK7777rs4++2wlJyfrjDPO0NKlS3XWWWdpxowZocd8+7wVK1bo7LPPVlpams455xytXr26I/9oMYPlChHENE0tLt93wdmobBfbhgEHMCrHqS/rPZKk18urNDrHJRvfKwA60Pjx45Wfn69//etfcjqd+vrrr7Vz584DPra4uFijRo1Snz599Pzzz6uqqko//elPVVVVpbPOOqvFY/fs2aMbbrhBv/71r+V0OnXbbbdp0qRJ2rx5sxISWL54NCi5EeSzugbtDF5Ik2G36XxXusWJgMh0Tma6shxlqvT5VdLk09qaevXNTLM6FoA4UVZWpi1btujhhx/W+PHjJUlDhgw56OMffPBBORwOvfbaa8rIaN4S9LjjjtMFF1yw32MrKir01ltv6dRTT5UkpaWlaciQIfrggw90/vnnt8OfJnaxXCGChM/iDs1yKpFtw4ADchhGi11HFrOdGIAOlJOTo+7du+u2227T008/fdAZ3G999NFHGjJkSKjgStL555+v7Ozs/R7buXPnUMGVpFNOOUWSDvs5sD9aVITY623SmprmKzFtkoZnZ1obCIhwF2U7lRBcorC+rkE7PWwnBqBjGIahJUuWqHfv3rr++utVVFSkfv366e233z7g44uLi5WXt/9OMJ06ddrvPpfL1WKcmJgoSfJ4PMcePM5QciPEkvJ924adk5muXLYNAw4p02HXAOe+JT3hx2ADQHs76aST9MILL6iyslIrVqxQUlKSxo8fr9ra2v0eW1hYqNLS0v3uLykp6YiocYuSGwE8/oCWV4ZvG8bhD8CRCP9eeaeyRrVsJwaggyUkJGjQoEH69a9/rerqau3evXu/x/Tv31/Lli1TTU1N6L533nlHFRUVHRk17lByI8Db7hrVB7cN65GcpF5sGwYckeNSkkPfL2wnBqCjfPrppxo+fLieeOIJLV++XK+88oruvvtu9ejRQyeccMJ+j7/55pvl9/s1duxYLViwQLNmzdJVV12l3Nxc2bj+pt3wlbVYwDRbvM06OsfJtmHAUQifzX29vEoB0zzEowHg2BUUFKigoED33XefRo8erWuvvVZFRUVasmSJ7Hb7fo8vLCzUokWLVFNToylTpui+++7T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WbNa/H5hYaEGDRqk5557LnTfc889p0GDBqmwsFDr1q3T5ZdfroceekgVFRX6wx/+oIsvvjiUwWoOqwMAgNWcw0ap6s0lkqSaD99XzmVXyOHKsjgVEB28e4pVv25t88Bul3PocGsDdYCpn7XPOz7PnXbiAe//n//5Hx1//PGSpHvuuUfnnHOOTjzxRI0cOVJjx46VJF155ZV67LHHNG/ePP3oRz9SYmKivv76a5WVlSk3N1fnnntuqzJNmzZNf//733XTTTdJkmbPnq0bbrhBkjRz5kxdc801Ov/85gM/xo0bp7POOksLFy7UlVde2arP15aYyQUQ9xILOyv19DObB36/qpZH/1XFQEf59gdESUrvd64cWdkWpolN3bp1C93u3r27vF6viouL1aNHjxaP69Gjh3bt2iVJevnll7V+/Xr17NlT/fv316uvvtqqz/39739fn3/+ub788ktt2rRJGzdu1Pe//31JzUsf/v73v8vlcoV+rVq1Srt3727dH7SNMZMLAJKcw0epfv06SVL18jeUPe5iGQkJFqcCIlugoV7V76wIjZ1h2/Kh7Wzfvr3F7YSEBBUWFmrt2rUtHrd161YNH948k96nTx+99NJL8vv9mjt3rqZMmaLy8vL9PvbhLhBMT0/XxIkT9eyzz8o0TU2cOFHp6emSmsv3r371K915553H+CdsH5RcAJCUetqZSsgvUNPePfJXV6n2o1XKGHDB4Z8IxLHqd9+W6WmQJCUdd7yST+hpcaKOcbBlBe3lscce07hx45Sbm6vf/e53uuyyyzR16lT96U9/0qJFizR8+HA999xz+uKLLzRx4kR5vV7NnTtX48aNU1ZWllwulwzDkN1u3+9j5+fna/PmzYf8/NOmTdPPf/5zSdLf//730P0/+clPNG7cOA0bNkwDBgxQY2OjVq1apZ49e6pr165t+0VoBZYrAICC24mFHfXrXrpIZvDkJgD7MwMBVb0Rvm3YaLYNaydXXnmlJkyYoK5du8put+vhhx/WSSedpOeff1633nqrcnJy9PDDD+u1115Tbm7zKXNz5szRCSecoIyMDN166616/vnnlZycvN/HvvHGGzV//nxlZWXphz/84QE//4gRI1RbW6va2lqNGLHvqOY+ffro6aef1i233KKcnBx1795dDzzwgAKBQPt8IY6SYfKveMiaNWvUt29frV69Wn36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", + "image/png": "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5l13jeqCe4B2S05U79RkixMBkeWE1GSdFPy+aDRNraisPswzAKDtbdiwQWPGjFFOTo5SU1PVq1cv/eUvf5F04OUK7777rs4++2wlJyfrjDPO0NKlS3XWWWdpxowZocd8+7wVK1bo7LPPVlpams455xytXr26I/9oMYPlChHENE0tLt93wdmobBfbhgEHMCrHqS/rPZKk18urNDrHJRvfKwA60Pjx45Wfn69//etfcjqd+vrrr7Vz584DPra4uFijRo1Snz599Pzzz6uqqko//elPVVVVpbPOOqvFY/fs2aMbbrhBv/71r+V0OnXbbbdp0qRJ2rx5sxISWL54NCi5EeSzugbtDF5Ik2G36XxXusWJgMh0Tma6shxlqvT5VdLk09qaevXNTLM6FoA4UVZWpi1btujhhx/W+PHjJUlDhgw56OMffPBBORwOvfbaa8rIaN4S9LjjjtMFF1yw32MrKir01ltv6dRTT5UkpaWlaciQIfrggw90/vnnt8OfJnaxXCGChM/iDs1yKpFtw4ADchhGi11HFrOdGIAOlJOTo+7du+u2227T008/fdAZ3G999NFHGjJkSKjgStL555+v7Ozs/R7buXPnUMGVpFNOOUWSDvs5sD9aVITY623SmprmKzFtkoZnZ1obCIhwF2U7lRBcorC+rkE7PWwnBqBjGIahJUuWqHfv3rr++utVVFSkfv366e233z7g44uLi5WXt/9OMJ06ddrvPpfL1WKcmJgoSfJ4PMcePM5QciPEkvJ924adk5muXLYNAw4p02HXAOe+JT3hx2ADQHs76aST9MILL6iyslIrVqxQUlKSxo8fr9ra2v0eW1hYqNLS0v3uLykp6YiocYuSGwE8/oCWV4ZvG8bhD8CRCP9eeaeyRrVsJwaggyUkJGjQoEH69a9/rerqau3evXu/x/Tv31/Lli1TTU1N6L533nlHFRUVHRk17lByI8Db7hrVB7cN65GcpF5sGwYckeNSkkPfL2wnBqCjfPrppxo+fLieeOIJLV++XK+88oruvvtu9ejRQyeccMJ+j7/55pvl9/s1duxYLViwQLNmzdJVV12l3Nxc2bj+pt3wlbVYwDRbvM06OsfJtmHAUQifzX29vEoB0zzEowHg2BUUFKigoED33XefRo8erWuvvVZFRUVasmSJ7Hb7fo8vLCzUokWLVFNToylTpui+++7T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WbNa/H5hYaEGDRqk5557LnTfc889p0GDBqmwsFDr1q3T5ZdfroceekgVFRX6wx/+oIsvvjiUwWoOqwMAgNWcw0ap6s0lkqSaD99XzmVXyOHKsjgVEB28e4pVv25t88Bul3PocGsDdYCpn7XPOz7PnXbiAe//n//5Hx1//PGSpHvuuUfnnHOOTjzxRI0cOVJjx46VJF155ZV67LHHNG/ePP3oRz9SYmKivv76a5WVlSk3N1fnnntuqzJNmzZNf//733XTTTdJkmbPnq0bbrhBkjRz5kxdc801Ov/85gM/xo0bp7POOksLFy7UlVde2arP15aYyQUQ9xILOyv19DObB36/qpZH/1XFQEf59gdESUrvd64cWdkWpolN3bp1C93u3r27vF6viouL1aNHjxaP69Gjh3bt2iVJevnll7V+/Xr17NlT/fv316uvvtqqz/39739fn3/+ub788ktt2rRJGzdu1Pe//31JzUsf/v73v8vlcoV+rVq1Srt3727dH7SNMZMLAJKcw0epfv06SVL18jeUPe5iGQkJFqcCIlugoV7V76wIjZ1h2/Kh7Wzfvr3F7YSEBBUWFmrt2rUtHrd161YNH948k96nTx+99NJL8vv9mjt3rqZMmaLy8vL9PvbhLhBMT0/XxIkT9eyzz8o0TU2cOFHp6emSmsv3r371K915553H+CdsH5RcAJCUetqZSsgvUNPePfJXV6n2o1XKGHDB4Z8IxLHqd9+W6WmQJCUdd7yST+hpcaKOcbBlBe3lscce07hx45Sbm6vf/e53uuyyyzR16lT96U9/0qJFizR8+HA999xz+uKLLzRx4kR5vV7NnTtX48aNU1ZWllwulwzDkN1u3+9j5+fna/PmzYf8/NOmTdPPf/5zSdLf//730P0/+clPNG7cOA0bNkwDBgxQY2OjVq1apZ49e6pr165t+0VoBZYrAICC24mFHfXrXrpIZvDkJgD7MwMBVb0Rvm3YaLYNaydXXnmlJkyYoK5du8put+vhhx/WSSedpOeff1633nqrcnJy9PDDD+u1115Tbm7zKXNz5szRCSecoIyMDN166616/vnnlZycvN/HvvHGGzV//nxlZWXphz/84QE//4gRI1RbW6va2lqNGLHvqOY+ffro6aef1i233KKcnBx1795dDzzwgAKBQPt8IY6SYfKveMiaNWvUt29frV69Wn36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", "text/plain": [ "
" ] @@ -175,10 +175,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 1, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -199,7 +199,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "c30cc8e9", "metadata": { "execution": { @@ -216,13 +216,13 @@ "" ] }, - "execution_count": 1, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA3wAAACsCAYAAAAgwq6QAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAABdSUlEQVR4nO3dd1yVZf/A8c91DnsqiAqi4h4oDswtrsyRpbZc5agsS7Pd06NP5WPL1q+yniwbjlylpallQ83MlSMXiltURFRA9jqcc/3+OHgCQQUEDuP7fr3Oi3Nf9/qew7nh/p5rKa01QgghhBBCCCEqH4O9AxBCCCGEEEIIUTok4RNCCCGEEEKISkoSPiGEEEIIIYSopCThE0IIIYQQQohKShI+IYQQQgghhKikHOwdQFENGDBA//zzz/YOQwghhBBCCCHKE1VQYYWr4YuNjbV3CEIIIYQQQghRIVS4hE8IIYQQQgghROFIwieEEEIIIYQQlZQkfEIIIYQQQghRSVW4QVuEEEIIIYQQpc9kMhEVFUVGRoa9QxG5uLi4EBgYiKOjY6G2rxQJX5s2bcjKyiI8PByj0QjA4sWLmTx5Mm+99RYTJkwo85gmTZrExo0b8fX1BWDIkCE8++yz+bZLS0vjqaeeIjw8HK01LVq04MMPP8TT09O2TWxsLN26daNz587Mnz+/zF6DEEIIIYSouqKiovD09CQoKAilChwAUpQxrTVxcXFERUXRoEGDQu1TaZp01q5dmw0bNtiWlyxZQps2bewYETz11FNs2rSJTZs2FZjsAcyfP5+srCy2bNnC1q1bMZvNzJ07N882zz33HLfeemtZhCyEEEIIIQQAGRkZ+Pr6SrJXjiil8PX1LVKta6VJ+EaOHMnixYsBiIyMJC0tjZYtW9rWZ2Vl8fLLL3PrrbfSo0cPJk6cSEpKCgDLly/n1ltvpWfPnvTs2ZM//vjDtl+bNm144403uO2222jTpg2ff/55icatlCI9PR2TyYTJZCItLY2AgADb+mXLllGzZk26detWoucVQgghhBDiRiTZK3+K+jupNAlf9+7dOXToEAkJCSxZsoThw4fnWT9r1iy8vLxYt24df/75J7Vr1+aDDz4AoE+fPvz222/88ccffPHFFzz++ON59k1PT+fXX39l9erVzJgxw5YovvHGG/lq43L75JNP6NatG/fffz9HjhwpcJtx48bh4eFBs2bNaNasGV5eXtxzzz0AnD9/nk8++YSXX365uG+LEEIIIYQQogqrNAkfwNChQ/n+++9ZsWKFLWm64ueff2bZsmWEhYURFhbG2rVrOXXqFACnTp3i7rvvpkuXLjz00ENcvHiRCxcu2Pa96667AKhXrx7e3t5ER0cDMHXqVMaPH19gLNOmTWP37t1s2bKFwYMHc++992I2m/Ntd6U2MSIigoiICEwmEx999BFgbRI6ffp0PDw8bvKdEUIIIYQQ4uacTYXjySX3OJt6g/OdPUvv3r1p2bIlwcHBfPjhhwVut3HjRry9vWnbti1t27ZlxowZAFy6dInu3bvTqlUrVq5cadt+yJAhtvv5otiwYQPt27enVatWjB07luzsbADeeecd27lbtWqF0WgkPj4+3/7jxo2jQYMGtm337t0LwHfffUdwcDA9evQgLi4OgBMnTuSrwCquSjFoyxUjR46kX79+dOnSBR8fnzzrtNa88847hIWF5dtvwoQJvPrqq9x+++1YLBbq1KlDZmambb2zs7PtudFotP1yryd3s8wRI0Ywbdo0oqOjqVu3bp7t5s6dy/Dhw3FxcQGsSes333zDE088wc6dO5kyZQoAqampZGRkcN999/Htt98W4t0QQgghhBCi5GRawK0Es4e0G9xSOzg48N5779G+fXuSk5MJDQ2lX79+ebptXdGjRw/WrFmTp2zJkiVMnDiRu+66i0GDBjF06FBWr15Nu3bt8tyrF4bFYmHs2LGsX7+epk2b8vLLLzN//nweeughnn/+eZ5//nkAVq9ezfvvv58vF7ninXfeyVcx9dFHH7Fz506+//57Fi9ezBNPPMF//vMfXnvttSLFeC2VqoYvKCiIadOm2d7w3AYMGMAnn3xCeno6AMnJybZmlomJidSvXx+AhQsX5kn2iiv3twbr16/HaDTi7++fb7v69euzYcMGtNZYLBbWr19PixYtADh58iT79u1j3759zJgxg759+0qyJ4QQQgghqgR/f3/at28PgKenJy1atODcuXOF3t/R0ZG0tDQyMzNtlTYffPABL7zwQpFjiYuLw8nJiaZNmwLQr18/vvvuu3zbLVmyhJEjRxbp2AaDgczMTNLS0nB0dLR1P2vSpEmR4yzw+CVylHJk3LhxtG7dOl/5U089RatWrejbty/du3dn0KBBHD16FLD2xbv//vvp1asXp0+fvmZGfrXr9eGbNGkS3bp1o0ePHrz33nssWrQIBwfrVyJTpkxh7dq1ALzwwgskJibStWtXunXrRlZWFs8880xxXroQQgghhBCVUmRkJHv27KFTp04Frt+2bRtt2rRh4MCBHDx4EIBRo0bxww8/0K9fP6ZOnconn3zCAw88gJubW5HPX6NGDbKzs9m1axdgHfTx7NmzebZJS0vj559/5u67777mcaZNm0ZISAhPP/20rZLp3//+N7feeiurV69m5MiRvPrqq7z00ktFjvFalNa6xA5WFjp06KCvvNFCCCGEEEKI0hEREWFreQbWfncl3aSzseeNt0tJSaFnz55MmzbNNrZGbklJSRgMBjw8PPjpp5948sknOXbsWJ5tLl++zH333ceKFSt4+umnuXz5Ms8++yxdunQpdLzbtm3jhRdeIDMzk9tuu401a9bY+uEBfPPNNyxcuJDVq1cXuP/58+epXbs2WVlZPPLIIzRq1Cjf4IwLFiwgPj6ezp078+6771K9enU+/PDDfEnq1b+bHAUO31npaviEEEIIIYQQlYPJZOLuu+9m9OjRBSZ7AF5eXrZBDgcNGoTJZCI2NjbPNq+++irTpk1jyZIldO/enfnz5zN9+vQ825jNZtuAKgWNkt+lSxf+/PNPduzYQVhYmK155xVLly69bnNOf39/lFI4Ozszfvx4duzYkWd9Wloa8+bNY9KkSbzyyivMnz+f7t27s2jRomseszAq1aAtQgghhBBCiMpBa81DDz1EixYtrtvlKSYmhlq1aqGUYseOHVgsFnx9fW3rjx07RlRUFL169WLfvn24uLjY5sLOzWg05qmxu9rFixepWbMmmZmZvPXWW0ybNs22LjExkT/++IOFCxdec//z58/j7++P1pqVK1fSqlWrPOvfeecdpkyZgqOjI+np6SilMBgMpKWlXfOYhSEJnxBCCCGEEOKGnA03HlmzqMe7ni1btvD111/TunVr2rZtC1jH0Bg0aBCffvopABMnTmT58uXMnj0bBwcHXF1dWbp0aZ7JyadNm8brr78OWEf1Hzp0KDNnzrRN31BY77zzDmvWrMFisfDYY4/Rp08f27oVK1Zw22234e7unmefQYMG8cUXXxAQEMDo0aO5dOkSWmvatm1rew1gHfBxx44dvPLKKwA88cQT3HLLLVSrVi3PlBLFUap9+JRS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", + "image/png": "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", "text/plain": [ "
" ] @@ -237,11 +237,11 @@ "# very easy to integrate with other libraries\n", "# for example: klib\n", "import klib\n", - "from pipda import register_verb\n", + "from datar.core.factory import verb_factory\n", "from datar.datasets import iris\n", "from datar.dplyr import pull\n", "\n", - "dist_plot = register_verb(func=klib.dist_plot)\n", + "dist_plot = verb_factory(func=klib.dist_plot)\n", "iris >> pull(f.Sepal_Length) >> dist_plot()" ] }, diff --git a/docs/reference-maps/datar.md b/docs/reference-maps/datar.md index b102b1a8..d9d2d9ff 100644 --- a/docs/reference-maps/datar.md +++ b/docs/reference-maps/datar.md @@ -20,10 +20,26 @@ |[**bold**]()|API that is unique in `datar`| |[_italic_]()|Working in process| -### Constants +### Verbs |API|Description|Notebook example| |---|---|---:| -|[**`get()`**][1]|Extract values from data frames|[:material-notebook:][2]| -|[**`flatten()`**][3]|Flatten values of data frames|[:material-notebook:][2]| -|[**`itemgetter()`**][3]|Turn `a[f.x]` to a valid verb argument with `itemgetter(a, f.x)`|[:material-notebook:][2]| +|[**`get()`**][2]|Extract values from data frames|[:material-notebook:][1]| +|[**`flatten()`**][2]|Flatten values of data frames|[:material-notebook:][1]| + +### Functions +|[**`itemgetter()`**][3]|Turn `a[f.x]` to a valid verb argument with `itemgetter(a, f.x)`|[:material-notebook:][1]| +|[**`attrgetter()`**][4]|`f.x.` but works with `SeriesGroupBy` object|[:material-notebook:][1]| +|[**`pd_str()`**][4]|`f.x.str` but works with `SeriesGroupBy` object|[:material-notebook:][1]| +|[**`pd_cat()`**][4]|`f.x.cat` but works with `SeriesGroupBy` object|[:material-notebook:][1]| +|[**`pd_dt()`**][4]|`f.x.dt` but works with `SeriesGroupBy` object|[:material-notebook:][1]| + + +[1]: ../../notebooks/datar +[2]: ../../api/datar.datar.verbs/#datar.datar.verbs.get +[3]: ../../api/datar.datar.verbs/#datar.datar.verbs.flatten +[4]: ../../api/datar.datar.funcs/#datar.datar.funcs.itemgetter +[5]: ../../api/datar.datar.funcs/#datar.datar.funcs.attrgetter +[6]: ../../api/datar.datar.funcs/#datar.datar.funcs.pd_str +[7]: ../../api/datar.datar.funcs/#datar.datar.funcs.pd_cat +[8]: ../../api/datar.datar.funcs/#datar.datar.funcs.pd_dt diff --git a/pyproject.toml b/pyproject.toml index 0a309b9d..f23b9a54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "datar" -version = "0.6.2" +version = "0.6.3" description = "Port of dplyr and other related R packages in python, using pipda." authors = ["pwwang "] readme = "README.md" diff --git a/tests/base/test_funs.py b/tests/base/test_funs.py index 1c229a81..1938903f 100644 --- a/tests/base/test_funs.py +++ b/tests/base/test_funs.py @@ -1,6 +1,6 @@ import pytest # noqa -from pandas import Interval, DataFrame +from pandas import Interval, DataFrame, Series from pandas.testing import assert_frame_equal from datar import f from datar.base.funs import ( @@ -73,6 +73,13 @@ def test_diff(): assert_iterable_equal(diff(diff(x)), diff(x, differences=2)) + assert_iterable_equal(diff(x, differences=40), []) + + x = Series([1, 2, 3, 4, 5]).groupby([1, 2, 2, 3, 3]) + out = diff(x) + assert_iterable_equal(out.obj, [1, 1]) + assert out.grouper.ngroups == 3 + def test_identity(): assert identity(1) == 1 diff --git a/tests/base/test_rep.py b/tests/base/test_rep.py new file mode 100644 index 00000000..0e3f34e2 --- /dev/null +++ b/tests/base/test_rep.py @@ -0,0 +1,80 @@ +import pytest + +from pandas.testing import assert_frame_equal +from datar import f +from datar.base import c, rep +from datar.core.tibble import TibbleGrouped +from datar.tibble import tibble + +from ..conftest import assert_iterable_equal + + +@pytest.mark.parametrize( + "x, times, length, each, expected", + [ + (range(4), 2, None, 1, [0, 1, 2, 3] * 2), + (range(4), 1, None, 2, [0, 0, 1, 1, 2, 2, 3, 3]), + (range(4), [2] * 4, None, 1, [0, 0, 1, 1, 2, 2, 3, 3]), + (range(4), [2, 1] * 2, None, 1, [0, 0, 1, 2, 2, 3]), + (range(4), 1, 4, 2, [0, 0, 1, 1]), + (range(4), 1, 10, 2, [0, 0, 1, 1, 2, 2, 3, 3, 0, 0]), + (range(4), 3, None, 2, [0, 0, 1, 1, 2, 2, 3, 3] * 3), + (1, 7, None, 1, [1, 1, 1, 1, 1, 1, 1]), + ], +) +def test_rep(x, times, length, each, expected): + assert_iterable_equal( + rep(x, times=times, length=length, each=each), expected + ) + + +def test_rep_sgb_param(caplog): + df = tibble( + x=[1, 1, 2, 2], + times=[1, 2, 1, 2], + length=[3, 4, 4, 3], + each=[1, 1, 1, 1], + ).group_by("x") + out = rep([1, 2], df.times) + assert_iterable_equal(out.obj, [1, 2, 2, 1, 2, 2]) + + out = rep([1, 2], times=df.times, each=1, length=df.length) + assert "first element" in caplog.text + + assert_iterable_equal(out.obj, [1, 2, 2, 1, 2, 2, 1]) + assert_iterable_equal(out.grouper.size(), [3, 4]) + + df2 = tibble(x=[1, 2], each=[1, 1]).group_by("x") + out = rep(df2.x, each=df2.each) + assert_iterable_equal(out.obj, [1, 2]) + out = rep(df2.x, times=df2.each, length=df2.each, each=df2.each) + assert_iterable_equal(out.obj, [1, 2]) + out = rep(3, each=df2.each) + assert_iterable_equal(out.obj, [3, 3]) + + out = rep(df2.x.obj, 2) + assert_iterable_equal(out, [1, 2, 1, 2]) + + +def test_rep_df(): + df = tibble(x=f[:3]) + with pytest.raises(ValueError): + rep(df, each=2) + + out = rep(df, times=2, length=5) + assert_frame_equal(out, tibble(x=[0, 1, 2, 0, 1])) + + +def test_rep_grouped_df(): + df = tibble(x=f[:3], g=[1, 1, 2]).group_by("g") + out = rep(df, 2, length=5) + assert isinstance(out, TibbleGrouped) + assert_iterable_equal(out.x.obj, [0, 1, 2, 0, 1]) + assert out._datar["grouped"].grouper.ngroups == 2 + + +def test_rep_error(): + with pytest.raises(ValueError): + rep(c(1, 2, 3), c(1, 2)) + with pytest.raises(ValueError): + rep(c(1, 2, 3), c(1, 2, 3), each=2) diff --git a/tests/base/test_seq.py b/tests/base/test_seq.py index 7a501920..b26863c0 100644 --- a/tests/base/test_seq.py +++ b/tests/base/test_seq.py @@ -8,7 +8,6 @@ seq_along, sample, sort, - rep, rev, length, lengths, @@ -153,32 +152,6 @@ def test_seq_derives(): assert_iterable_equal(seq(to=2), [1, 2]) -@pytest.mark.parametrize( - "x, times, length, each, expected", - [ - (range(4), 2, None, 1, [0, 1, 2, 3] * 2), - (range(4), 1, None, 2, [0, 0, 1, 1, 2, 2, 3, 3]), - (range(4), [2] * 4, None, 1, [0, 0, 1, 1, 2, 2, 3, 3]), - (range(4), [2, 1] * 2, None, 1, [0, 0, 1, 2, 2, 3]), - (range(4), 1, 4, 2, [0, 0, 1, 1]), - (range(4), 1, 10, 2, [0, 0, 1, 1, 2, 2, 3, 3, 0, 0]), - (range(4), 3, None, 2, [0, 0, 1, 1, 2, 2, 3, 3] * 3), - (1, 7, None, 1, [1, 1, 1, 1, 1, 1, 1]), - ], -) -def test_rep(x, times, length, each, expected): - assert_iterable_equal( - rep(x, times=times, length=length, each=each), expected - ) - - -def test_rep_error(): - with pytest.raises(ValueError): - rep(c(1, 2, 3), c(1, 2)) - with pytest.raises(ValueError): - rep(c(1, 2, 3), c(1, 2, 3), each=2) - - def test_sample(): x = sample(range(1, 13)) assert set(x) == set(range(1, 13)) @@ -289,3 +262,13 @@ def test_order(): x = Series([1, 2, 3, 4]).groupby([1, 1, 2, 2]) out = order(x) assert_iterable_equal(out.obj, [0, 1, 0, 1]) + + +def test_c(): + assert_iterable_equal(c(1, 2, 3), [1, 2, 3]) + assert_iterable_equal(c(1, 2, 3, 4), [1, 2, 3, 4]) + assert_iterable_equal(c(1, c(2, 3), 4, 5), [1, 2, 3, 4, 5]) + + x = Series([1, 2, 3, 4]).groupby([1, 1, 2, 2]) + out = c(7, [8, 9], x) + assert_iterable_equal(out.obj, [7, 8, 9, 1, 2, 7, 8, 9, 3, 4]) diff --git a/tests/core/test_broadcast.py b/tests/core/test_broadcast.py index 1cc914c8..9ba783db 100644 --- a/tests/core/test_broadcast.py +++ b/tests/core/test_broadcast.py @@ -320,6 +320,10 @@ def test_broadcast_to_groupby_ndframe(): out = broadcast_to(df, df.index, df._datar["grouped"].grouper) assert_frame_equal(df, out) + nn = df.x.grouper.size().to_frame("size").reset_index().groupby("x") + out = broadcast_to(nn, df.index, df._datar["grouped"].grouper) + assert_iterable_equal(out["size"], [3] * 6) + def test_broadcast2(): # types: scalar/arrays, DattaFrame/Series, GroupBy, TibbleGrouped diff --git a/tests/core/test_operator.py b/tests/core/test_operator.py index c741c0f0..4ebce2c0 100644 --- a/tests/core/test_operator.py +++ b/tests/core/test_operator.py @@ -76,8 +76,11 @@ def test_and_or(): out = df >> select(c(f.x, f.y) & c(f.y, f.z)) assert out.columns.tolist() == ["y"] - out = df >> mutate(a = f.x & f.y) - assert out.a.tolist() == [0] + out = df >> mutate(a=f.x & f.y) + assert out.a.tolist() == [True] + + out = df >> mutate(a=True & f.y) + assert out.a.tolist() == [True] out = df >> select(c(f.x, f.y) | c(f.y, f.z)) assert out.columns.tolist() == ["x", "y", "z"] diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py index 36bb144b..1d533845 100644 --- a/tests/core/test_utils.py +++ b/tests/core/test_utils.py @@ -87,3 +87,13 @@ def test_arg_match(): # df = tibble(x=[]) # out = recycle_value(df, 1) # assert_frame_equal(out, tibble(x=NA)) + + +def test_dict_get(): + d = {'a': 1, 'b': 2, np.nan: 3} + assert dict_get(d, 'a') == 1 + assert dict_get(d, 'b') == 2 + assert dict_get(d, float("nan")) == 3 + assert dict_get(d, 'c', None) is None + with pytest.raises(KeyError): + dict_get(d, 'c') diff --git a/tests/dplyr/test_across.py b/tests/dplyr/test_across.py index bad86837..b22d452c 100644 --- a/tests/dplyr/test_across.py +++ b/tests/dplyr/test_across.py @@ -55,7 +55,7 @@ def test_not_selecting_grouping_var(): df = tibble(g=1, x=1) out = df >> group_by(f.g) >> summarise(x=across(everything())) expected = tibble(x=1) - assert out["x"].equals(expected) + assert_frame_equal(out["x"], expected) def test_names_output(): diff --git a/tests/dplyr/test_empty_groups.py b/tests/dplyr/test_empty_groups.py index 331d8209..1cae796c 100644 --- a/tests/dplyr/test_empty_groups.py +++ b/tests/dplyr/test_empty_groups.py @@ -9,107 +9,108 @@ @pytest.fixture def df(): return tibble( - e = 1, - f = factor(c(1, 1, 2, 2), levels = [1,2,3]), - g = c(1, 1, 2, 2), - x = c(1, 2, 1, 4) - # group_by(..., _drop=False) only works for a - # single categorical columns - ) >> group_by(f.f, _drop = FALSE) + e=1, + f=factor(c(1, 1, 2, 2), levels=[1, 2, 3]), + g=c(1, 1, 2, 2), + x=c(1, 2, 1, 4) + # group_by(..., _drop=False) only works for a + # single categorical columns + ) >> group_by(f.f, _drop=FALSE) + def test_filter_slice_keep_zero_len_groups(df): out = df >> filter(f.f == 1) gsize = group_size(out) - assert gsize == [2,0,0] + assert gsize == [2, 0, 0] out = df >> slice(1) gsize = group_size(out) - assert gsize == [1,1,0] + assert gsize == [1, 1, 0] + def test_filter_slice_retain_zero_group_labels(df): # count loses _drop=False - out = df >> filter(f.f==1) >> count() >> ungroup() - expect = tibble( - f=factor([1,2,3], levels=[1,2,3]), - n=[2,0,0] - ) + out = df >> filter(f.f == 1) >> count() >> ungroup() + expect = tibble(f=factor([1, 2, 3], levels=[1, 2, 3]), n=[2, 0, 0]) assert_frame_equal(out, expect) out = df >> slice(1) >> count() >> ungroup() - expect = tibble( - f=factor([1,2,3], levels=[1,2,3]), - n=[1,1,0] - ) + expect = tibble(f=factor([1, 2, 3], levels=[1, 2, 3]), n=[1, 1, 0]) assert_frame_equal(out, expect) + def test_mutate_keeps_zero_len_groups(df): gsize = group_size(mutate(df, z=2)) - assert gsize == [2,2,0] + assert gsize == [2, 2, 0] + def test_summarise_returns_a_row_for_zero_len_groups(df): summarised = df >> summarise(z=n()) rows = summarised >> nrow() assert rows == 3 + def test_arrange_keeps_zero_len_groups(df): gsize = group_size(arrange(df)) - assert gsize == [2,2,0] + assert gsize == [2, 2, 0] gsize = group_size(df >> arrange(f.x)) - assert gsize == [2,2,0] + assert gsize == [2, 2, 0] + def test_bind_rows(df): gg = df >> bind_rows(df) gsize = group_size(gg) - assert gsize == [4,4,0] + assert gsize == [4, 4, 0] + def test_join_respect_zero_len_groups(): df1 = tibble( - f=factor([1,1,2,2], levels=[1,2,3]), - x=[1,2,1,4] + f=factor([1, 1, 2, 2], levels=[1, 2, 3]), x=[1, 2, 1, 4] ) >> group_by(f.f, _sort=True) df2 = tibble( - f=factor([2,2,3,3], levels=[1,2,3]), - x=[1,2,3,4] + f=factor([2, 2, 3, 3], levels=[1, 2, 3]), x=[1, 2, 3, 4] ) >> group_by(f.f, _sort=True) gsize = group_size(left_join(df1, df2, by=f.f)) - assert gsize == [2,4] + assert gsize == [2, 4] gsize = group_size(right_join(df1, df2, by=f.f)) - assert gsize == [4,2] + assert gsize == [4, 2] gsize = group_size(full_join(df1, df2, by=f.f)) - assert gsize == [2,4,2] + assert gsize == [2, 4, 2] gsize = group_size(anti_join(df1, df2, by=f.f)) assert gsize == [2] gsize = group_size(inner_join(df1, df2, by=f.f)) assert gsize == [4] df1 = tibble( - f=factor([1,1,2,2], levels=[1,2,3]), - x=[1,2,1,4] + f=factor([1, 1, 2, 2], levels=[1, 2, 3]), x=[1, 2, 1, 4] ) >> group_by(f.f, _drop=False) df2 = tibble( - f=factor([2,2,3,3], levels=[1,2,3]), - x=[1,2,3,4] + f=factor([2, 2, 3, 3], levels=[1, 2, 3]), x=[1, 2, 3, 4] ) >> group_by(f.f, _drop=False) gsize = group_size(left_join(df1, df2, by=f.f)) - assert gsize == [2,4,0] + assert gsize == [2, 4, 0] gsize = group_size(right_join(df1, df2, by=f.f)) assert gsize == [4, 2, 0] gsize = group_size(full_join(df1, df2, by=f.f)) - assert gsize == [2,4,2] + assert gsize == [2, 4, 2] gsize = group_size(anti_join(df1, df2, by=f.f)) - assert gsize == [2,0,0] + assert gsize == [2, 0, 0] gsize = group_size(inner_join(df1, df2, by=f.f)) - assert gsize == [4,0,0] + assert gsize == [4, 0, 0] + def test_n_groups_respect_zero_len_groups(): - df = tibble(x=factor([1,2,3], levels=[1,2,3,4])) >> group_by(f.x, _drop=False) + df = tibble(x=factor([1, 2, 3], levels=[1, 2, 3, 4])) >> group_by( + f.x, _drop=False + ) assert n_groups(df) == 4 + def test_summarise_respect_zero_len_groups(): - df = tibble(x=factor(rep([1,2,3], each=10), levels=[1,2,3,4])) + df = tibble(x=factor(rep([1, 2, 3], each=10), levels=[1, 2, 3, 4])) out = df >> group_by(f.x, _drop=False) >> summarise(n=n()) - assert out.n.tolist() == [10,10,10,0] + assert out.n.tolist() == [10, 10, 10, 0] diff --git a/tests/dplyr/test_slice.py b/tests/dplyr/test_slice.py index e1a4277c..e38dcb23 100644 --- a/tests/dplyr/test_slice.py +++ b/tests/dplyr/test_slice.py @@ -1,9 +1,11 @@ # tests grabbed from: # https://github.com/tidyverse/dplyr/blob/master/tests/testthat/test-slice.r +from pandas import Categorical, Series from pandas.testing import assert_frame_equal import pytest from datar import f from datar.datasets import mtcars +from datar.testing import assert_tibble_equal from datar.tibble import tibble, as_tibble from datar.base import nrow, c, NA, rep, seq, dim, names from datar.dplyr import ( @@ -28,6 +30,8 @@ from datar.core.tibble import TibbleRowwise from datar.dplyr.dslice import _n_from_prop +from ..conftest import assert_iterable_equal + def test_empty_slice_returns_input(): df = tibble(x=[1, 2, 3]) @@ -72,7 +76,7 @@ def test_slice_works_with_grouped_data(): res = slice(g, ~f[:2]) exp = filter(g, row_number() >= 3) - assert res.equals(exp) + assert_tibble_equal(res, exp) g = group_by(tibble(x=c(1, 1, 2, 2, 2)), f.x) # out = group_keys(slice(g, 3, _preserve=True)) @@ -80,6 +84,21 @@ def test_slice_works_with_grouped_data(): out = group_keys(slice(g, 2, _preserve=False)) assert out.x.tolist() == [2] + gf = tibble(x=f[1:4]) >> group_by( + g=Categorical([1, 1, 2], categories=[1, 2, 3]), + _drop=False, + ) + with pytest.raises(TypeError): + gf >> slice("a") + with pytest.raises(ValueError): + gf >> slice(~f[:2], 1) + + out = gf >> slice(0) + assert out.shape[0] == 2 + + out = gf >> slice(Series([1, 0, 0]).groupby(gf._datar["grouped"].grouper.result_index)) + assert_iterable_equal(out.x.obj, [2, 3]) + def test_slice_gives_correct_rows(): a = tibble(value=[f"row{i}" for i in range(1, 11)]) @@ -154,13 +173,13 @@ def test_slice_accepts_star_args(): out2 = slice(mtcars, [1, 2]) assert out1.equals(out2) - out3 = slice(mtcars, 1, n()) - out4 = slice(mtcars, c(1, nrow(mtcars))) + out3 = slice(mtcars, 0, n() - 1) + out4 = slice(mtcars, c(0, nrow(mtcars) - 1)) assert out3.equals(out4) g = mtcars >> group_by(f.cyl) - out5 = slice(g, 0, n()) - out6 = slice(g, c(0, n())) + out5 = slice(g, 0, n() - 1) + out6 = slice(g, c(0, n() - 1)) assert out5.equals(out6) @@ -271,10 +290,7 @@ def test_arguments_to_sample_are_passed_along(): out = df >> slice_sample(n=1, weight_by=f.wt) assert out.x.tolist() == [1] - out = ( - df - >> slice_sample(n=2, weight_by=f.wt, replace=True) - ) + out = df >> slice_sample(n=2, weight_by=f.wt, replace=True) assert out.x.tolist() == [1, 1] @@ -423,7 +439,7 @@ def test_slice_head_tail_on_grouped_data(): def test_slice_family_on_rowwise_df(): df = tibble(x=f[1:6]) >> rowwise() - out = df >> slice_head(prop=.1) + out = df >> slice_head(prop=0.1) assert out.shape[0] == 0 out = df >> slice([0, 1, 2]) @@ -457,13 +473,13 @@ def test_preserve_prop_not_support(caplog): assert "_preserve" in caplog.text with pytest.raises(ValueError): - df >> slice_min(f.x, prop=.5) + df >> slice_min(f.x, prop=0.5) with pytest.raises(ValueError): - df >> slice_max(f.x, prop=.5) + df >> slice_max(f.x, prop=0.5) with pytest.raises(ValueError): - df >> slice_sample(f.x, prop=.5) + df >> slice_sample(f.x, prop=0.5) def test_wrong_indices(): diff --git a/tests/forcats/test_forcats_lvl_order.py b/tests/forcats/test_forcats_lvl_order.py index cf9d8711..31cd0322 100644 --- a/tests/forcats/test_forcats_lvl_order.py +++ b/tests/forcats/test_forcats_lvl_order.py @@ -1,3 +1,4 @@ +from pandas import Series import pytest import numpy @@ -15,20 +16,23 @@ def test_warns_about_unknown_levels(caplog): assert_iterable_equal(levels(f1), levels(f2)) + def test_moves_supplied_levels_to_front(): f1 = factor(c("a", "b", "c", "d")) f2 = fct_relevel(f1, "c", "b") assert_iterable_equal(levels(f2), c("c", "b", "a", "d")) + def test_can_moves_supplied_levels_to_end(): f1 = factor(c("a", "b", "c", "d")) - f2 = fct_relevel(f1, "a", "b", after = 1) - f3 = fct_relevel(f1, "a", "b", after = -1) + f2 = fct_relevel(f1, "a", "b", after=1) + f3 = fct_relevel(f1, "a", "b", after=-1) assert_iterable_equal(levels(f2), c("c", "d", "a", "b")) assert_iterable_equal(levels(f3), c("c", "d", "a", "b")) + def test_can_relevel_with_function(): f1 = fct_rev(factor(c("a", "b"))) f2a = fct_relevel(f1, rev) @@ -37,8 +41,10 @@ def test_can_relevel_with_function(): assert_iterable_equal(levels(f2a), c("a", "b")) # assert_iterable_equal(levels(f2b), c("a", "b")) + # fct_reorder, fct_inorder, fct_infreq, fct_inseq + def test_reorder_unmatched_lens(): f1 = factor(c("a", "b", "c", "d")) with pytest.raises(ValueError): @@ -46,35 +52,40 @@ def test_reorder_unmatched_lens(): with pytest.raises(ValueError): fct_reorder2(f1, [1], [2]) + def test_can_reorder_by_2d_summary(): - df = tribble( - f.g, f.x, - "a", 3, - "a", 3, - "b", 2, - "b", 2, - "b", 1 - ) + df = tribble(f.g, f.x, "a", 3, "a", 3, "b", 2, "b", 2, "b", 1) f1 = fct_reorder(df.g, df.x) assert_iterable_equal(levels(f1), c("b", "a")) - f2 = fct_reorder(df.g, df.x, _desc = TRUE) + f2 = fct_reorder(df.g, df.x, _desc=TRUE) assert_iterable_equal(levels(f2), c("a", "b")) + def test_can_reorder_by_2d_summary(): df = tribble( - f.g, f.x, f.y, - "a", 1, 10, - "a", 2.1, 5, # ties order differ - "b", 1, 5, - "b", 2, 10 + f.g, + f.x, + f.y, + "a", + 1, + 10, + "a", + 2.1, + 5, # ties order differ + "b", + 1, + 5, + "b", + 2, + 10, ) f1 = fct_reorder2(df.g, df.x, df.y) assert_iterable_equal(levels(f1), c("b", "a")) - f2 = fct_reorder(df.g, df.x, _desc = TRUE) + f2 = fct_reorder(df.g, df.x, _desc=TRUE) assert_iterable_equal(levels(f2), c("a", "b")) @@ -87,32 +98,38 @@ def test_complains_if_summary_doesnt_return_single_value(): with pytest.raises(ValueError, match="single value per group"): fct_reorder2(["a"], 1, 2, _fun=fun2) + def test_fct_infreq_respects_missing_values(): - f = factor(c("a", "b", "b", NA, NA, NA), exclude = FALSE) + f = factor(c("a", "b", "b", NA, NA, NA), exclude=FALSE) # assert_iterable_equal(levels(fct_infreq(f)), c(NA, "b", "a")) # NA cannot be used as categories for pandas.Categorical assert_iterable_equal(levels(fct_infreq(f)), c("b", "a")) + def test_fct_inseq_sorts_in_numeric_order(): x = c("1", "2", "3") - f1 = fct_inseq(factor(x, levels = c("3", "1", "2"))) - f2 = factor(x, levels = c("1", "2", "3")) + f1 = fct_inseq(factor(x, levels=c("3", "1", "2"))) + f2 = factor(x, levels=c("1", "2", "3")) assert_iterable_equal(f1, f2) assert_iterable_equal(levels(f1), levels(f2)) # non-numeric go to end x = c("1", "2", "3", "a") - f3 = fct_inseq(factor(x, levels = c("a", "3", "1", "2"))) - f4 = factor(x, levels = c("1", "2", "3", "a")) + f3 = fct_inseq(factor(x, levels=c("a", "3", "1", "2"))) + f4 = factor(x, levels=c("1", "2", "3", "a")) assert_iterable_equal(f3, f4) assert_iterable_equal(levels(f3), levels(f4)) def test_fct_inseq_gives_error_for_non_numericlevels(): f = factor(c("c", "a", "a", "b")) - with pytest.raises(ValueError, match="At least one existing level must be coercible to numeric"): + with pytest.raises( + ValueError, + match="At least one existing level must be coercible to numeric", + ): fct_inseq(f) + def test_fct_inorder(): f = factor(c("c", "a", "a", "b"), c("a", "b", "c")) f1 = fct_inorder(f) @@ -120,16 +137,29 @@ def test_fct_inorder(): assert_iterable_equal(f1, f2) assert_iterable_equal(levels(f1), levels(f2)) + s = Series(f) + s1 = fct_inorder(s) + assert_iterable_equal(s1, f2) + assert_iterable_equal(levels(s1), levels(f2)) + + sgb = s.groupby([1, 1, 2, 2]) + s2 = fct_inorder(sgb) + assert_iterable_equal(s2.obj, f2) + assert_iterable_equal(levels(s2.obj), levels(f2)) + + def test_first2(): - out = first2([4,3,1,4], numpy.array([1,2,3,4])) + out = first2([4, 3, 1, 4], numpy.array([1, 2, 3, 4])) assert out == 3 + def test_shuffle(): f = factor(c("c", "a", "a", "b")) f2 = fct_shuffle(f) assert_iterable_equal(f, f2) assert_iterable_equal(sorted(levels(f)), sorted(levels(f2))) + def test_shift(): f = factor(c("c", "a", "a", "b")) f2 = fct_shift(f) diff --git a/tests/test_datar.py b/tests/test_datar.py index 9a47688e..306ca530 100644 --- a/tests/test_datar.py +++ b/tests/test_datar.py @@ -1,9 +1,21 @@ +from pandas import Categorical from datar import f -from datar.datar import get, flatten, itemgetter -from datar.dplyr import mutate +from datar.base.date import as_date +from datar.datar import ( + get, + flatten, + itemgetter, + attrgetter, + pd_str, + pd_cat, + pd_dt, +) +from datar.dplyr import group_by, mutate, summarise from datar.tibble import tibble from pandas.testing import assert_frame_equal +from .conftest import assert_iterable_equal + def test_itemgetter(): arr = [1, 2, 3] @@ -41,3 +53,48 @@ def test_flatten(): assert out == [1, 2, 3, 4] out = df >> flatten() assert out == [1, 3, 2, 4] + + +def test_attrgetter(): + df = tibble(x=list("abc")) + + out = df >> mutate(y=attrgetter(f.x, "str").upper()) + assert_iterable_equal(out.y, ["A", "B", "C"]) + + out = df >> mutate(y=pd_str(f.x).upper()) + assert_iterable_equal(out.y, ["A", "B", "C"]) + + gf = df >> group_by(g=1) + out = gf >> mutate(y=attrgetter(f.x, "str").upper()) + assert_iterable_equal(out.y.obj, ["A", "B", "C"]) + + out = gf >> mutate(y=pd_str(f.x).upper()) + assert_iterable_equal(out.y.obj, ["A", "B", "C"]) + + +def test_pd_str(): + df = tibble(x=["ab", "bc"]) >> group_by(g=[1, 2]) + out = pd_str(df.x)[:1] + + assert_iterable_equal(out.obj, ["a", "b"]) + + +def test_pd_cat(): + df = tibble( + x=Categorical(["a", "b"], categories=["a", "b", "c"]) + ) >> group_by(g=[1, 2]) + out = df >> summarise(lvls=pd_cat(f.x).categories) + + assert_iterable_equal(out.lvls[0], ["a", "b", "c"]) + assert_iterable_equal(out.lvls[1], ["a", "b", "c"]) + + +def test_pd_dt(): + df = ( + tibble(x=["2022-01-01", "2022-12-12"]) + >> mutate(x=as_date(f.x, format="%Y-%m-%d")) + >> group_by(g=[1, 2]) + ) + out = pd_dt(df.x).month + + assert_iterable_equal(out.obj, [1, 12])