@@ -1066,22 +1066,12 @@ def iset(
1066
1066
1067
1067
# Note: we exclude DTA/TDA here
1068
1068
value_is_extension_type = is_1d_only_ea_dtype (value .dtype )
1069
-
1070
- # categorical/sparse/datetimetz
1071
- if value_is_extension_type :
1072
-
1073
- def value_getitem (placement ):
1074
- return value
1075
-
1076
- else :
1069
+ if not value_is_extension_type :
1077
1070
if value .ndim == 2 :
1078
1071
value = value .T
1079
1072
else :
1080
1073
value = ensure_block_shape (value , ndim = 2 )
1081
1074
1082
- def value_getitem (placement ):
1083
- return value [placement .indexer ]
1084
-
1085
1075
if value .shape [1 :] != self .shape [1 :]:
1086
1076
raise AssertionError (
1087
1077
"Shape of new values must be compatible with manager shape"
@@ -1092,11 +1082,37 @@ def value_getitem(placement):
1092
1082
# In this case, get_blkno_placements will yield only one tuple,
1093
1083
# containing (self._blknos[loc], BlockPlacement(slice(0, 1, 1)))
1094
1084
1085
+ # Check if we can use _iset_single fastpath
1086
+ blkno = self .blknos [loc ]
1087
+ blk = self .blocks [blkno ]
1088
+ if len (blk ._mgr_locs ) == 1 : # TODO: fastest way to check this?
1089
+ return self ._iset_single (
1090
+ # error: Argument 1 to "_iset_single" of "BlockManager" has
1091
+ # incompatible type "Union[int, slice, ndarray[Any, Any]]";
1092
+ # expected "int"
1093
+ loc , # type:ignore[arg-type]
1094
+ value ,
1095
+ inplace = inplace ,
1096
+ blkno = blkno ,
1097
+ blk = blk ,
1098
+ )
1099
+
1095
1100
# error: Incompatible types in assignment (expression has type
1096
1101
# "List[Union[int, slice, ndarray]]", variable has type "Union[int,
1097
1102
# slice, ndarray]")
1098
1103
loc = [loc ] # type: ignore[assignment]
1099
1104
1105
+ # categorical/sparse/datetimetz
1106
+ if value_is_extension_type :
1107
+
1108
+ def value_getitem (placement ):
1109
+ return value
1110
+
1111
+ else :
1112
+
1113
+ def value_getitem (placement ):
1114
+ return value [placement .indexer ]
1115
+
1100
1116
# Accessing public blknos ensures the public versions are initialized
1101
1117
blknos = self .blknos [loc ]
1102
1118
blklocs = self .blklocs [loc ].copy ()
@@ -1172,6 +1188,29 @@ def value_getitem(placement):
1172
1188
# Newly created block's dtype may already be present.
1173
1189
self ._known_consolidated = False
1174
1190
1191
+ def _iset_single (
1192
+ self , loc : int , value : ArrayLike , inplace : bool , blkno : int , blk : Block
1193
+ ) -> None :
1194
+ """
1195
+ Fastpath for iset when we are only setting a single position and
1196
+ the Block currently in that position is itself single-column.
1197
+
1198
+ In this case we can swap out the entire Block and blklocs and blknos
1199
+ are unaffected.
1200
+ """
1201
+ # Caller is responsible for verifying value.shape
1202
+
1203
+ if inplace and blk .should_store (value ):
1204
+ iloc = self .blklocs [loc ]
1205
+ blk .set_inplace (slice (iloc , iloc + 1 ), value )
1206
+ return
1207
+
1208
+ nb = new_block_2d (value , placement = blk ._mgr_locs )
1209
+ old_blocks = self .blocks
1210
+ new_blocks = old_blocks [:blkno ] + (nb ,) + old_blocks [blkno + 1 :]
1211
+ self .blocks = new_blocks
1212
+ return
1213
+
1175
1214
def insert (self , loc : int , item : Hashable , value : ArrayLike ) -> None :
1176
1215
"""
1177
1216
Insert item at selected position.
@@ -1197,8 +1236,13 @@ def insert(self, loc: int, item: Hashable, value: ArrayLike) -> None:
1197
1236
bp = BlockPlacement (slice (loc , loc + 1 ))
1198
1237
block = new_block_2d (values = value , placement = bp )
1199
1238
1200
- self ._insert_update_mgr_locs (loc )
1201
- self ._insert_update_blklocs_and_blknos (loc )
1239
+ if not len (self .blocks ):
1240
+ # Fastpath
1241
+ self ._blklocs = np .array ([0 ], dtype = np .intp )
1242
+ self ._blknos = np .array ([0 ], dtype = np .intp )
1243
+ else :
1244
+ self ._insert_update_mgr_locs (loc )
1245
+ self ._insert_update_blklocs_and_blknos (loc )
1202
1246
1203
1247
self .axes [0 ] = new_axis
1204
1248
self .blocks += (block ,)
0 commit comments