Repository: deepchecks/deepchecks

Issue:
[BUG] Identifier Leakage is using deprecated columns for datetime and index
Identifier Leakage is using `datetime_name` and `index_name` which may exist, but index and datetime may also exist on index columns instead.


Discussion:

Resolving patch:
diff --git a/deepchecks/tabular/checks/methodology/identifier_leakage.py b/deepchecks/tabular/checks/methodology/identifier_leakage.py
--- a/deepchecks/tabular/checks/methodology/identifier_leakage.py
+++ b/deepchecks/tabular/checks/methodology/identifier_leakage.py
@@ -11,6 +11,7 @@
 """module contains Identifier Leakage check."""
 from typing import Dict
 
+import pandas as pd
 import plotly.express as px
 
 import deepchecks.ppscore as pps
@@ -58,15 +59,24 @@ def run_logic(self, context: Context, dataset_type: str = 'train') -> CheckResul
         dataset.assert_label()
         label_name = dataset.label_name
 
-        relevant_columns = list(filter(None, [dataset.datetime_name, dataset.index_name, label_name]))
+        relevant_data = pd.DataFrame({
+            it.name: it
+            for it in (dataset.index_col, dataset.datetime_col, dataset.label_col)
+            if it is not None
+        })
 
-        if len(relevant_columns) == 1:
+        if len(relevant_data.columns) == 1:
             raise DatasetValidationError(
                 'Check is irrelevant for Datasets without index or date column'
             )
 
-        df_pps = pps.predictors(df=dataset.data[relevant_columns], y=label_name, random_seed=42,
-                                **self.ppscore_params)
+        df_pps = pps.predictors(
+            df=relevant_data,
+            y=label_name,
+            random_seed=42,
+            **self.ppscore_params
+        )
+
         df_pps = df_pps.set_index('x', drop=True)
         s_ppscore = df_pps['ppscore']
 
diff --git a/deepchecks/tabular/dataset.py b/deepchecks/tabular/dataset.py
--- a/deepchecks/tabular/dataset.py
+++ b/deepchecks/tabular/dataset.py
@@ -624,11 +624,12 @@ def index_col(self) -> t.Optional[pd.Series]:
            If index column exists, returns a pandas Series of the index column.
         """
         if self._set_index_from_dataframe_index is True:
+            index_name = self.data.index.name or 'index'
             if self._index_name is None:
-                return pd.Series(self.data.index.get_level_values(0), name=self.data.index.name,
+                return pd.Series(self.data.index.get_level_values(0), name=index_name,
                                  index=self.data.index)
             elif isinstance(self._index_name, (str, int)):
-                return pd.Series(self.data.index.get_level_values(self._index_name), name=self.data.index.name,
+                return pd.Series(self.data.index.get_level_values(self._index_name), name=index_name,
                                  index=self.data.index)
             else:
                 raise DeepchecksValueError(f'Don\'t know to handle index_name of type {type(self._index_name)}')
@@ -650,11 +651,12 @@ def datetime_name(self) -> t.Optional[Hashable]:
 
     def get_datetime_column_from_index(self, datetime_name):
         """Retrieve the datetime info from the index if _set_datetime_from_dataframe_index is True."""
+        index_name = self.data.index.name or 'datetime'
         if datetime_name is None:
-            return pd.Series(self.data.index.get_level_values(0), name='datetime',
+            return pd.Series(self.data.index.get_level_values(0), name=index_name,
                              index=self.data.index)
         elif isinstance(datetime_name, (str, int)):
-            return pd.Series(self.data.index.get_level_values(datetime_name), name='datetime',
+            return pd.Series(self.data.index.get_level_values(datetime_name), name=index_name,
                              index=self.data.index)
 
     @property


Test patch:
diff --git a/tests/checks/methodology/identifier_leakage_test.py b/tests/checks/methodology/identifier_leakage_test.py
--- a/tests/checks/methodology/identifier_leakage_test.py
+++ b/tests/checks/methodology/identifier_leakage_test.py
@@ -20,7 +20,7 @@
 from tests.checks.utils import equal_condition_result
 
 
-def util_generate_dataframe_and_expected():
+def generate_dataframe_and_expected():
     np.random.seed(42)
     df = pd.DataFrame(np.random.randn(100, 3), columns=['x1', 'x2', 'x3'])
     df['label'] = df['x2'] + 0.1 * df['x1']
@@ -29,13 +29,33 @@ def util_generate_dataframe_and_expected():
 
 
 def test_assert_identifier_leakage():
-    df, expected = util_generate_dataframe_and_expected()
+    df, expected = generate_dataframe_and_expected()
     result = IdentifierLeakage().run(dataset=Dataset(df, label='label', datetime_name='x2', index_name='x3'))
     for key, value in result.value.items():
         assert_that(key, is_in(expected.keys()))
         assert_that(value, close_to(expected[key], 0.1))
 
 
+def test_identifier_leakage_with_extracted_from_dataframe_index():
+    df, expected = generate_dataframe_and_expected()
+    df.set_index('x3', inplace=True)
+    dataset = Dataset(df=df, label='label', set_index_from_dataframe_index=True)
+    result = IdentifierLeakage().run(dataset=dataset)
+    for key, value in result.value.items():
+        assert_that(key, is_in(expected.keys()))
+        assert_that(value, close_to(expected[key], 0.1))
+
+
+def test_identifier_leakage_with_extracted_from_dataframe_datatime_index():
+    df, expected = generate_dataframe_and_expected()
+    df.set_index('x2', inplace=True)
+    dataset = Dataset(df=df, label='label', set_datetime_from_dataframe_index=True)
+    result = IdentifierLeakage().run(dataset=dataset)
+    for key, value in result.value.items():
+        assert_that(key, is_in(expected.keys()))
+        assert_that(value, close_to(expected[key], 0.1))
+
+
 def test_dataset_wrong_input():
     wrong = 'wrong_input'
     assert_that(
@@ -47,7 +67,7 @@ def test_dataset_wrong_input():
 
 
 def test_dataset_no_label():
-    df, _ = util_generate_dataframe_and_expected()
+    df, _ = generate_dataframe_and_expected()
     df = Dataset(df)
     assert_that(
         calling(IdentifierLeakage().run).with_args(dataset=df),
@@ -57,7 +77,7 @@ def test_dataset_no_label():
 
 
 def test_dataset_only_label():
-    df, _ = util_generate_dataframe_and_expected()
+    df, _ = generate_dataframe_and_expected()
     df = Dataset(df, label='label')
     assert_that(
         calling(IdentifierLeakage().run).with_args(dataset=df),
@@ -68,7 +88,7 @@ def test_dataset_only_label():
 
 
 def test_assert_identifier_leakage_class():
-    df, expected = util_generate_dataframe_and_expected()
+    df, expected = generate_dataframe_and_expected()
     identifier_leakage_check = IdentifierLeakage()
     result = identifier_leakage_check.run(dataset=Dataset(df, label='label', datetime_name='x2', index_name='x3'))
     for key, value in result.value.items():
@@ -77,7 +97,7 @@ def test_assert_identifier_leakage_class():
 
 
 def test_nan():
-    df, expected = util_generate_dataframe_and_expected()
+    df, expected = generate_dataframe_and_expected()
     nan_df = df.append(pd.DataFrame({'x1': [np.nan],
                                      'x2': [np.nan],
                                      'x3': [np.nan],
@@ -90,7 +110,7 @@ def test_nan():
 
 
 def test_condition_pps_pass():
-    df, expected = util_generate_dataframe_and_expected()
+    df, expected = generate_dataframe_and_expected()
 
     check = IdentifierLeakage().add_condition_pps_not_greater_than(0.5)
 
@@ -104,7 +124,7 @@ def test_condition_pps_pass():
 
 
 def test_condition_pps_fail():
-    df, expected = util_generate_dataframe_and_expected()
+    df, expected = generate_dataframe_and_expected()
 
     check = IdentifierLeakage().add_condition_pps_not_greater_than(0.2)