69
69
70
70
class DataManager (PandasObject ):
71
71
72
- pass
72
+ # TODO share more methods/attributes
73
+
74
+ def __len__ (self ) -> int :
75
+ return len (self .items )
76
+
77
+ @property
78
+ def ndim (self ) -> int :
79
+ return len (self .axes )
80
+
81
+ def reindex_axis (
82
+ self ,
83
+ new_index ,
84
+ axis : int ,
85
+ method = None ,
86
+ limit = None ,
87
+ fill_value = None ,
88
+ copy : bool = True ,
89
+ ):
90
+ """
91
+ Conform block manager to new index.
92
+ """
93
+ new_index = ensure_index (new_index )
94
+ new_index , indexer = self .axes [axis ].reindex (
95
+ new_index , method = method , limit = limit
96
+ )
97
+
98
+ return self .reindex_indexer (
99
+ new_index , indexer , axis = axis , fill_value = fill_value , copy = copy
100
+ )
73
101
74
102
75
103
class ArrayManager (DataManager ):
@@ -110,7 +138,7 @@ def shape(self) -> Tuple[int, ...]:
110
138
111
139
@property
112
140
def shape_proper (self ) -> Tuple [int , ...]:
113
- # this still gives the "old" transposed shape
141
+ # this returns (n_rows, n_columns)
114
142
return tuple (len (ax ) for ax in self ._axes )
115
143
116
144
@staticmethod
@@ -119,10 +147,13 @@ def _normalize_axis(axis):
119
147
axis = 1 if axis == 0 else 0
120
148
return axis
121
149
122
- # TODO can be shared
123
- @property
124
- def ndim (self ) -> int :
125
- return len (self .axes )
150
+ def make_empty (self : T , axes = None ) -> T :
151
+ """ return an empty BlockManager with the items axis of len 0 """
152
+ if axes is None :
153
+ axes = [self .axes [1 :], Index ([])]
154
+
155
+ arrays = []
156
+ return type (self )(arrays , axes )
126
157
127
158
def consolidate (self ) -> "ArrayManager" :
128
159
return self
@@ -153,10 +184,6 @@ def get_dtypes(self):
153
184
154
185
# TODO setstate getstate
155
186
156
- # TODO can be shared
157
- def __len__ (self ) -> int :
158
- return len (self .items )
159
-
160
187
def __repr__ (self ) -> str :
161
188
output = type (self ).__name__
162
189
output += f"\n Index: { self ._axes [0 ]} "
@@ -181,6 +208,19 @@ def _verify_integrity(self) -> None:
181
208
# f"tot_items: {tot_items}"
182
209
# )
183
210
211
+ def reduce (self : T , func ) -> T :
212
+ # TODO this still fails because `func` assumes to work on 2D arrays
213
+ assert self .ndim == 2
214
+
215
+ res_arrays = []
216
+ for array in self .arrays :
217
+ res = func (array )
218
+ res_arrays .append (np .array ([res ]))
219
+
220
+ index = Index ([0 ]) # placeholder
221
+ new_mgr = type (self )(res_arrays , [index , self .items ])
222
+ return new_mgr
223
+
184
224
def apply (self : T , f , align_keys = None , ** kwargs ) -> T :
185
225
"""
186
226
Iterate over the blocks, collect and create a new BlockManager.
@@ -202,10 +242,13 @@ def apply(self: T, f, align_keys=None, **kwargs) -> T:
202
242
203
243
aligned_args = {k : kwargs [k ] for k in align_keys }
204
244
245
+ if f == "apply" :
246
+ f = kwargs .pop ("func" )
247
+
205
248
for a in self .arrays :
206
249
207
250
if aligned_args :
208
-
251
+ # TODO
209
252
raise NotImplementedError
210
253
211
254
if callable (f ):
@@ -219,6 +262,9 @@ def apply(self: T, f, align_keys=None, **kwargs) -> T:
219
262
220
263
return type (self )(result_arrays , self ._axes )
221
264
265
+ def isna (self , func ) -> "BlockManager" :
266
+ return self .apply ("apply" , func = func )
267
+
222
268
def where (
223
269
self , other , cond , align : bool , errors : str , try_cast : bool , axis : int
224
270
) -> "ArrayManager" :
@@ -239,6 +285,12 @@ def where(
239
285
axis = axis ,
240
286
)
241
287
288
+ def replace (self , value , ** kwargs ) -> "ArrayManager" :
289
+ assert np .ndim (value ) == 0 , value
290
+ # TODO "replace" is right now implemented on the blocks, we should move
291
+ # it to general array algos so it can be reused here
292
+ return self .apply ("replace" , value = value , ** kwargs )
293
+
242
294
def operate_blockwise (self , other : "ArrayManager" , array_op ) -> "ArrayManager" :
243
295
"""
244
296
Apply array_op blockwise with another (aligned) BlockManager.
@@ -297,6 +349,16 @@ def iget_values(self, i: int) -> ArrayLike:
297
349
"""
298
350
return self .arrays [i ]
299
351
352
+ def idelete (self , indexer ):
353
+ """
354
+ Delete selected locations in-place (new block and array, same BlockManager)
355
+ """
356
+ to_keep = np .ones (self .shape [0 ], dtype = np .bool_ )
357
+ to_keep [indexer ] = False
358
+
359
+ self .arrays = [self .arrays [i ] for i in np .nonzero (to_keep )[0 ]]
360
+ self ._axes = [self ._axes [0 ], self ._axes [1 ][to_keep ]]
361
+
300
362
def take (self , indexer , axis : int = 1 , verify : bool = True , convert : bool = True ):
301
363
"""
302
364
Take items along any axis.
@@ -427,9 +489,15 @@ def iset(self, loc: Union[int, slice, np.ndarray], value):
427
489
contained in the current set of items
428
490
"""
429
491
if lib .is_integer (loc ):
430
- # TODO normalize array
431
- assert isinstance (value , np .ndarray )
432
- value = value [0 , :]
492
+ # TODO normalize array -> this should in theory not be needed
493
+ if isinstance (value , ExtensionArray ):
494
+ import pytest
495
+
496
+ pytest .skip ()
497
+ value = np .asarray (value )
498
+ # assert isinstance(value, np.ndarray)
499
+ if value .ndim == 2 :
500
+ value = value [0 , :]
433
501
assert len (value ) == len (self ._axes [0 ])
434
502
self .arrays [loc ] = value
435
503
return
@@ -462,7 +530,8 @@ def insert(self, loc: int, item: Label, value, allow_duplicates: bool = False):
462
530
463
531
if value .ndim == 2 :
464
532
value = value [0 , :]
465
- assert len (value ) == len (self .arrays [0 ])
533
+ # TODO self.arrays can be empty
534
+ # assert len(value) == len(self.arrays[0])
466
535
467
536
# TODO is this copy needed?
468
537
arrays = self .arrays .copy ()
@@ -471,6 +540,21 @@ def insert(self, loc: int, item: Label, value, allow_duplicates: bool = False):
471
540
self .arrays = arrays
472
541
self ._axes [1 ] = new_axis
473
542
543
+ def fast_xs (self , loc : int ) -> ArrayLike :
544
+ """
545
+ Return the array corresponding to `frame.iloc[loc]`.
546
+
547
+ Parameters
548
+ ----------
549
+ loc : int
550
+
551
+ Returns
552
+ -------
553
+ np.ndarray or ExtensionArray
554
+ """
555
+ dtype = _interleaved_dtype (self .arrays )
556
+ return np .array ([a [loc ] for a in self .arrays ], dtype = dtype )
557
+
474
558
def fillna (self , value , limit , inplace : bool , downcast ) -> "ArrayManager" :
475
559
476
560
inplace = validate_bool_kwarg (inplace , "inplace" )
@@ -495,31 +579,6 @@ def array_fillna(array, value, limit, inplace):
495
579
496
580
return self .apply (array_fillna , value = value , limit = limit , inplace = inplace )
497
581
498
- # if self._can_hold_element(value):
499
- # # equivalent: _try_coerce_args(value) would not raise
500
- # blocks = self.putmask(mask, value, inplace=inplace)
501
- # return self._maybe_downcast(blocks, downcast)
502
-
503
- # # we can't process the value, but nothing to do
504
- # if not mask.any():
505
- # return [self] if inplace else [self.copy()]
506
-
507
- # # operate column-by-column
508
- # def f(mask, val, idx):
509
- # block = self.coerce_to_target_dtype(value)
510
-
511
- # # slice out our block
512
- # if idx is not None:
513
- # # i.e. self.ndim == 2
514
- # block = block.getitem_block(slice(idx, idx + 1))
515
- # return block.fillna(value, limit=limit, inplace=inplace, downcast=None)
516
-
517
- # return self.split_and_operate(None, f, inplace)
518
-
519
- # return self.apply(
520
- # "fillna", value=value, limit=limit, inplace=inplace, downcast=downcast
521
- # )
522
-
523
582
def as_array (
524
583
self ,
525
584
transpose : bool = False ,
@@ -614,6 +673,10 @@ def any_extension_types(self) -> bool:
614
673
"""Whether any of the blocks in this manager are extension blocks"""
615
674
return False # any(block.is_extension for block in self.blocks)
616
675
676
+ # TODO
677
+ # unstack
678
+ # to_dict
679
+
617
680
618
681
class BlockManager (DataManager ):
619
682
"""
0 commit comments