@@ -1174,7 +1174,10 @@ def _wrap_transformed_output(
1174
1174
Series or DataFrame
1175
1175
Series for SeriesGroupBy, DataFrame for DataFrameGroupBy
1176
1176
"""
1177
- result = self ._indexed_output_to_ndframe (output )
1177
+ if isinstance (output , (Series , DataFrame )):
1178
+ result = output
1179
+ else :
1180
+ result = self ._indexed_output_to_ndframe (output )
1178
1181
1179
1182
if self .axis == 1 :
1180
1183
# Only relevant for DataFrameGroupBy
@@ -2254,17 +2257,55 @@ def _fill(self, direction: Literal["ffill", "bfill"], limit=None):
2254
2257
if limit is None :
2255
2258
limit = - 1
2256
2259
2257
- return self ._get_cythonized_result (
2260
+ ids , _ , _ = self .grouper .group_info
2261
+
2262
+ col_func = partial (
2258
2263
libgroupby .group_fillna_indexer ,
2259
- numeric_only = False ,
2260
- needs_mask = True ,
2261
- cython_dtype = np .dtype (np .int64 ),
2262
- result_is_index = True ,
2264
+ labels = ids ,
2263
2265
direction = direction ,
2264
2266
limit = limit ,
2265
2267
dropna = self .dropna ,
2266
2268
)
2267
2269
2270
+ def blk_func (values : ArrayLike ) -> ArrayLike :
2271
+ mask = isna (values )
2272
+ if values .ndim == 1 :
2273
+ indexer = np .empty (values .shape , dtype = np .intp )
2274
+ col_func (out = indexer , mask = mask )
2275
+ return algorithms .take_nd (values , indexer )
2276
+
2277
+ else :
2278
+ # We broadcast algorithms.take_nd analogous to
2279
+ # np.take_along_axis
2280
+
2281
+ # Note: we only get here with backfill/pad,
2282
+ # so if we have a dtype that cannot hold NAs,
2283
+ # then there will be no -1s in indexer, so we can use
2284
+ # the original dtype (no need to ensure_dtype_can_hold_na)
2285
+ if isinstance (values , np .ndarray ):
2286
+ out = np .empty (values .shape , dtype = values .dtype )
2287
+ else :
2288
+ out = type (values )._empty (values .shape , dtype = values .dtype )
2289
+
2290
+ for i in range (len (values )):
2291
+ # call group_fillna_indexer column-wise
2292
+ indexer = np .empty (values .shape [1 ], dtype = np .intp )
2293
+ col_func (out = indexer , mask = mask [i ])
2294
+ out [i , :] = algorithms .take_nd (values [i ], indexer )
2295
+ return out
2296
+
2297
+ obj = self ._obj_with_exclusions
2298
+ if self .axis == 1 :
2299
+ obj = obj .T
2300
+ mgr = obj ._mgr
2301
+ res_mgr = mgr .apply (blk_func )
2302
+
2303
+ new_obj = obj ._constructor (res_mgr )
2304
+ if isinstance (new_obj , Series ):
2305
+ new_obj .name = obj .name
2306
+
2307
+ return self ._wrap_transformed_output (new_obj )
2308
+
2268
2309
@final
2269
2310
@Substitution (name = "groupby" )
2270
2311
def pad (self , limit = None ):
@@ -2944,7 +2985,6 @@ def _get_cythonized_result(
2944
2985
min_count : int | None = None ,
2945
2986
needs_mask : bool = False ,
2946
2987
needs_ngroups : bool = False ,
2947
- result_is_index : bool = False ,
2948
2988
pre_processing = None ,
2949
2989
post_processing = None ,
2950
2990
fill_value = None ,
@@ -2981,9 +3021,6 @@ def _get_cythonized_result(
2981
3021
needs_nullable : bool, default False
2982
3022
Whether a bool specifying if the input is nullable is part
2983
3023
of the Cython call signature
2984
- result_is_index : bool, default False
2985
- Whether the result of the Cython operation is an index of
2986
- values to be retrieved, instead of the actual values themselves
2987
3024
pre_processing : function, default None
2988
3025
Function to be applied to `values` prior to passing to Cython.
2989
3026
Function should return a tuple where the first element is the
@@ -3009,8 +3046,6 @@ def _get_cythonized_result(
3009
3046
"""
3010
3047
numeric_only = self ._resolve_numeric_only (numeric_only )
3011
3048
3012
- if result_is_index and aggregate :
3013
- raise ValueError ("'result_is_index' and 'aggregate' cannot both be True!" )
3014
3049
if post_processing and not callable (post_processing ):
3015
3050
raise ValueError ("'post_processing' must be a callable!" )
3016
3051
if pre_processing :
@@ -3082,14 +3117,9 @@ def blk_func(values: ArrayLike) -> ArrayLike:
3082
3117
3083
3118
func (** kwargs ) # Call func to modify indexer values in place
3084
3119
3085
- if result_is_index :
3086
- result = algorithms .take_nd (values , result , fill_value = fill_value )
3087
-
3088
3120
if real_2d and values .ndim == 1 :
3089
3121
assert result .shape [1 ] == 1 , result .shape
3090
- # error: No overload variant of "__getitem__" of "ExtensionArray"
3091
- # matches argument type "Tuple[slice, int]"
3092
- result = result [:, 0 ] # type: ignore[call-overload]
3122
+ result = result [:, 0 ]
3093
3123
if needs_mask :
3094
3124
mask = mask [:, 0 ]
3095
3125
0 commit comments