Repository: pandas-dev/pandas

Issue:
Merge Using Index Name Produces Incorrect Result
```
In [1]: import pandas as pd

In [2]: pd.__version__
Out[2]: '0.23.4'

In [3]: df_left_data = [
   ...:     {
   ...:         'id': 11,
   ...:         'left_data': 'left_11'
   ...:     },
   ...:     {
   ...:         'id': 22,
   ...:         'left_data': 'left_22'
   ...:     },
   ...:     {
   ...:         'id': 33,
   ...:         'left_data': 'left_33'
   ...:     }
   ...: ]
   ...: 
   ...: df_left = pd.DataFrame(df_left_data)
   ...: df_left.set_index('id', drop=False, inplace=True)
   ...: df_left
   ...: 
   ...: 
Out[3]: 
    id left_data
id              
11  11   left_11
22  22   left_22
33  33   left_33
In [4]: df_right_data = [
   ...:     {
   ...:         'id': 22,
   ...:         'right_data': 'right_22'
   ...:     },
   ...:     {
   ...:         'id': 33,
   ...:         'right_data': 'right_33'
   ...:     }
   ...: ]
   ...: 
   ...: df_right = pd.DataFrame(df_right_data)
   ...: df_right.set_index('id', drop=True, inplace=True)
   ...: df_right
   ...: 
   ...: 
Out[4]: 
   right_data
id           
22   right_22
33   right_33

In [5]: pd.merge(left=df_left, right=df_right, left_index=True, right_on='id', how='left')
Out[5]: 
    id left_data right_data
id                         
33  11   left_11        NaN
22  22   left_22   right_22
33  33   left_33   right_33
```
#### Problem description
The index of the 1st row should be 11 but it's producing **a wrong value of 33** right now.

The following 2 permutations of parameters produce correct results:
#### Expected Output
```
In [6]: pd.merge(left=df_left, right=df_right, left_index=True, right_index=True, how='left')
Out[6]: 
    id left_data right_data
id                         
11  11   left_11        NaN
22  22   left_22   right_22
33  33   left_33   right_33

In [7]: pd.merge(left=df_left, right=df_right, left_on='id', right_index=True, how='left')
Out[7]: 
    id left_data right_data
id                         
11  11   left_11        NaN
22  22   left_22   right_22
33  33   left_33   right_33
```

#### Output of ``pd.show_versions()``

<details>

[paste the output of ``pd.show_versions()`` here below this line]
INSTALLED VERSIONS
------------------
commit: None
python: 3.6.5.final.0
python-bits: 64
OS: Linux
OS-release: 3.10.0-514.6.1.el7.x86_64
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.23.4
pytest: None
pip: 10.0.1
setuptools: 39.1.0
Cython: None
numpy: 1.15.1
scipy: 1.1.0
pyarrow: None
xarray: None
IPython: 6.5.0
sphinx: None
patsy: None
dateutil: 2.7.3
pytz: 2018.5
blosc: None
bottleneck: None
tables: None
numexpr: None
feather: None
matplotlib: 2.2.3
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: 1.0.1
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None

</details>



Discussion:
Hmm yea that does look strange. Investigation and PRs are always welcome!

Resolving patch:
diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py
--- a/pandas/core/reshape/merge.py
+++ b/pandas/core/reshape/merge.py
@@ -757,13 +757,19 @@ def _get_join_info(self):
 
             if self.right_index:
                 if len(self.left) > 0:
-                    join_index = self.left.index.take(left_indexer)
+                    join_index = self._create_join_index(self.left.index,
+                                                         self.right.index,
+                                                         left_indexer,
+                                                         how='right')
                 else:
                     join_index = self.right.index.take(right_indexer)
                     left_indexer = np.array([-1] * len(join_index))
             elif self.left_index:
                 if len(self.right) > 0:
-                    join_index = self.right.index.take(right_indexer)
+                    join_index = self._create_join_index(self.right.index,
+                                                         self.left.index,
+                                                         right_indexer,
+                                                         how='left')
                 else:
                     join_index = self.left.index.take(left_indexer)
                     right_indexer = np.array([-1] * len(join_index))
@@ -774,6 +780,37 @@ def _get_join_info(self):
             join_index = join_index.astype(object)
         return join_index, left_indexer, right_indexer
 
+    def _create_join_index(self, index, other_index, indexer, how='left'):
+        """
+        Create a join index by rearranging one index to match another
+
+        Parameters
+        ----------
+        index: Index being rearranged
+        other_index: Index used to supply values not found in index
+        indexer: how to rearrange index
+        how: replacement is only necessary if indexer based on other_index
+
+        Returns
+        -------
+        join_index
+        """
+        join_index = index.take(indexer)
+        if (self.how in (how, 'outer') and
+                not isinstance(other_index, MultiIndex)):
+            # if final index requires values in other_index but not target
+            # index, indexer may hold missing (-1) values, causing Index.take
+            # to take the final value in target index
+            mask = indexer == -1
+            if np.any(mask):
+                # if values missing (-1) from target index,
+                # take from other_index instead
+                join_list = join_index.to_numpy()
+                join_list[mask] = other_index.to_numpy()[mask]
+                join_index = Index(join_list, dtype=join_index.dtype,
+                                   name=join_index.name)
+        return join_index
+
     def _get_merge_keys(self):
         """
         Note: has side effects (copy/delete key columns)


Test patch:
diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py
--- a/pandas/tests/reshape/merge/test_merge.py
+++ b/pandas/tests/reshape/merge/test_merge.py
@@ -939,6 +939,26 @@ def test_merge_two_empty_df_no_division_error(self):
         with np.errstate(divide='raise'):
             merge(a, a, on=('a', 'b'))
 
+    @pytest.mark.parametrize('how', ['left', 'outer'])
+    def test_merge_on_index_with_more_values(self, how):
+        # GH 24212
+        # pd.merge gets [-1, -1, 0, 1] as right_indexer, ensure that -1 is
+        # interpreted as a missing value instead of the last element
+        df1 = pd.DataFrame([[1, 2], [2, 4], [3, 6], [4, 8]],
+                           columns=['a', 'b'])
+        df2 = pd.DataFrame([[3, 30], [4, 40]],
+                           columns=['a', 'c'])
+        df1.set_index('a', drop=False, inplace=True)
+        df2.set_index('a', inplace=True)
+        result = pd.merge(df1, df2, left_index=True, right_on='a', how=how)
+        expected = pd.DataFrame([[1, 2, np.nan],
+                                 [2, 4, np.nan],
+                                 [3, 6, 30.0],
+                                 [4, 8, 40.0]],
+                                columns=['a', 'b', 'c'])
+        expected.set_index('a', drop=False, inplace=True)
+        assert_frame_equal(result, expected)
+
 
 def _check_merge(x, y):
     for how in ['inner', 'left', 'outer']: