Repository: alteryx/evalml (python)

Issue:
IndexError: positional indexers are out-of-bounds 
### Reproducer

Note: the example below works when `n_rows` is small

```
import evalml
import pandas as pd
automl = evalml.AutoMLSearch(problem_type="binary")

n_rows = 1000000 
X = pd.DataFrame({"f_1": [1, 2, 3]*n_rows})
X.index = pd.Series(range(X.shape[0])) + 1
y = pd.Series([True, False, True]*n_rows)

automl.search(X, y, data_checks='disabled')
```

this give the follow stacktrace

```
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/pandas/core/indexing.py in _get_list_axis(self, key, axis)
   2110         try:
-> 2111             return self.obj._take_with_is_copy(key, axis=axis)
   2112         except IndexError:

~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/pandas/core/generic.py in _take_with_is_copy(self, indices, axis, **kwargs)
   3408         """
-> 3409         result = self.take(indices=indices, axis=axis, **kwargs)
   3410         # Maybe set copy if we didn't actually change the index.

~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/pandas/core/generic.py in take(self, indices, axis, is_copy, **kwargs)
   3394         new_data = self._data.take(
-> 3395             indices, axis=self._get_block_manager_axis(axis), verify=True
   3396         )

~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/pandas/core/internals/managers.py in take(self, indexer, axis, verify, convert)
   1385         if convert:
-> 1386             indexer = maybe_convert_indices(indexer, n)
   1387 

~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/pandas/core/indexers.py in maybe_convert_indices(indices, n)
    212     if mask.any():
--> 213         raise IndexError("indices are out-of-bounds")
    214     return indices

IndexError: indices are out-of-bounds

During handling of the above exception, another exception occurred:

IndexError                                Traceback (most recent call last)
<ipython-input-24-2d762c4df3d4> in <module>
      8 y = pd.Series([True, False, True]*n_rows)
      9 
---> 10 automl.search(X, y, data_checks='disabled')

~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/evalml/automl/automl_search.py in search(self, X, y, data_checks, feature_types, show_iteration_plot)
    410         self._start = time.time()
    411 
--> 412         should_terminate = self._add_baseline_pipelines(X, y)
    413         if should_terminate:
    414             return

~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/evalml/automl/automl_search.py in _add_baseline_pipelines(self, X, y)
    543                                 self._start)
    544 
--> 545                 baseline_results = self._compute_cv_scores(baseline, X, y)
    546                 self._baseline_cv_score = baseline_results["cv_score_mean"]
    547                 self._add_result(trained_pipeline=baseline,

~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/evalml/automl/automl_search.py in _compute_cv_scores(self, pipeline, X, y)
    563         for i, (train, test) in enumerate(self.data_split.split(X, y)):
    564             logger.debug(f"\t\tTraining and scoring on fold {i}")
--> 565             X_train, X_test = X.iloc[train], X.iloc[test]
    566             y_train, y_test = y.iloc[train], y.iloc[test]
    567             objectives_to_score = [self.objective] + self.additional_objectives

~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/pandas/core/indexing.py in __getitem__(self, key)
   1766 
   1767             maybe_callable = com.apply_if_callable(key, self.obj)
-> 1768             return self._getitem_axis(maybe_callable, axis=axis)
   1769 
   1770     def _is_scalar_access(self, key: Tuple):

~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/pandas/core/indexing.py in _getitem_axis(self, key, axis)
   2127         # a list of integers
   2128         elif is_list_like_indexer(key):
-> 2129             return self._get_list_axis(key, axis=axis)
   2130 
   2131         # a single integer

~/.pyenv/versions/3.6.9/envs/tmp-install/lib/python3.6/site-packages/pandas/core/indexing.py in _get_list_axis(self, key, axis)
   2112         except IndexError:
   2113             # re-raise with different error message
-> 2114             raise IndexError("positional indexers are out-of-bounds")
   2115 
   2116     def _getitem_axis(self, key, axis: int):

IndexError: positional indexers are out-of-bounds
```


### workaround

This works

```
automl.search(X.reset_index(), y, data_checks='disabled')
```

### Notes

This seems to only occur once the data is above a certain number of rows, so i suspect it has to do with the different data splitter we use in that case

Pull request:
Fix bug with TrainingValidationSplit and custom index
### Pull Request Description
Fixes #1126

-----
*After creating the pull request: in order to pass the **release_notes_updated** check you will need to update the "Future Release" section of* `docs/source/release_notes.rst` *to include this pull request by adding :pr:`123`.*


Patch:
diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst
index cc06fe2c..97209e7f 100644
--- a/docs/source/release_notes.rst
+++ b/docs/source/release_notes.rst
@@ -23,6 +23,7 @@ Release Notes
         * Added stacked ensemble estimators to to ``evalml.pipelines.__init__`` file :pr:`1326`
         * Fixed bug in OHE where calls to transform were not deterministic if ``top_n`` was less than the number of categories in a column :pr:`1324`
         * Fix warnings thrown during AutoMLSearch in ``HighVarianceCVDataCheck`` :pr:`1346`
+        * Fixed bug where TrainingValidationSplit would return invalid location indices for dataframes with a custom index :pr:`1348`
     * Changes
         * Allow ``add_to_rankings`` to be called before AutoMLSearch is called :pr:`1250`
         * Removed Graphviz from test-requirements to add to requirements.txt :pr:`1327`
diff --git a/evalml/automl/data_splitters/training_validation_split.py b/evalml/automl/data_splitters/training_validation_split.py
index ce9900ba..47a5e5c4 100644
--- a/evalml/automl/data_splitters/training_validation_split.py
+++ b/evalml/automl/data_splitters/training_validation_split.py
@@ -1,3 +1,4 @@
+import numpy as np
 from sklearn.model_selection import train_test_split
 from sklearn.model_selection._split import BaseCrossValidator
 
@@ -41,5 +42,5 @@ class TrainingValidationSplit(BaseCrossValidator):
             Returns:
                 list: indices to split data into training and test set
         """
-        train, test = train_test_split(X.index, test_size=self.test_size, train_size=self.train_size, shuffle=self.shuffle, stratify=self.stratify, random_state=self.random_state)
+        train, test = train_test_split(np.arange(X.shape[0]), test_size=self.test_size, train_size=self.train_size, shuffle=self.shuffle, stratify=self.stratify, random_state=self.random_state)
         return [(train, test)]


Test patch:
diff --git a/evalml/tests/automl_tests/test_training_validation_split.py b/evalml/tests/automl_tests/test_training_validation_split.py
index 90caeb98..82952183 100644
--- a/evalml/tests/automl_tests/test_training_validation_split.py
+++ b/evalml/tests/automl_tests/test_training_validation_split.py
@@ -1,5 +1,6 @@
 import numpy as np
 import pandas as pd
+import pytest
 
 from evalml.automl import TrainingValidationSplit
 
@@ -15,8 +16,8 @@ def test_tvsplit_default():
     splits = splitter.split(X, y=y)
     assert len(splits) == 1 and len(splits[0]) == 2
     # sklearn train_test_split will do a 75/25 split by default
-    pd.testing.assert_index_equal(splits[0][0], pd.Int64Index([0, 1, 2, 3, 4, 5, 6], dtype='int64'))
-    pd.testing.assert_index_equal(splits[0][1], pd.Int64Index([7, 8, 9], dtype='int64'))
+    np.testing.assert_equal(splits[0][0], [0, 1, 2, 3, 4, 5, 6])
+    np.testing.assert_equal(splits[0][1], [7, 8, 9])
 
 
 def test_tvsplit_size():
@@ -25,14 +26,14 @@ def test_tvsplit_size():
     splitter = TrainingValidationSplit(test_size=0.2, train_size=0.3)
     splits = splitter.split(X, y=y)
     assert len(splits) == 1 and len(splits[0]) == 2
-    pd.testing.assert_index_equal(splits[0][0], pd.Int64Index([0, 1, 2], dtype='int64'))
-    pd.testing.assert_index_equal(splits[0][1], pd.Int64Index([3, 4], dtype='int64'))
+    np.testing.assert_equal(splits[0][0], [0, 1, 2])
+    np.testing.assert_equal(splits[0][1], [3, 4])
 
     splitter = TrainingValidationSplit(test_size=2, train_size=3)
     splits = splitter.split(X, y=y)
     assert len(splits) == 1 and len(splits[0]) == 2
-    pd.testing.assert_index_equal(splits[0][0], pd.Int64Index([0, 1, 2], dtype='int64'))
-    pd.testing.assert_index_equal(splits[0][1], pd.Int64Index([3, 4], dtype='int64'))
+    np.testing.assert_equal(splits[0][0], [0, 1, 2])
+    np.testing.assert_equal(splits[0][1], [3, 4])
 
 
 def test_tvsplit_shuffle():
@@ -41,8 +42,8 @@ def test_tvsplit_shuffle():
     splitter = TrainingValidationSplit(shuffle=True, random_state=0)
     splits = splitter.split(X, y=y)
     assert len(splits) == 1 and len(splits[0]) == 2
-    pd.testing.assert_index_equal(splits[0][0], pd.Int64Index([9, 1, 6, 7, 3, 0, 5], dtype='int64'))
-    pd.testing.assert_index_equal(splits[0][1], pd.Int64Index([2, 8, 4], dtype='int64'))
+    np.testing.assert_equal(splits[0][0], [9, 1, 6, 7, 3, 0, 5])
+    np.testing.assert_equal(splits[0][1], [2, 8, 4])
 
 
 def test_tvsplit_stratify():
@@ -51,5 +52,15 @@ def test_tvsplit_stratify():
     splitter = TrainingValidationSplit(train_size=5, test_size=5, shuffle=True, stratify=y, random_state=0)
     splits = splitter.split(X, y=y)
     assert len(splits) == 1 and len(splits[0]) == 2
-    pd.testing.assert_index_equal(splits[0][0], pd.Int64Index([1, 4, 2, 8, 7], dtype='int64'))
-    pd.testing.assert_index_equal(splits[0][1], pd.Int64Index([3, 6, 9, 0, 5], dtype='int64'))
+    np.testing.assert_equal(splits[0][0], [1, 4, 2, 8, 7])
+    np.testing.assert_equal(splits[0][1], [3, 6, 9, 0, 5])
+
+
+@pytest.mark.parametrize("random_state", [0, 11, 57, 99, 1000])
+def test_tvsplit_always_within_bounds_with_custom_index(random_state):
+    N = 11000
+    X = pd.DataFrame({'col1': np.arange(0, N)}, index=np.arange(20000, 20000 + N))
+    splitter = TrainingValidationSplit(train_size=0.75, shuffle=True, random_state=random_state)
+    splits = splitter.split(X, y=None)
+    assert np.all(np.logical_and(splits[0][0] < N, splits[0][0] >= 0))
+    assert np.all(np.logical_and(splits[0][1] < N, splits[0][1] >= 0))