@@ -925,33 +925,10 @@ def _multi_take(self, tup):
925
925
""" create the reindex map for our objects, raise the _exception if we
926
926
can't create the indexer
927
927
"""
928
- try :
929
- o = self .obj
930
- d = {}
931
- for key , axis in zip (tup , o ._AXIS_ORDERS ):
932
- ax = o ._get_axis (axis )
933
- # Have the index compute an indexer or return None
934
- # if it cannot handle:
935
- indexer , keyarr = ax ._convert_listlike_indexer (key ,
936
- kind = self .name )
937
- # We only act on all found values:
938
- if indexer is not None and (indexer != - 1 ).all ():
939
- self ._validate_read_indexer (key , indexer , axis )
940
- d [axis ] = (ax [indexer ], indexer )
941
- continue
942
-
943
- # If we are trying to get actual keys from empty Series, we
944
- # patiently wait for a KeyError later on - otherwise, convert
945
- if len (ax ) or not len (key ):
946
- key = self ._convert_for_reindex (key , axis )
947
- indexer = ax .get_indexer_for (key )
948
- keyarr = ax .reindex (keyarr )[0 ]
949
- self ._validate_read_indexer (keyarr , indexer ,
950
- o ._get_axis_number (axis ))
951
- d [axis ] = (keyarr , indexer )
952
- return o ._reindex_with_indexers (d , copy = True , allow_dups = True )
953
- except (KeyError , IndexingError ) as detail :
954
- raise self ._exception (detail )
928
+ o = self .obj
929
+ d = {axis : self ._get_listlike_indexer (key , axis )
930
+ for (key , axis ) in zip (tup , o ._AXIS_ORDERS )}
931
+ return o ._reindex_with_indexers (d , copy = True , allow_dups = True )
955
932
956
933
def _convert_for_reindex (self , key , axis = None ):
957
934
return key
@@ -1124,63 +1101,110 @@ def _getitem_axis(self, key, axis=None):
1124
1101
1125
1102
return self ._get_label (key , axis = axis )
1126
1103
1127
- def _getitem_iterable (self , key , axis = None ):
1128
- if axis is None :
1129
- axis = self . axis or 0
1104
+ def _get_listlike_indexer (self , key , axis , raise_missing = False ):
1105
+ """
1106
+ Transform a list-like of keys into a new index and an indexer.
1130
1107
1131
- self ._validate_key (key , axis )
1108
+ Parameters
1109
+ ----------
1110
+ key : list-like
1111
+ Target labels
1112
+ axis: int
1113
+ Dimension on which the indexing is being made
1114
+ raise_missing: bool
1115
+ Whether to raise a KeyError if some labels are not found. Will be
1116
+ removed in the future, and then this method will always behave as
1117
+ if raise_missing=True.
1132
1118
1133
- labels = self .obj ._get_axis (axis )
1119
+ Raises
1120
+ ------
1121
+ KeyError
1122
+ If at least one key was requested but none was found, and
1123
+ raise_missing=True.
1134
1124
1135
- if com .is_bool_indexer (key ):
1136
- key = check_bool_indexer (labels , key )
1137
- inds , = key .nonzero ()
1138
- return self .obj ._take (inds , axis = axis )
1139
- else :
1125
+ Returns
1126
+ -------
1127
+ keyarr: Index
1128
+ New index (coinciding with 'key' if the axis is unique)
1129
+ values : array-like
1130
+ An indexer for the return object; -1 denotes keys not found
1131
+ """
1132
+ o = self .obj
1133
+ ax = o ._get_axis (axis )
1134
+ try :
1140
1135
# Have the index compute an indexer or return None
1141
- # if it cannot handle; we only act on all found values
1142
- indexer , keyarr = labels ._convert_listlike_indexer (
1143
- key , kind = self .name )
1136
+ # if it cannot handle:
1137
+ indexer , keyarr = ax ._convert_listlike_indexer (key ,
1138
+ kind = self .name )
1139
+ # We only act on all found values:
1144
1140
if indexer is not None and (indexer != - 1 ).all ():
1145
- self ._validate_read_indexer (key , indexer , axis )
1146
- return self .obj .take (indexer , axis = axis )
1141
+ self ._validate_read_indexer (key , indexer , axis ,
1142
+ raise_missing = raise_missing )
1143
+ return ax [indexer ], indexer
1147
1144
1148
- ax = self .obj ._get_axis (axis )
1149
- # existing labels are unique and indexer are unique
1150
- if labels .is_unique and Index (keyarr ).is_unique :
1145
+ if ax .is_unique :
1146
+ # If we are trying to get actual keys from empty Series, we
1147
+ # patiently wait for a KeyError later on - otherwise, convert
1148
+ if len (ax ) or not len (key ):
1149
+ key = self ._convert_for_reindex (key , axis )
1151
1150
indexer = ax .get_indexer_for (key )
1152
- self ._validate_read_indexer (key , indexer , axis )
1151
+ keyarr = ax .reindex (keyarr )[0 ]
1152
+ else :
1153
+ keyarr , indexer , new_indexer = ax ._reindex_non_unique (keyarr )
1153
1154
1154
- d = {axis : [ax .reindex (keyarr )[0 ], indexer ]}
1155
- return self .obj ._reindex_with_indexers (d , copy = True ,
1156
- allow_dups = True )
1155
+ self ._validate_read_indexer (keyarr , indexer ,
1156
+ o ._get_axis_number (axis ),
1157
+ raise_missing = raise_missing )
1158
+ return keyarr , indexer
1159
+ except (KeyError , IndexingError ) as detail :
1160
+ raise self ._exception (detail )
1157
1161
1158
- # existing labels are non-unique
1159
- else :
1162
+ def _getitem_iterable (self , key , axis = None ):
1163
+ """
1164
+ Index current object with an an iterable key (which can be a boolean
1165
+ indexer, or a collection of keys).
1160
1166
1161
- # reindex with the specified axis
1162
- if axis + 1 > self .obj .ndim :
1163
- raise AssertionError ("invalid indexing error with "
1164
- "non-unique index" )
1167
+ Parameters
1168
+ ----------
1169
+ key : iterable
1170
+ Target labels, or boolean indexer
1171
+ axis: int, default None
1172
+ Dimension on which the indexing is being made
1165
1173
1166
- new_target , indexer , new_indexer = labels ._reindex_non_unique (
1167
- keyarr )
1174
+ Raises
1175
+ ------
1176
+ KeyError
1177
+ If no key was found. Will change in the future to raise if not all
1178
+ keys were found.
1179
+ IndexingError
1180
+ If the boolean indexer is unalignable with the object being
1181
+ indexed.
1168
1182
1169
- if new_indexer is not None :
1170
- result = self .obj ._take (indexer [indexer != - 1 ], axis = axis )
1183
+ Returns
1184
+ -------
1185
+ scalar, DataFrame, or Series: indexed value(s),
1186
+ """
1171
1187
1172
- self ._validate_read_indexer (key , new_indexer , axis )
1173
- result = result ._reindex_with_indexers (
1174
- {axis : [new_target , new_indexer ]},
1175
- copy = True , allow_dups = True )
1188
+ if axis is None :
1189
+ axis = self .axis or 0
1176
1190
1177
- else :
1178
- self ._validate_read_indexer (key , indexer , axis )
1179
- result = self .obj ._take (indexer , axis = axis )
1191
+ self ._validate_key (key , axis )
1180
1192
1181
- return result
1193
+ labels = self .obj ._get_axis (axis )
1194
+
1195
+ if com .is_bool_indexer (key ):
1196
+ # A boolean indexer
1197
+ key = check_bool_indexer (labels , key )
1198
+ inds , = key .nonzero ()
1199
+ return self .obj ._take (inds , axis = axis )
1200
+ else :
1201
+ # A collection of keys
1202
+ keyarr , indexer = self ._get_listlike_indexer (key , axis ,
1203
+ raise_missing = False )
1204
+ return self .obj ._reindex_with_indexers ({axis : [keyarr , indexer ]},
1205
+ copy = True , allow_dups = True )
1182
1206
1183
- def _validate_read_indexer (self , key , indexer , axis ):
1207
+ def _validate_read_indexer (self , key , indexer , axis , raise_missing = False ):
1184
1208
"""
1185
1209
Check that indexer can be used to return a result (e.g. at least one
1186
1210
element was found, unless the list of keys was actually empty).
@@ -1193,11 +1217,16 @@ def _validate_read_indexer(self, key, indexer, axis):
1193
1217
Indices corresponding to the key (with -1 indicating not found)
1194
1218
axis: int
1195
1219
Dimension on which the indexing is being made
1220
+ raise_missing: bool
1221
+ Whether to raise a KeyError if some labels are not found. Will be
1222
+ removed in the future, and then this method will always behave as
1223
+ if raise_missing=True.
1196
1224
1197
1225
Raises
1198
1226
------
1199
1227
KeyError
1200
- If at least one key was requested none was found.
1228
+ If at least one key was requested but none was found, and
1229
+ raise_missing=True.
1201
1230
"""
1202
1231
1203
1232
ax = self .obj ._get_axis (axis )
@@ -1214,6 +1243,12 @@ def _validate_read_indexer(self, key, indexer, axis):
1214
1243
u"None of [{key}] are in the [{axis}]" .format (
1215
1244
key = key , axis = self .obj ._get_axis_name (axis )))
1216
1245
1246
+ # We (temporarily) allow for some missing keys with .loc, except in
1247
+ # some cases (e.g. setting) in which, "raise_missing" will be False
1248
+ if not (self .name == 'loc' and not raise_missing ):
1249
+ not_found = list (set (key ) - set (ax ))
1250
+ raise KeyError ("{} not in index" .format (not_found ))
1251
+
1217
1252
# we skip the warning on Categorical/Interval
1218
1253
# as this check is actually done (check for
1219
1254
# non-missing values), but a bit later in the
@@ -1229,9 +1264,10 @@ def _validate_read_indexer(self, key, indexer, axis):
1229
1264
1230
1265
if not (ax .is_categorical () or ax .is_interval ()):
1231
1266
warnings .warn (_missing_key_warning ,
1232
- FutureWarning , stacklevel = 5 )
1267
+ FutureWarning , stacklevel = 6 )
1233
1268
1234
- def _convert_to_indexer (self , obj , axis = None , is_setter = False ):
1269
+ def _convert_to_indexer (self , obj , axis = None , is_setter = False ,
1270
+ raise_missing = False ):
1235
1271
"""
1236
1272
Convert indexing key into something we can use to do actual fancy
1237
1273
indexing on an ndarray
@@ -1310,33 +1346,10 @@ def _convert_to_indexer(self, obj, axis=None, is_setter=False):
1310
1346
inds , = obj .nonzero ()
1311
1347
return inds
1312
1348
else :
1313
-
1314
- # Have the index compute an indexer or return None
1315
- # if it cannot handle
1316
- indexer , objarr = labels ._convert_listlike_indexer (
1317
- obj , kind = self .name )
1318
- if indexer is not None :
1319
- return indexer
1320
-
1321
- # unique index
1322
- if labels .is_unique :
1323
- indexer = check = labels .get_indexer (objarr )
1324
-
1325
- # non-unique (dups)
1326
- else :
1327
- (indexer ,
1328
- missing ) = labels .get_indexer_non_unique (objarr )
1329
- # 'indexer' has dupes, create 'check' using 'missing'
1330
- check = np .zeros (len (objarr ), dtype = np .intp )
1331
- check [missing ] = - 1
1332
-
1333
- mask = check == - 1
1334
- if mask .any ():
1335
- raise KeyError ('{mask} not in index'
1336
- .format (mask = objarr [mask ]))
1337
-
1338
- return com ._values_from_object (indexer )
1339
-
1349
+ # When setting, missing keys are not allowed, even with .loc:
1350
+ kwargs = {'raise_missing' : True if is_setter else
1351
+ raise_missing }
1352
+ return self ._get_listlike_indexer (obj , axis , ** kwargs )[1 ]
1340
1353
else :
1341
1354
try :
1342
1355
return labels .get_loc (obj )
0 commit comments