diff --git a/pandas/_libs/reduction.pyx b/pandas/_libs/reduction.pyx index fa9c12777eb5b..ab99771385962 100644 --- a/pandas/_libs/reduction.pyx +++ b/pandas/_libs/reduction.pyx @@ -493,8 +493,8 @@ def apply_frame_axis0(object frame, object f, object names, object piece dict item_cache - if frame.index._has_complex_internals: - raise InvalidApply('Cannot modify frame index internals') + # We have already checked that we don't have a MultiIndex before calling + assert frame.index.nlevels == 1 results = [] @@ -625,7 +625,7 @@ def compute_reduction(arr, f, axis=0, dummy=None, labels=None): if labels is not None: # Caller is responsible for ensuring we don't have MultiIndex - assert not labels._has_complex_internals + assert labels.nlevels == 1 # pass as an ndarray/ExtensionArray labels = labels._values diff --git a/pandas/core/groupby/ops.py b/pandas/core/groupby/ops.py index ae397277de41c..aa925c5aea022 100644 --- a/pandas/core/groupby/ops.py +++ b/pandas/core/groupby/ops.py @@ -159,27 +159,26 @@ def apply(self, f, data, axis: int = 0): com.get_callable_name(f) not in base.plotting_methods and hasattr(splitter, "fast_apply") and axis == 0 - # with MultiIndex, apply_frame_axis0 would raise InvalidApply - # TODO: can we make this check prettier? - and not sdata.index._has_complex_internals + # apply_frame_axis0 doesn't allow MultiIndex + and not isinstance(sdata.index, MultiIndex) ): try: result_values, mutated = splitter.fast_apply(f, group_keys) - # If the fast apply path could be used we can return here. - # Otherwise we need to fall back to the slow implementation. - if len(result_values) == len(group_keys): - return group_keys, result_values, mutated - except libreduction.InvalidApply as err: - # Cannot fast apply on MultiIndex (_has_complex_internals). - # This Exception is also raised if `f` triggers an exception + # This Exception is raised if `f` triggers an exception # but it is preferable to raise the exception in Python. if "Let this error raise above us" not in str(err): # TODO: can we infer anything about whether this is # worth-retrying in pure-python? raise + else: + # If the fast apply path could be used we can return here. + # Otherwise we need to fall back to the slow implementation. + if len(result_values) == len(group_keys): + return group_keys, result_values, mutated + for key, (i, group) in zip(group_keys, splitter): object.__setattr__(group, "name", key) @@ -615,7 +614,7 @@ def agg_series(self, obj: Series, func): # TODO: is the datetime64tz case supposed to go through here? return self._aggregate_series_pure_python(obj, func) - elif obj.index._has_complex_internals: + elif isinstance(obj.index, MultiIndex): # MultiIndex; Pre-empt TypeError in _aggregate_series_fast return self._aggregate_series_pure_python(obj, func)