Repository: alteryx/woodwork (python)

Issue:
Mutual information job can fail with certain index types
Mutual information job can fail if index has a logical type that is not included in the types returned by `ww.utils.get_valid_mi_types()`. This is because of the flow in the code. We first generate a list of valid columns and then attempt to remove the index column if it is set. However, if the index column is not included in the original list of valid columns because of its LogicalType, the attempt to remove it will fail.

#### Code Sample, a copy-pastable example to reproduce your bug.

```python
import pandas as pd
import woodwork as ww

df = pd.DataFrame({
    "idx": ["A", "B", "C", "D"],
    "int": range(4),
    "cat": [1, 2, 1, 1]
})
df.ww.init(index="idx")
df.ww.mutual_information()
```

```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/var/folders/b1/hzhmvl_s1xv9n11qgdmzrsw5c63y2l/T/ipykernel_96938/1116556468.py in <module>
----> 1 df.ww.mutual_information()

~/dev/woodwork/woodwork/table_accessor.py in mutual_information(self, num_bins, nrows, include_index, callback)
    941             (perfect dependency).
    942         """
--> 943         mutual_info = self.mutual_information_dict(
    944             num_bins, nrows, include_index, callback
    945         )

~/dev/woodwork/woodwork/accessor_utils.py in wrapper(self, *args, **kwargs)
    205             )
    206             raise WoodworkNotInitError(msg)
--> 207         return method(self, *args, **kwargs)
    208 
    209     return wrapper

~/dev/woodwork/woodwork/table_accessor.py in mutual_information_dict(self, num_bins, nrows, include_index, callback)
    903             (perfect dependency).
    904         """
--> 905         return _get_mutual_information_dict(
    906             self._dataframe, num_bins, nrows, include_index, callback
    907         )

~/dev/woodwork/woodwork/statistics_utils.py in _get_mutual_information_dict(dataframe, num_bins, nrows, include_index, callback)
    282 
    283     if not include_index and dataframe.ww.index is not None:
--> 284         valid_columns.remove(dataframe.ww.index)
    285 
    286     data = dataframe.loc[:, valid_columns]

ValueError: list.remove(x): x not in list
```


Pull request:
Fix mutual_information bug with string index
- Fix mutual_information bug with string index
- Closes #1198 

Update so that we only try to remove the index column from the list of valid columns for mutual information if it is present, preventing an error that happened when a string index was used previously.

Patch:
diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst
index bd21f23..31e89c9 100644
--- a/docs/source/release_notes.rst
+++ b/docs/source/release_notes.rst
@@ -7,6 +7,7 @@ Future Release
 ==============
     * Enhancements
     * Fixes
+        * Fix bug that causes ``mutual_information`` to fail with certain index types (:pr:`1199`)
     * Changes
         * Update pip to 21.3.1 for test requirements (:pr:`1196`)
     * Documentation Changes
@@ -14,7 +15,7 @@ Future Release
     * Testing Changes
 
     Thanks to the following people for contributing to this release:
-    :user:`gsheni`
+    :user:`gsheni`, :user:`thehomebrewnerd`
 
 v0.9.0 Nov 11, 2021
 ===================
diff --git a/woodwork/statistics_utils.py b/woodwork/statistics_utils.py
index 0a74f67..19fc5f5 100644
--- a/woodwork/statistics_utils.py
+++ b/woodwork/statistics_utils.py
@@ -280,8 +280,9 @@ def _get_mutual_information_dict(
         if type(col.logical_type) in valid_types
     ]
 
-    if not include_index and dataframe.ww.index is not None:
-        valid_columns.remove(dataframe.ww.index)
+    index = dataframe.ww.index
+    if not include_index and index is not None and index in valid_columns:
+        valid_columns.remove(index)
 
     data = dataframe.loc[:, valid_columns]
     if _is_dask_dataframe(data):


Test patch:
diff --git a/woodwork/tests/accessor/test_statistics.py b/woodwork/tests/accessor/test_statistics.py
index 68094d1..7cb1de3 100644
--- a/woodwork/tests/accessor/test_statistics.py
+++ b/woodwork/tests/accessor/test_statistics.py
@@ -295,6 +295,23 @@ def test_get_valid_mi_columns_with_index(sample_df):
     assert "id" in mi
 
 
+def test_mutual_info_with_string_index():
+    df = pd.DataFrame(
+        {
+            "id": ["a", "b", "c"],
+            "col1": [1, 2, 3],
+            "col2": [10, 20, 30],
+        }
+    )
+    df.ww.init(index="id", logical_types={"id": "unknown"})
+    mi = df.ww.mutual_information()
+
+    cols_used = set(np.unique(mi[["column_1", "column_2"]].values))
+    assert "id" not in cols_used
+    assert "col1" in cols_used
+    assert "col2" in cols_used
+
+
 def test_get_describe_dict(describe_df):
     describe_df.ww.init(index="index_col")