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