@@ -956,9 +956,6 @@ def __getattr__(self, attr: str):
956
956
def _op_via_apply (self , name : str , * args , ** kwargs ):
957
957
"""Compute the result of an operation by using GroupBy's apply."""
958
958
f = getattr (type (self ._obj_with_exclusions ), name )
959
- if not callable (f ):
960
- return self .apply (lambda self : getattr (self , name ))
961
-
962
959
sig = inspect .signature (f )
963
960
964
961
# a little trickery for aggregation functions that need an axis
@@ -980,9 +977,6 @@ def curried(x):
980
977
return self .apply (curried )
981
978
982
979
is_transform = name in base .transformation_kernels
983
- # Transform needs to keep the same schema, including when empty
984
- if is_transform and self ._obj_with_exclusions .empty :
985
- return self ._obj_with_exclusions
986
980
result = self ._python_apply_general (
987
981
curried ,
988
982
self ._obj_with_exclusions ,
@@ -1105,6 +1099,7 @@ def _set_result_index_ordered(
1105
1099
1106
1100
return result
1107
1101
1102
+ @final
1108
1103
def _insert_inaxis_grouper (self , result : Series | DataFrame ) -> DataFrame :
1109
1104
if isinstance (result , Series ):
1110
1105
result = result .to_frame ()
@@ -1131,30 +1126,22 @@ def _indexed_output_to_ndframe(
1131
1126
@final
1132
1127
def _wrap_aggregated_output (
1133
1128
self ,
1134
- output : Series | DataFrame | Mapping [ base . OutputKey , ArrayLike ] ,
1129
+ result : Series | DataFrame ,
1135
1130
qs : npt .NDArray [np .float64 ] | None = None ,
1136
1131
):
1137
1132
"""
1138
1133
Wraps the output of GroupBy aggregations into the expected result.
1139
1134
1140
1135
Parameters
1141
1136
----------
1142
- output : Series, DataFrame, or Mapping[base.OutputKey, ArrayLike]
1143
- Data to wrap.
1137
+ result : Series, DataFrame
1144
1138
1145
1139
Returns
1146
1140
-------
1147
1141
Series or DataFrame
1148
1142
"""
1149
-
1150
- if isinstance (output , (Series , DataFrame )):
1151
- # We get here (for DataFrameGroupBy) if we used Manager.grouped_reduce,
1152
- # in which case our columns are already set correctly.
1153
- # ATM we do not get here for SeriesGroupBy; when we do, we will
1154
- # need to require that result.name already match self.obj.name
1155
- result = output
1156
- else :
1157
- result = self ._indexed_output_to_ndframe (output )
1143
+ # ATM we do not get here for SeriesGroupBy; when we do, we will
1144
+ # need to require that result.name already match self.obj.name
1158
1145
1159
1146
if not self .as_index :
1160
1147
# `not self.as_index` is only relevant for DataFrameGroupBy,
@@ -1183,36 +1170,6 @@ def _wrap_aggregated_output(
1183
1170
1184
1171
return self ._reindex_output (result , qs = qs )
1185
1172
1186
- @final
1187
- def _wrap_transformed_output (
1188
- self , output : Mapping [base .OutputKey , ArrayLike ]
1189
- ) -> Series | DataFrame :
1190
- """
1191
- Wraps the output of GroupBy transformations into the expected result.
1192
-
1193
- Parameters
1194
- ----------
1195
- output : Mapping[base.OutputKey, ArrayLike]
1196
- Data to wrap.
1197
-
1198
- Returns
1199
- -------
1200
- Series or DataFrame
1201
- Series for SeriesGroupBy, DataFrame for DataFrameGroupBy
1202
- """
1203
- if isinstance (output , (Series , DataFrame )):
1204
- result = output
1205
- else :
1206
- result = self ._indexed_output_to_ndframe (output )
1207
-
1208
- if self .axis == 1 :
1209
- # Only relevant for DataFrameGroupBy
1210
- result = result .T
1211
- result .columns = self .obj .columns
1212
-
1213
- result .index = self .obj .index
1214
- return result
1215
-
1216
1173
def _wrap_applied_output (
1217
1174
self ,
1218
1175
data ,
@@ -1456,7 +1413,8 @@ def _python_agg_general(self, func, *args, **kwargs):
1456
1413
output : dict [base .OutputKey , ArrayLike ] = {}
1457
1414
1458
1415
if self .ngroups == 0 :
1459
- # agg_series below assumes ngroups > 0
1416
+ # e.g. test_evaluate_with_empty_groups different path gets different
1417
+ # result dtype in empty case.
1460
1418
return self ._python_apply_general (f , self ._selected_obj , is_agg = True )
1461
1419
1462
1420
for idx , obj in enumerate (self ._iterate_slices ()):
@@ -1466,9 +1424,11 @@ def _python_agg_general(self, func, *args, **kwargs):
1466
1424
output [key ] = result
1467
1425
1468
1426
if not output :
1427
+ # e.g. test_groupby_crash_on_nunique, test_margins_no_values_no_cols
1469
1428
return self ._python_apply_general (f , self ._selected_obj )
1470
1429
1471
- return self ._wrap_aggregated_output (output )
1430
+ res = self ._indexed_output_to_ndframe (output )
1431
+ return self ._wrap_aggregated_output (res )
1472
1432
1473
1433
@final
1474
1434
def _agg_general (
@@ -1837,6 +1797,7 @@ def hfunc(bvalues: ArrayLike) -> ArrayLike:
1837
1797
# If we are grouping on categoricals we want unobserved categories to
1838
1798
# return zero, rather than the default of NaN which the reindexing in
1839
1799
# _wrap_agged_manager() returns. GH 35028
1800
+ # e.g. test_dataframe_groupby_on_2_categoricals_when_observed_is_false
1840
1801
with com .temp_setattr (self , "observed" , True ):
1841
1802
result = self ._wrap_agged_manager (new_mgr )
1842
1803
@@ -2555,6 +2516,7 @@ def ohlc(self) -> DataFrame:
2555
2516
)
2556
2517
return self ._reindex_output (result )
2557
2518
2519
+ # TODO: 2023-02-05 all tests that get here have self.as_index
2558
2520
return self ._apply_to_column_groupbys (
2559
2521
lambda x : x .ohlc (), self ._obj_with_exclusions
2560
2522
)
@@ -2832,7 +2794,13 @@ def blk_func(values: ArrayLike) -> ArrayLike:
2832
2794
if isinstance (new_obj , Series ):
2833
2795
new_obj .name = obj .name
2834
2796
2835
- return self ._wrap_transformed_output (new_obj )
2797
+ if self .axis == 1 :
2798
+ # Only relevant for DataFrameGroupBy
2799
+ new_obj = new_obj .T
2800
+ new_obj .columns = self .obj .columns
2801
+
2802
+ new_obj .index = self .obj .index
2803
+ return new_obj
2836
2804
2837
2805
@final
2838
2806
@Substitution (name = "groupby" )
0 commit comments