@@ -102,12 +102,41 @@ def _convert_tuple(self, key, is_setter=False):
102102 keyidx .append (idx )
103103 return tuple (keyidx )
104104
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+
105131 def _setitem_with_indexer (self , indexer , value ):
106132
133+ self ._has_valid_setitem_indexer (indexer )
134+
107135 # also has the side effect of consolidating in-place
108136 from pandas import Panel , DataFrame , Series
109137
110138 # maybe partial set
139+ take_split_path = self .obj ._is_mixed_type
111140 if isinstance (indexer ,tuple ):
112141 nindexer = []
113142 for i , idx in enumerate (indexer ):
@@ -116,10 +145,26 @@ def _setitem_with_indexer(self, indexer, value):
116145 # reindex the axis to the new value
117146 # and set inplace
118147 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 )
120164 self .obj ._data = self .obj .reindex_axis (labels ,i )._data
121165
122166 nindexer .append (labels .get_loc (key ))
167+
123168 else :
124169 nindexer .append (idx )
125170
@@ -133,11 +178,19 @@ def _setitem_with_indexer(self, indexer, value):
133178 # reindex the axis to the new value
134179 # and set inplace
135180 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
138190
139191 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 )
141194 self .obj ._data = self .obj .reindex_axis (labels ,0 )._data
142195 return getattr (self .obj ,self .name ).__setitem__ (indexer ,value )
143196
@@ -146,7 +199,7 @@ def _setitem_with_indexer(self, indexer, value):
146199 return self .obj .__setitem__ (indexer ,value )
147200
148201 # align and set the values
149- if self . obj . _is_mixed_type :
202+ if take_split_path :
150203 if not isinstance (indexer , tuple ):
151204 indexer = self ._tuplify (indexer )
152205
@@ -732,6 +785,10 @@ def _convert_to_indexer(self, obj, axis=0, is_setter=False):
732785
733786 mask = check == - 1
734787 if mask .any ():
788+
789+ # mi here
790+ if isinstance (obj , tuple ) and is_setter :
791+ return { 'key' : obj }
735792 raise KeyError ('%s not in index' % objarr [mask ])
736793
737794 return indexer
@@ -742,7 +799,7 @@ def _convert_to_indexer(self, obj, axis=0, is_setter=False):
742799 except (KeyError ):
743800
744801 # 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 :
746803 return { 'key' : obj }
747804 raise
748805
@@ -933,6 +990,9 @@ def _has_valid_type(self, key, axis):
933990
934991 return isinstance (key , slice ) or com .is_integer (key ) or _is_list_like (key )
935992
993+ def _has_valid_setitem_indexer (self , indexer ):
994+ self ._has_valid_positional_setitem_indexer (indexer )
995+
936996 def _getitem_tuple (self , tup ):
937997
938998 self ._has_valid_tuple (tup )
@@ -965,7 +1025,6 @@ def _get_slice_axis(self, slice_obj, axis=0):
9651025 return self .obj .take (slice_obj , axis = axis )
9661026
9671027 def _getitem_axis (self , key , axis = 0 ):
968-
9691028 if isinstance (key , slice ):
9701029 self ._has_valid_type (key ,axis )
9711030 return self ._get_slice_axis (key , axis = axis )
@@ -1005,14 +1064,12 @@ def __getitem__(self, key):
10051064 else :
10061065 raise ValueError ('Invalid call for scalar access (getting)!' )
10071066
1008- if len (key ) != self .obj .ndim :
1009- raise ValueError ('Not enough indexers for scalar access (getting)!' )
10101067 key = self ._convert_key (key )
10111068 return self .obj .get_value (* key )
10121069
10131070 def __setitem__ (self , key , value ):
10141071 if not isinstance (key , tuple ):
1015- raise ValueError ( 'Invalid call for scalar access (setting)!' )
1072+ key = self . _tuplify ( key )
10161073 if len (key ) != self .obj .ndim :
10171074 raise ValueError ('Not enough indexers for scalar access (setting)!' )
10181075 key = self ._convert_key (key )
@@ -1026,6 +1083,9 @@ class _AtIndexer(_ScalarAccessIndexer):
10261083class _iAtIndexer (_ScalarAccessIndexer ):
10271084 """ integer based scalar accessor """
10281085
1086+ def _has_valid_setitem_indexer (self , indexer ):
1087+ self ._has_valid_positional_setitem_indexer (indexer )
1088+
10291089 def _convert_key (self , key ):
10301090 """ require integer args (and convert to label arguments) """
10311091 ckey = []
@@ -1179,6 +1239,19 @@ def _convert_missing_indexer(indexer):
11791239
11801240 return indexer , False
11811241
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+
11821255def _maybe_convert_indices (indices , n ):
11831256 """ if we have negative indicies, translate to postive here
11841257 if have indicies that are out-of-bounds, raise an IndexError """
0 commit comments