@@ -2483,6 +2483,7 @@ def _get_unique_index(self, dropna=False):
2483
2483
includes list, tuple, array, Series, and must be the same size as
2484
2484
the index and its dtype must exactly match the index's type.
2485
2485
2486
+ .. versionadded:: 0.17.0
2486
2487
.. versionadded:: 0.21.0 (list-like tolerance)
2487
2488
2488
2489
Returns
@@ -2632,6 +2633,7 @@ def _get_level_values(self, level):
2632
2633
the same size as the index and its dtype must exactly match the
2633
2634
index's type.
2634
2635
2636
+ .. versionadded:: 0.17.0
2635
2637
.. versionadded:: 0.21.0 (list-like tolerance)
2636
2638
2637
2639
Examples
@@ -3190,46 +3192,68 @@ def join(self, other, how='left', level=None, return_indexers=False,
3190
3192
3191
3193
def _join_multi (self , other , how , return_indexers = True ):
3192
3194
from .multi import MultiIndex
3193
- self_is_mi = isinstance (self , MultiIndex )
3194
- other_is_mi = isinstance (other , MultiIndex )
3195
+ from pandas .core .reshape .merge import _complete_multilevel_join
3195
3196
3196
3197
# figure out join names
3197
- self_names = _not_none (* self .names )
3198
- other_names = _not_none (* other .names )
3198
+ self_names = list ( _not_none (* self .names ) )
3199
+ other_names = list ( _not_none (* other .names ) )
3199
3200
overlap = list (set (self_names ) & set (other_names ))
3200
3201
3201
- # need at least 1 in common, but not more than 1
3202
+ # need at least 1 in common
3202
3203
if not len (overlap ):
3203
- raise ValueError ("cannot join with no level specified and no "
3204
- "overlapping names" )
3205
- if len (overlap ) > 1 :
3206
- raise NotImplementedError ("merging with more than one level "
3207
- "overlap on a multi-index is not "
3208
- "implemented" )
3209
- jl = overlap [0 ]
3204
+ raise ValueError ("cannot join with no overlapping index names" )
3205
+
3206
+ self_is_mi = isinstance (self , MultiIndex )
3207
+ other_is_mi = isinstance (other , MultiIndex )
3208
+
3209
+ # Drop the non matching levels
3210
+ ldrop_levels = list (set (self_names ) - set (overlap ))
3211
+ rdrop_levels = list (set (other_names ) - set (overlap ))
3212
+
3213
+ if self_is_mi and other_is_mi :
3214
+ self_jnlevels = self .droplevel (ldrop_levels )
3215
+ other_jnlevels = other .droplevel (rdrop_levels )
3216
+
3217
+ if not (self_jnlevels .is_unique and other_jnlevels .is_unique ):
3218
+ raise ValueError ("Join on level between two MultiIndex objects"
3219
+ "is ambiguous" )
3220
+
3221
+ dropped_levels = ldrop_levels + rdrop_levels
3222
+
3223
+ join_idx , lidx , ridx = self_jnlevels .join (other_jnlevels , how ,
3224
+ return_indexers = True )
3225
+
3226
+ levels , labels , names = _complete_multilevel_join (self , other , how ,
3227
+ dropped_levels ,
3228
+ join_idx ,
3229
+ lidx , ridx )
3230
+
3231
+ multi_join_idx = MultiIndex (levels = levels , labels = labels ,
3232
+ names = names , verify_integrity = False )
3233
+
3234
+ # Check for unused levels
3235
+ multi_join_idx = multi_join_idx .remove_unused_levels ()
3236
+
3237
+ return multi_join_idx , lidx , ridx
3238
+
3239
+ jl = list (overlap )[0 ]
3210
3240
3211
3241
# make the indices into mi's that match
3212
- if not (self_is_mi and other_is_mi ):
3213
-
3214
- flip_order = False
3215
- if self_is_mi :
3216
- self , other = other , self
3217
- flip_order = True
3218
- # flip if join method is right or left
3219
- how = {'right' : 'left' , 'left' : 'right' }.get (how , how )
3220
-
3221
- level = other .names .index (jl )
3222
- result = self ._join_level (other , level , how = how ,
3223
- return_indexers = return_indexers )
3224
-
3225
- if flip_order :
3226
- if isinstance (result , tuple ):
3227
- return result [0 ], result [2 ], result [1 ]
3228
- return result
3242
+ flip_order = False
3243
+ if self_is_mi :
3244
+ self , other = other , self
3245
+ flip_order = True
3246
+ # flip if join method is right or left
3247
+ how = {'right' : 'left' , 'left' : 'right' }.get (how , how )
3229
3248
3230
- # 2 multi-indexes
3231
- raise NotImplementedError ("merging with both multi-indexes is not "
3232
- "implemented" )
3249
+ level = other .names .index (jl )
3250
+ result = self ._join_level (other , level , how = how ,
3251
+ return_indexers = return_indexers )
3252
+
3253
+ if flip_order :
3254
+ if isinstance (result , tuple ):
3255
+ return result [0 ], result [2 ], result [1 ]
3256
+ return result
3233
3257
3234
3258
def _join_non_unique (self , other , how = 'left' , return_indexers = False ):
3235
3259
from pandas .core .reshape .merge import _get_join_indexers
@@ -3438,8 +3462,8 @@ def _get_string_slice(self, key, use_lhs=True, use_rhs=True):
3438
3462
3439
3463
def slice_indexer (self , start = None , end = None , step = None , kind = None ):
3440
3464
"""
3441
- For an ordered or unique index , compute the slice indexer for input
3442
- labels and step.
3465
+ For an ordered Index , compute the slice indexer for input labels and
3466
+ step
3443
3467
3444
3468
Parameters
3445
3469
----------
@@ -3452,28 +3476,11 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None):
3452
3476
3453
3477
Returns
3454
3478
-------
3455
- indexer : slice
3456
-
3457
- Raises
3458
- ------
3459
- KeyError : If key does not exist, or key is not unique and index is
3460
- not ordered.
3479
+ indexer : ndarray or slice
3461
3480
3462
3481
Notes
3463
3482
-----
3464
3483
This function assumes that the data is sorted, so use at your own peril
3465
-
3466
- Examples
3467
- ---------
3468
- This is a method on all index types. For example you can do:
3469
-
3470
- >>> idx = pd.Index(list('abcd'))
3471
- >>> idx.slice_indexer(start='b', end='c')
3472
- slice(1, 3)
3473
-
3474
- >>> idx = pd.MultiIndex.from_arrays([list('abcd'), list('efgh')])
3475
- >>> idx.slice_indexer(start='b', end=('c', 'g'))
3476
- slice(1, 3)
3477
3484
"""
3478
3485
start_slice , end_slice = self .slice_locs (start , end , step = step ,
3479
3486
kind = kind )
0 commit comments