Repository: modin-project/modin

Issue:
loc with missing index values is supported in Modin but not in pandas
We should throw an error when `loc` is passed non-existent values similar to pandas.


Discussion:

Resolving patch:
diff --git a/modin/pandas/dataframe.py b/modin/pandas/dataframe.py
--- a/modin/pandas/dataframe.py
+++ b/modin/pandas/dataframe.py
@@ -67,11 +67,38 @@ def __init__(
             query_compiler: A query compiler object to manage distributed computation.
         """
         if isinstance(data, (DataFrame, Series)):
-            self._query_compiler = data._query_compiler
-            if isinstance(data, Series) and data.name is None:
-                self.columns = [0]
-            else:
+            self._query_compiler = data._query_compiler.copy()
+            if index is not None and any(i not in data.index for i in index):
+                raise NotImplementedError(
+                    "Passing non-existant columns or index values to constructor not"
+                    " yet implemented."
+                )
+            if isinstance(data, Series):
+                # We set the column name if it is not in the provided Series
+                if data.name is None:
+                    self.columns = [0] if columns is None else columns
+                # If the columns provided are not in the named Series, pandas clears
+                # the DataFrame and sets columns to the columns provided.
+                elif columns is not None and data.name not in columns:
+                    self._query_compiler = from_pandas(
+                        DataFrame(columns=columns)
+                    )._query_compiler
+                if index is not None:
+                    self._query_compiler = data.loc[index]._query_compiler
+            elif columns is None and index is None:
                 data._add_sibling(self)
+            else:
+                if columns is not None and any(i not in data.columns for i in columns):
+                    raise NotImplementedError(
+                        "Passing non-existant columns or index values to constructor not"
+                        " yet implemented."
+                    )
+                if index is None:
+                    index = slice(None)
+                if columns is None:
+                    columns = slice(None)
+                self._query_compiler = data.loc[index, columns]._query_compiler
+
         # Check type of data and use appropriate constructor
         elif query_compiler is None:
             distributed_frame = from_non_pandas(data, index, columns, dtype)
diff --git a/modin/pandas/indexing.py b/modin/pandas/indexing.py
--- a/modin/pandas/indexing.py
+++ b/modin/pandas/indexing.py
@@ -227,6 +227,11 @@ def __getitem__(self, key):
                 return self.df.iloc[:, slice(*result_slice)]
 
         row_lookup, col_lookup = self._compute_lookup(row_loc, col_loc)
+        if any(i == -1 for i in row_lookup) or any(i == -1 for i in col_lookup):
+            raise KeyError(
+                "Passing list-likes to .loc or [] with any missing labels is no longer "
+                "supported, see https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#deprecate-loc-reindex-listlike"
+            )
         result = super(_LocIndexer, self).__getitem__(row_lookup, col_lookup, ndim)
         # Pandas drops the levels that are in the `loc`, so we have to as well.
         if hasattr(result, "index") and isinstance(result.index, pandas.MultiIndex):
diff --git a/modin/pandas/series.py b/modin/pandas/series.py
--- a/modin/pandas/series.py
+++ b/modin/pandas/series.py
@@ -48,14 +48,29 @@ def __init__(
         fastpath=False,
         query_compiler=None,
     ):
-        """Constructor for a Series object.
-
-        Args:
-            series_oids ([ObjectID]): The list of remote Series objects.
         """
+    One-dimensional ndarray with axis labels (including time series).
+
+    Args:
+        data: Contains data stored in Series.
+        index: Values must be hashable and have the same length as `data`.
+        dtype: Data type for the output Series. If not specified, this will be
+            inferred from `data`.
+        name: The name to give to the Series.
+        copy: Copy input data.
+        query_compiler: A query compiler object to create the Series from.
+    """
         if isinstance(data, type(self)):
-            query_compiler = data._query_compiler
+            query_compiler = data._query_compiler.copy()
+            if index is not None:
+                if any(i not in data.index for i in index):
+                    raise NotImplementedError(
+                        "Passing non-existant columns or index values to constructor "
+                        "not yet implemented."
+                    )
+                query_compiler = data.loc[index]._query_compiler
         if query_compiler is None:
+            # Defaulting to pandas
             warnings.warn(
                 "Distributing {} object. This may take some time.".format(type(data))
             )
@@ -78,6 +93,9 @@ def __init__(
         ):
             query_compiler = query_compiler.transpose()
         self._query_compiler = query_compiler
+        if name is not None:
+            self._query_compiler = self._query_compiler
+            self.name = name
 
     def _get_name(self):
         name = self._query_compiler.columns[0]


Test patch:
diff --git a/modin/pandas/test/test_dataframe.py b/modin/pandas/test/test_dataframe.py
--- a/modin/pandas/test/test_dataframe.py
+++ b/modin/pandas/test/test_dataframe.py
@@ -4367,6 +4367,10 @@ def test_loc(self, request, data):
             pandas_df_copy.loc[[1, 2]] = 42
             df_equals(modin_df_copy, pandas_df_copy)
 
+        # From issue #1374
+        with pytest.raises(KeyError):
+            modin_df.loc["NO_EXIST"]
+
     def test_loc_multi_index(self):
         modin_df = pd.read_csv(
             "modin/pandas/test/data/blah.csv", header=[0, 1, 2, 3], index_col=0
@@ -5391,6 +5395,44 @@ def test_constructor(self, data):
         modin_df = pd.DataFrame({k: pd.Series(v) for k, v in data.items()})
         df_equals(pandas_df, modin_df)
 
+    def test_constructor_columns_and_index(self):
+        modin_df = pd.DataFrame(
+            [[1, 1, 10], [2, 4, 20], [3, 7, 30]],
+            index=[1, 2, 3],
+            columns=["id", "max_speed", "health"],
+        )
+        pandas_df = pandas.DataFrame(
+            [[1, 1, 10], [2, 4, 20], [3, 7, 30]],
+            index=[1, 2, 3],
+            columns=["id", "max_speed", "health"],
+        )
+        df_equals(modin_df, pandas_df)
+        df_equals(pd.DataFrame(modin_df), pandas.DataFrame(pandas_df))
+        df_equals(
+            pd.DataFrame(modin_df, columns=["max_speed", "health"]),
+            pandas.DataFrame(pandas_df, columns=["max_speed", "health"]),
+        )
+        df_equals(
+            pd.DataFrame(modin_df, index=[1, 2]),
+            pandas.DataFrame(pandas_df, index=[1, 2]),
+        )
+        df_equals(
+            pd.DataFrame(modin_df, index=[1, 2], columns=["health"]),
+            pandas.DataFrame(pandas_df, index=[1, 2], columns=["health"]),
+        )
+        df_equals(
+            pd.DataFrame(modin_df.iloc[:, 0], index=[1, 2, 3]),
+            pandas.DataFrame(pandas_df.iloc[:, 0], index=[1, 2, 3]),
+        )
+        df_equals(
+            pd.DataFrame(modin_df.iloc[:, 0], columns=["NO_EXIST"]),
+            pandas.DataFrame(pandas_df.iloc[:, 0], columns=["NO_EXIST"]),
+        )
+        with pytest.raises(NotImplementedError):
+            pd.DataFrame(modin_df, index=[1, 2, 99999])
+        with pytest.raises(NotImplementedError):
+            pd.DataFrame(modin_df, columns=["NO_EXIST"])
+
 
 class TestDataFrameJoinSort:
     @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
diff --git a/modin/pandas/test/test_series.py b/modin/pandas/test/test_series.py
--- a/modin/pandas/test/test_series.py
+++ b/modin/pandas/test/test_series.py
@@ -967,6 +967,30 @@ def test_compress(data):
         modin_series.compress(modin_series > 30)
 
 
+@pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
+def test_constructor(data):
+    modin_series, pandas_series = create_test_series(data)
+    df_equals(modin_series, pandas_series)
+    df_equals(pd.Series(modin_series), pandas.Series(pandas_series))
+
+
+def test_constructor_columns_and_index():
+    modin_series = pd.Series([1, 1, 10], index=[1, 2, 3], name="health")
+    pandas_series = pandas.Series([1, 1, 10], index=[1, 2, 3], name="health")
+    df_equals(modin_series, pandas_series)
+    df_equals(pd.Series(modin_series), pandas.Series(pandas_series))
+    df_equals(
+        pd.Series(modin_series, name="max_speed"),
+        pandas.Series(pandas_series, name="max_speed"),
+    )
+    df_equals(
+        pd.Series(modin_series, index=[1, 2]),
+        pandas.Series(pandas_series, index=[1, 2]),
+    )
+    with pytest.raises(NotImplementedError):
+        pd.Series(modin_series, index=[1, 2, 99999])
+
+
 @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
 def test_copy(data):
     modin_series, pandas_series = create_test_series(data)