Repository: pandas-dev/pandas
Pull request: BUG in reindex raises IndexingError when df is empty sometimes
- [x] closes #27315
- [x] tests added / passed
- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry

Build on top of #27326

Problem statement:
some argument combinations with reindex fails on an empty dataframe
```python
import pandas as pd
from datetime import datetime, timedelta

end = datetime.utcnow()
begin = end - timedelta(minutes=1)
data_interval = 10
date_index = pd.date_range(start=begin,
                           end=end,
                           freq='{} s'.format(data_interval))
df = pd.DataFrame([], columns=['time','a','b'])
df = df.set_index('time', drop=True)
tol = timedelta(seconds=9)
df = df.reindex(date_index, method='pad', tolerance=tol)

# IndexError: index -1 is out of bounds for axis 0 with size 0

df = pd.DataFrame([], columns=['time','a','b'])
df = df.reindex(date_index, method='nearest')

# IndexError: index -1 is out of bounds for axis 0 with size 0

```
You get an index error when a dataframe is empty using the `tolerance=` or `method='nearest'` .

This is not something that happens with other usages of reindex and can come up as a surprise when reindexing an empty window of data. I would expect it to behave the same as it does without tolerance here.

#### Expected Output
Should be same as reindex with no args in this case which returns...
```
                              a    b
2019-07-09 22:35:05.165640  NaN  NaN
2019-07-09 22:35:15.165640  NaN  NaN
2019-07-09 22:35:25.165640  NaN  NaN
2019-07-09 22:35:35.165640  NaN  NaN
2019-07-09 22:35:45.165640  NaN  NaN
2019-07-09 22:35:55.165640  NaN  NaN
2019-07-09 22:36:05.165640  NaN  NaN
```

#### Temp Solution
Simple user solution is to check length... but this is a problem that might surprise someone at a bad time like it did for us.
``` python
if(len(df) is 0):
    df = df.reindex(date_index)
else:
    df = df.reindex(date_index, method='pad', tolerance=tol)
```

#### Output of ``pd.show_versions()``

<details>
INSTALLED VERSIONS
------------------
commit: None
python: 3.6.5.final.0
python-bits: 64
OS: Linux
OS-release: 4.15.0-54-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.24.2
pytest: 5.0.1
pip: 18.0
setuptools: 40.4.1
Cython: None
numpy: 1.16.4
scipy: None
pyarrow: None
xarray: None
IPython: None
sphinx: None
patsy: None
dateutil: 2.8.0
pytz: 2019.1
blosc: None
bottleneck: None
tables: None
numexpr: None
feather: None
matplotlib: None
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: 1.1.8
lxml.etree: None
bs4: None
html5lib: None
sqlalchemy: 1.3.5
pymysql: 0.9.3
psycopg2: None
jinja2: 2.10.1
s3fs: None
fastparquet: None
pandas_gbq: 0.6.1
pandas_datareader: None
gcsfs: None

</details>

Hints from issue discussion:

Commit message: Change import order

Patch to review:
diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst
index 28f7df98cb86b..22712a8f049ff 100644
--- a/doc/source/whatsnew/v1.2.0.rst
+++ b/doc/source/whatsnew/v1.2.0.rst
@@ -584,6 +584,7 @@ Indexing
 - Bug in :meth:`DataFrame.loc` returned requested key plus missing values when ``loc`` was applied to single level from :class:`MultiIndex` (:issue:`27104`)
 - Bug in indexing on a :class:`Series` or :class:`DataFrame` with a :class:`CategoricalIndex` using a listlike indexer containing NA values (:issue:`37722`)
 - Bug in :meth:`DataFrame.xs` ignored ``droplevel=False`` for columns (:issue:`19056`)
+- Bug in :meth:`DataFrame.reindex` returns ``IndexingError`` for empty :class:`DataFrame` with ``tolerance`` not None or ``method="nearest"`` (:issue:`27315`)
 
 Missing
 ^^^^^^^
diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py
index cb5641a74e60b..30b582f854b57 100644
--- a/pandas/core/indexes/base.py
+++ b/pandas/core/indexes/base.py
@@ -3163,7 +3163,7 @@ def _get_fill_indexer(
             indexer = engine_method(target_values, limit)
         else:
             indexer = self._get_fill_indexer_searchsorted(target, method, limit)
-        if tolerance is not None:
+        if tolerance is not None and len(self):
             indexer = self._filter_indexer_tolerance(target_values, indexer, tolerance)
         return indexer
 
@@ -3208,6 +3208,9 @@ def _get_nearest_indexer(self, target: "Index", limit, tolerance) -> np.ndarray:
         values that can be subtracted from each other (e.g., not strings or
         tuples).
         """
+        if not len(self):
+            return self._get_fill_indexer(target, "pad")
+
         left_indexer = self.get_indexer(target, "pad", limit=limit)
         right_indexer = self.get_indexer(target, "backfill", limit=limit)
 
diff --git a/pandas/tests/indexes/datetimes/test_reindex.py b/pandas/tests/indexes/datetimes/test_reindex.py
new file mode 100644
index 0000000000000..d9a231c9a560a
--- /dev/null
+++ b/pandas/tests/indexes/datetimes/test_reindex.py
@@ -0,0 +1,25 @@
+from datetime import timedelta
+
+import pytest
+
+from pandas import NA, DataFrame, Index, date_range
+import pandas._testing as tm
+
+
+class TestReindex:
+    @pytest.mark.parametrize(
+        "kwargs",
+        [
+            {"method": "pad", "tolerance": timedelta(seconds=9)},
+            {"method": "backfill", "tolerance": timedelta(seconds=9)},
+            {"method": "nearest"},
+            {"method": None},
+        ],
+    )
+    def test_reindex_empty_frame(self, kwargs):
+        # GH#27315
+        idx = date_range(start="2020", freq="30s", periods=3)
+        df = DataFrame([], index=Index([], name="time"), columns=["a"])
+        result = df.reindex(idx, **kwargs)
+        expected = DataFrame({"a": [NA] * 3}, index=idx)
+        tm.assert_frame_equal(result, expected)


Code review comments:
diff_hunk: @@ -584,6 +584,7 @@ Indexing
 - Bug in :meth:`DataFrame.loc` returned requested key plus missing values when ``loc`` was applied to single level from :class:`MultiIndex` (:issue:`27104`)
 - Bug in indexing on a :class:`Series` or :class:`DataFrame` with a :class:`CategoricalIndex` using a listlike indexer containing NA values (:issue:`37722`)
 - Bug in :meth:`DataFrame.xs` ignored ``droplevel=False`` for columns (:issue:`19056`)
+- Bug in :meth:`DataFrame.reindex` returns ``IndexingError`` for empty :class:`DataFrame` with ``tolerance`` not None or ``method="nearest"`` (:issue:`27315`)
path: doc/source/whatsnew/v1.2.0.rst
text: @user1:
is the bug that it is raising IndexingError when it shouldnt or that it isnt when it should?

returns -> raising

@author:
wrongly raising when it should not. Changed and clarified it