@@ -189,6 +189,18 @@ def _make_selectors(self):
189
189
self .unique_groups = obs_ids
190
190
self .compressor = comp_index .searchsorted (np .arange (ngroups ))
191
191
192
+ @cache_readonly
193
+ def mask_all (self ) -> bool :
194
+ return bool (self .mask .all ())
195
+
196
+ @cache_readonly
197
+ def arange_result (self ) -> tuple [npt .NDArray [np .intp ], npt .NDArray [np .bool_ ]]:
198
+ # We cache this for re-use in ExtensionBlock._unstack
199
+ dummy_arr = np .arange (len (self .index ), dtype = np .intp )
200
+ new_values , mask = self .get_new_values (dummy_arr , fill_value = - 1 )
201
+ return new_values , mask .any (0 )
202
+ # TODO: in all tests we have mask.any(0).all(); can we rely on that?
203
+
192
204
def get_result (self , values , value_columns , fill_value ):
193
205
194
206
if values .ndim == 1 :
@@ -216,7 +228,7 @@ def get_new_values(self, values, fill_value=None):
216
228
result_width = width * stride
217
229
result_shape = (length , result_width )
218
230
mask = self .mask
219
- mask_all = mask . all ()
231
+ mask_all = self . mask_all
220
232
221
233
# we can simply reshape if we don't have a mask
222
234
if mask_all and len (values ):
@@ -510,7 +522,11 @@ def _unstack_extension_series(series, level, fill_value):
510
522
# Defer to the logic in ExtensionBlock._unstack
511
523
df = series .to_frame ()
512
524
result = df .unstack (level = level , fill_value = fill_value )
513
- return result .droplevel (level = 0 , axis = 1 )
525
+
526
+ # equiv: result.droplevel(level=0, axis=1)
527
+ # but this avoids an extra copy
528
+ result .columns = result .columns .droplevel (0 )
529
+ return result
514
530
515
531
516
532
def stack (frame , level = - 1 , dropna = True ):
0 commit comments