@@ -421,7 +421,9 @@ def coerce_to_target_dtype(self, other) -> Block:
421
421
return self .astype (new_dtype , copy = False )
422
422
423
423
@final
424
- def _maybe_downcast (self , blocks : list [Block ], downcast = None ) -> list [Block ]:
424
+ def _maybe_downcast (
425
+ self , blocks : list [Block ], downcast = None , using_cow : bool = False
426
+ ) -> list [Block ]:
425
427
if downcast is False :
426
428
return blocks
427
429
@@ -431,23 +433,30 @@ def _maybe_downcast(self, blocks: list[Block], downcast=None) -> list[Block]:
431
433
# but ATM it breaks too much existing code.
432
434
# split and convert the blocks
433
435
434
- return extend_blocks ([blk .convert () for blk in blocks ])
436
+ return extend_blocks (
437
+ [blk .convert (copy = not using_cow , using_cow = using_cow ) for blk in blocks ]
438
+ )
435
439
436
440
if downcast is None :
437
441
return blocks
438
442
439
- return extend_blocks ([b ._downcast_2d (downcast ) for b in blocks ])
443
+ return extend_blocks (
444
+ [b ._downcast_2d (downcast , using_cow = using_cow ) for b in blocks ]
445
+ )
440
446
441
447
@final
442
448
@maybe_split
443
- def _downcast_2d (self , dtype ) -> list [Block ]:
449
+ def _downcast_2d (self , dtype , using_cow : bool = False ) -> list [Block ]:
444
450
"""
445
451
downcast specialized to 2D case post-validation.
446
452
447
453
Refactored to allow use of maybe_split.
448
454
"""
449
455
new_values = maybe_downcast_to_dtype (self .values , dtype = dtype )
450
- return [self .make_block (new_values )]
456
+ refs = None
457
+ if using_cow and new_values is self .values :
458
+ refs = self .refs
459
+ return [self .make_block (new_values , refs = refs )]
451
460
452
461
def convert (
453
462
self ,
@@ -1152,7 +1161,12 @@ def where(self, other, cond, _downcast: str | bool = "infer") -> list[Block]:
1152
1161
return [self .make_block (result )]
1153
1162
1154
1163
def fillna (
1155
- self , value , limit : int | None = None , inplace : bool = False , downcast = None
1164
+ self ,
1165
+ value ,
1166
+ limit : int | None = None ,
1167
+ inplace : bool = False ,
1168
+ downcast = None ,
1169
+ using_cow : bool = False ,
1156
1170
) -> list [Block ]:
1157
1171
"""
1158
1172
fillna on the block with the value. If we fail, then convert to
@@ -1171,19 +1185,25 @@ def fillna(
1171
1185
if noop :
1172
1186
# we can't process the value, but nothing to do
1173
1187
if inplace :
1188
+ if using_cow :
1189
+ return [self .copy (deep = False )]
1174
1190
# Arbitrarily imposing the convention that we ignore downcast
1175
1191
# on no-op when inplace=True
1176
1192
return [self ]
1177
1193
else :
1178
1194
# GH#45423 consistent downcasting on no-ops.
1179
- nb = self .copy ()
1180
- nbs = nb ._maybe_downcast ([nb ], downcast = downcast )
1195
+ nb = self .copy (deep = not using_cow )
1196
+ nbs = nb ._maybe_downcast ([nb ], downcast = downcast , using_cow = using_cow )
1181
1197
return nbs
1182
1198
1183
1199
if limit is not None :
1184
1200
mask [mask .cumsum (self .ndim - 1 ) > limit ] = False
1185
1201
1186
1202
if inplace :
1203
+ if using_cow and self .refs .has_reference ():
1204
+ # TODO(CoW): If using_cow is implemented for putmask we can defer
1205
+ # the copy
1206
+ self = self .copy ()
1187
1207
nbs = self .putmask (mask .T , value )
1188
1208
else :
1189
1209
# without _downcast, we would break
@@ -1194,7 +1214,10 @@ def fillna(
1194
1214
# makes a difference bc blk may have object dtype, which has
1195
1215
# different behavior in _maybe_downcast.
1196
1216
return extend_blocks (
1197
- [blk ._maybe_downcast ([blk ], downcast = downcast ) for blk in nbs ]
1217
+ [
1218
+ blk ._maybe_downcast ([blk ], downcast = downcast , using_cow = using_cow )
1219
+ for blk in nbs
1220
+ ]
1198
1221
)
1199
1222
1200
1223
def interpolate (
@@ -1662,12 +1685,21 @@ class ExtensionBlock(libinternals.Block, EABackedBlock):
1662
1685
values : ExtensionArray
1663
1686
1664
1687
def fillna (
1665
- self , value , limit : int | None = None , inplace : bool = False , downcast = None
1688
+ self ,
1689
+ value ,
1690
+ limit : int | None = None ,
1691
+ inplace : bool = False ,
1692
+ downcast = None ,
1693
+ using_cow : bool = False ,
1666
1694
) -> list [Block ]:
1667
1695
if is_interval_dtype (self .dtype ):
1668
1696
# Block.fillna handles coercion (test_fillna_interval)
1669
1697
return super ().fillna (
1670
- value = value , limit = limit , inplace = inplace , downcast = downcast
1698
+ value = value ,
1699
+ limit = limit ,
1700
+ inplace = inplace ,
1701
+ downcast = downcast ,
1702
+ using_cow = using_cow ,
1671
1703
)
1672
1704
new_values = self .values .fillna (value = value , method = None , limit = limit )
1673
1705
nb = self .make_block_same_class (new_values )
0 commit comments