@@ -102,12 +102,41 @@ def _convert_tuple(self, key, is_setter=False):
102
102
keyidx .append (idx )
103
103
return tuple (keyidx )
104
104
105
+ def _has_valid_setitem_indexer (self , indexer ):
106
+ return True
107
+
108
+ def _has_valid_positional_setitem_indexer (self , indexer ):
109
+ """ validate that an positional indexer cannot enlarge its target
110
+ will raise if needed, does not modify the indexer externally """
111
+ if isinstance (indexer , dict ):
112
+ raise IndexError ("{0} cannot enlarge its target object" .format (self .name ))
113
+ else :
114
+ if not isinstance (indexer , tuple ):
115
+ indexer = self ._tuplify (indexer )
116
+ for ax , i in zip (self .obj .axes ,indexer ):
117
+ if isinstance (i , slice ):
118
+ # should check the stop slice?
119
+ pass
120
+ elif is_list_like (i ):
121
+ # should check the elements?
122
+ pass
123
+ elif com .is_integer (i ):
124
+ if i >= len (ax ):
125
+ raise IndexError ("{0} cannot enlarge its target object" .format (self .name ))
126
+ elif isinstance (i , dict ):
127
+ raise IndexError ("{0} cannot enlarge its target object" .format (self .name ))
128
+
129
+ return True
130
+
105
131
def _setitem_with_indexer (self , indexer , value ):
106
132
133
+ self ._has_valid_setitem_indexer (indexer )
134
+
107
135
# also has the side effect of consolidating in-place
108
136
from pandas import Panel , DataFrame , Series
109
137
110
138
# maybe partial set
139
+ take_split_path = self .obj ._is_mixed_type
111
140
if isinstance (indexer ,tuple ):
112
141
nindexer = []
113
142
for i , idx in enumerate (indexer ):
@@ -116,10 +145,26 @@ def _setitem_with_indexer(self, indexer, value):
116
145
# reindex the axis to the new value
117
146
# and set inplace
118
147
key ,_ = _convert_missing_indexer (idx )
119
- labels = self .obj ._get_axis (i ) + Index ([key ])
148
+
149
+ # if this is the items axes, then take the main missing path
150
+ # first; this correctly sets the dtype and avoids cache issues
151
+ # essentially this separates out the block that is needed to possibly
152
+ # be modified
153
+ if self .ndim > 1 and i == self .obj ._info_axis_number :
154
+
155
+ # add the new item, and set the value
156
+ new_indexer = _convert_from_missing_indexer_tuple (indexer )
157
+ self .obj [key ] = np .nan
158
+ self .obj .loc [new_indexer ] = value
159
+ return self .obj
160
+
161
+ # reindex the axis
162
+ index = self .obj ._get_axis (i )
163
+ labels = _safe_append_to_index (index , key )
120
164
self .obj ._data = self .obj .reindex_axis (labels ,i )._data
121
165
122
166
nindexer .append (labels .get_loc (key ))
167
+
123
168
else :
124
169
nindexer .append (idx )
125
170
@@ -133,11 +178,19 @@ def _setitem_with_indexer(self, indexer, value):
133
178
# reindex the axis to the new value
134
179
# and set inplace
135
180
if self .ndim == 1 :
136
- self .obj ._data = self .obj .append (Series (value ,index = [indexer ]))._data
137
- return
181
+ index = self .obj .index
182
+ if len (index ) == 0 :
183
+ new_index = Index ([indexer ])
184
+ else :
185
+ new_index = _safe_append_to_index (index , indexer )
186
+
187
+ new_values = np .concatenate ([self .obj .values , [value ]])
188
+ self .obj ._data = self .obj ._constructor (new_values , index = new_index , name = self .obj .name )
189
+ return self .obj
138
190
139
191
elif self .ndim == 2 :
140
- labels = self .obj ._get_axis (0 ) + Index ([indexer ])
192
+ index = self .obj ._get_axis (0 )
193
+ labels = _safe_append_to_index (index , indexer )
141
194
self .obj ._data = self .obj .reindex_axis (labels ,0 )._data
142
195
return getattr (self .obj ,self .name ).__setitem__ (indexer ,value )
143
196
@@ -146,7 +199,7 @@ def _setitem_with_indexer(self, indexer, value):
146
199
return self .obj .__setitem__ (indexer ,value )
147
200
148
201
# align and set the values
149
- if self . obj . _is_mixed_type :
202
+ if take_split_path :
150
203
if not isinstance (indexer , tuple ):
151
204
indexer = self ._tuplify (indexer )
152
205
@@ -732,6 +785,10 @@ def _convert_to_indexer(self, obj, axis=0, is_setter=False):
732
785
733
786
mask = check == - 1
734
787
if mask .any ():
788
+
789
+ # mi here
790
+ if isinstance (obj , tuple ) and is_setter :
791
+ return { 'key' : obj }
735
792
raise KeyError ('%s not in index' % objarr [mask ])
736
793
737
794
return indexer
@@ -742,7 +799,7 @@ def _convert_to_indexer(self, obj, axis=0, is_setter=False):
742
799
except (KeyError ):
743
800
744
801
# allow a not found key only if we are a setter
745
- if np . isscalar (obj ) and is_setter :
802
+ if not is_list_like (obj ) and is_setter :
746
803
return { 'key' : obj }
747
804
raise
748
805
@@ -933,6 +990,9 @@ def _has_valid_type(self, key, axis):
933
990
934
991
return isinstance (key , slice ) or com .is_integer (key ) or _is_list_like (key )
935
992
993
+ def _has_valid_setitem_indexer (self , indexer ):
994
+ self ._has_valid_positional_setitem_indexer (indexer )
995
+
936
996
def _getitem_tuple (self , tup ):
937
997
938
998
self ._has_valid_tuple (tup )
@@ -965,7 +1025,6 @@ def _get_slice_axis(self, slice_obj, axis=0):
965
1025
return self .obj .take (slice_obj , axis = axis )
966
1026
967
1027
def _getitem_axis (self , key , axis = 0 ):
968
-
969
1028
if isinstance (key , slice ):
970
1029
self ._has_valid_type (key ,axis )
971
1030
return self ._get_slice_axis (key , axis = axis )
@@ -1005,14 +1064,12 @@ def __getitem__(self, key):
1005
1064
else :
1006
1065
raise ValueError ('Invalid call for scalar access (getting)!' )
1007
1066
1008
- if len (key ) != self .obj .ndim :
1009
- raise ValueError ('Not enough indexers for scalar access (getting)!' )
1010
1067
key = self ._convert_key (key )
1011
1068
return self .obj .get_value (* key )
1012
1069
1013
1070
def __setitem__ (self , key , value ):
1014
1071
if not isinstance (key , tuple ):
1015
- raise ValueError ( 'Invalid call for scalar access (setting)!' )
1072
+ key = self . _tuplify ( key )
1016
1073
if len (key ) != self .obj .ndim :
1017
1074
raise ValueError ('Not enough indexers for scalar access (setting)!' )
1018
1075
key = self ._convert_key (key )
@@ -1026,6 +1083,9 @@ class _AtIndexer(_ScalarAccessIndexer):
1026
1083
class _iAtIndexer (_ScalarAccessIndexer ):
1027
1084
""" integer based scalar accessor """
1028
1085
1086
+ def _has_valid_setitem_indexer (self , indexer ):
1087
+ self ._has_valid_positional_setitem_indexer (indexer )
1088
+
1029
1089
def _convert_key (self , key ):
1030
1090
""" require integer args (and convert to label arguments) """
1031
1091
ckey = []
@@ -1179,6 +1239,19 @@ def _convert_missing_indexer(indexer):
1179
1239
1180
1240
return indexer , False
1181
1241
1242
+ def _convert_from_missing_indexer_tuple (indexer ):
1243
+ """ create a filtered indexer that doesn't have any missing indexers """
1244
+ def get_indexer (_idx ):
1245
+ return _idx ['key' ] if isinstance (_idx ,dict ) else _idx
1246
+ return tuple ([ get_indexer (_idx ) for _i , _idx in enumerate (indexer ) ])
1247
+
1248
+ def _safe_append_to_index (index , key ):
1249
+ """ a safe append to an index, if incorrect type, then catch and recreate """
1250
+ try :
1251
+ return index .insert (len (index ), key )
1252
+ except :
1253
+ return Index (np .concatenate ([index .asobject .values ,np .array ([key ])]))
1254
+
1182
1255
def _maybe_convert_indices (indices , n ):
1183
1256
""" if we have negative indicies, translate to postive here
1184
1257
if have indicies that are out-of-bounds, raise an IndexError """
0 commit comments