Repository: pandas-dev/pandas
Pull request: BUG: merge_asof should be treated as a left join
- [x] closes #33463
- [x] tests added / passed
- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry

The fix broke one test (`test_merge_index_column_tz` in `test_merge_asof.py`) which expected the right index instead of the left index.

The default index selection does not work in case of asof and `left_index=True`. I had to catch this case here.

Problem statement:
merge_asof(left_index=True, right_on=...) overwrites left index with right index
Building from an example in the docs...
```
left = pd.DataFrame({'left_val': ['a', 'b', 'c'], 'left_time': [1, 5, 10]}, index=[1, 5, 10])
right = pd.DataFrame({'right_val': [1, 2, 3, 6, 7]}, index=[1, 2, 3, 6, 7])
```

```
>>> pd.merge_asof(left, right, left_index=True, right_index=True)
   left_val  left_time  right_val
1         a          1          1
5         b          5          3
10        c         10          7
```
Returned index is the index of left.

```
>>> pd.merge_asof(left, right, left_on='left_time', right_index=True)
   left_val  left_time  right_val
1         a          1          1
5         b          5          3
10        c         10          7
```
Returned index is the index of left.

```
>>> pd.merge_asof(left, right, left_on='left_time', right_on='right_val')
  left_val  left_time  right_val
0        a          1          1
1        b          5          3
2        c         10          7
```
Return value has index reset (I assume expected?)

```
>>> pd.merge_asof(left, right, left_index=True, right_on='right_val')
  left_val  left_time  right_val
1        a          1          1
3        b          5          3
7        c         10          7
```
Returned index is the index of _right_.

Perhaps I'm misunderstanding something about how this is supposed to work, but it seems to me that the behavior in the last example is incorrect.

Hints from issue discussion:
NB:  proving to myself that the indexes returned in examples 2 and 4 are indeed the _indexes_, not the values:
```
>>> left = pd.DataFrame({'left_val': ['a', 'b', 'c'], 'left_time': [1, 4, 10]}, index=[1, 5, 10])
>>> pd.merge_asof(left, right, left_on='left_time', right_index=True)
   left_val  left_time  right_val
1         a          1          1
5         b          4          3
10        c         10          7
>>> right = pd.DataFrame({'right_val': [1, 2, 3, 6, 8]}, index=[1, 2, 3, 6, 7])
>>> pd.merge_asof(left, right, left_index=True, right_on='right_val')
  left_val  left_time  right_val
1        a          1          1
3        b          4          3
7        c         10          8

```
@gtg944q Thank you for the report!

---

Do you mind paste the URL of the examples you found in the docs? we fixed a lot of documentation related problems recently, maybe this is already solved. 
It's the second block of examples at https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.merge_asof.html, but I'm really just building on an example in there, so it's not a documentation problem.
Since this is advertised as "left-join" like, the last 2 examples you provide are surprising, and I'd expect to propagate the values in the left index (and not reset the index or use the right index). I'll mark as a bug, but this _may_ break some existing tests.

Commit message: Add whatsnew entry

Patch to review:
diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst
index 17a830788be3f..d8df0d8ef347d 100644
--- a/doc/source/whatsnew/v1.1.0.rst
+++ b/doc/source/whatsnew/v1.1.0.rst
@@ -936,6 +936,7 @@ Reshaping
 - Bug in :meth:`DataFrame.replace` casts columns to ``object`` dtype if items in ``to_replace`` not in values (:issue:`32988`)
 - Ensure only named functions can be used in :func:`eval()` (:issue:`32460`)
 - Fixed bug in :func:`melt` where melting MultiIndex columns with ``col_level`` > 0 would raise a ``KeyError`` on ``id_vars`` (:issue:`34129`)
+- Bug in :func:`merge_asof` propagated the right Index with ``left_index=True`` and `right_on`` specification instead of left Index (:issue:`33463`)
 
 Sparse
 ^^^^^^
diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py
index 0c796c8f45a52..5ebe9a26319f9 100644
--- a/pandas/core/reshape/merge.py
+++ b/pandas/core/reshape/merge.py
@@ -938,6 +938,8 @@ def _create_join_index(
         -------
         join_index
         """
+        if how == "left" and self.how == "asof":
+            return other_index.take(other_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
diff --git a/pandas/tests/reshape/merge/test_merge_asof.py b/pandas/tests/reshape/merge/test_merge_asof.py
index 9b09f0033715d..db8d8e2fe2f5d 100644
--- a/pandas/tests/reshape/merge/test_merge_asof.py
+++ b/pandas/tests/reshape/merge/test_merge_asof.py
@@ -5,7 +5,7 @@
 import pytz
 
 import pandas as pd
-from pandas import Timedelta, merge_asof, read_csv, to_datetime
+from pandas import Index, Timedelta, merge_asof, read_csv, to_datetime
 import pandas._testing as tm
 from pandas.core.reshape.merge import MergeError
 
@@ -1323,7 +1323,7 @@ def test_merge_index_column_tz(self):
                 "from_date": index[1:],
                 "abc": [2.46] * 3 + [2.19],
             },
-            index=pd.Index([1, 2, 3, 4]),
+            index=index[1:],
         )
         tm.assert_frame_equal(result, expected)
 
@@ -1339,3 +1339,25 @@ def test_merge_index_column_tz(self):
             index=pd.Index([0, 1, 2, 3, 4]),
         )
         tm.assert_frame_equal(result, expected)
+
+
+@pytest.mark.parametrize(
+    "kwargs",
+    [
+        {"right_index": True, "left_index": True},
+        {"left_on": "left_time", "right_index": True},
+        {"left_index": True, "right_on": "right"},
+    ],
+)
+def test_merge_asof_index_behavior(kwargs):
+    # GH 33463
+    index = Index([1, 5, 10], name="test")
+    left = pd.DataFrame({"left": ["a", "b", "c"], "left_time": [1, 4, 10]}, index=index)
+    right = pd.DataFrame({"right": [1, 2, 3, 6, 7]}, index=[1, 2, 3, 6, 7])
+    result = merge_asof(left, right, **kwargs)
+
+    expected = pd.DataFrame(
+        {"left": ["a", "b", "c"], "left_time": [1, 4, 10], "right": [1, 3, 7]},
+        index=index,
+    )
+    tm.assert_frame_equal(result, expected)


Code review comments:
diff_hunk: @@ -938,6 +938,8 @@ def _create_join_index(
         -------
         join_index
         """
+        if how == "left" and self.how == "asof":
path: pandas/core/reshape/merge.py
text: @user1:
This seems very specific. Is this the deepest level at which this should be handled? 

@author:
I think so, yes. The error actually occurs there. Everything before is handled as expected in ``_get_join_info``. The only issue is the call of  ``_create_join_index`` with the wrong parametrization.

We could implement the function  ``_get_join_info`` for merge_asof too, but the function does more than just this part so we would duplicate a lot probably? 

@user2:
hmm, it seems that myabe asof should be in hit in _get_join_info (there is a case that is excluding). I agree this is not a great place to put this. rather it would pay to trace the asof paths and see if there is a more general problem. the conditions in _get_join_info are slightly convoluted .