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