Repository: alteryx/woodwork
Pull request #1523 (2022-09-15 21:31:34)

Add Spearman Correlation to WW
Adding spearman to dependence_dict and table accessors 

codecov[bot]: # [Codecov](https://codecov.io/gh/alteryx/woodwork/pull/1523?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx) Report
> Merging [#1523](https://codecov.io/gh/alteryx/woodwork/pull/1523?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx) (7e46e4c) into [main](https://codecov.io/gh/alteryx/woodwork/commit/c7704e19df12ca840df5fe8210d0e53988608c10?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx) (c7704e1) will **decrease** coverage by `80.68%`.
> The diff coverage is `11.00%`.

> :exclamation: Current head 7e46e4c differs from pull request most recent head 58dcf10. Consider uploading reports for the commit 58dcf10 to get more accurate results

```diff
@@             Coverage Diff             @@
##             main    #1523       +/-   ##
===========================================
- Coverage   98.71%   18.03%   -80.69%     
===========================================
  Files          95       96        +1     
  Lines       10969    11053       +84     
===========================================
- Hits        10828     1993     -8835     
- Misses        141     9060     +8919     
```


| [Impacted Files](https://codecov.io/gh/alteryx/woodwork/pull/1523?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx) | Coverage Δ | |
|---|---|---|
| [...istics\_utils/\_calculate\_max\_dependence\_for\_pair.py](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvc3RhdGlzdGljc191dGlscy9fY2FsY3VsYXRlX21heF9kZXBlbmRlbmNlX2Zvcl9wYWlyLnB5) | `25.00% <0.00%> (-75.00%)` | :arrow_down: |
| [woodwork/statistics\_utils/\_parse\_measures.py](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvc3RhdGlzdGljc191dGlscy9fcGFyc2VfbWVhc3VyZXMucHk=) | `6.97% <0.00%> (-93.03%)` | :arrow_down: |
| [woodwork/tests/accessor/test\_statistics.py](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvYWNjZXNzb3IvdGVzdF9zdGF0aXN0aWNzLnB5) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [woodwork/tests/schema/test\_column\_schema.py](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvc2NoZW1hL3Rlc3RfY29sdW1uX3NjaGVtYS5weQ==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [woodwork/statistics\_utils/\_get\_dependence\_dict.py](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvc3RhdGlzdGljc191dGlscy9fZ2V0X2RlcGVuZGVuY2VfZGljdC5weQ==) | `12.19% <11.76%> (-87.81%)` | :arrow_down: |
| [woodwork/utils.py](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdXRpbHMucHk=) | `31.52% <12.50%> (-68.48%)` | :arrow_down: |
| [...rk/statistics\_utils/\_convert\_ordinal\_to\_numeric.py](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvc3RhdGlzdGljc191dGlscy9fY29udmVydF9vcmRpbmFsX3RvX251bWVyaWMucHk=) | `16.66% <16.66%> (ø)` | |
| [.../statistics\_utils/\_calculate\_dependence\_measure.py](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvc3RhdGlzdGljc191dGlscy9fY2FsY3VsYXRlX2RlcGVuZGVuY2VfbWVhc3VyZS5weQ==) | `20.58% <33.33%> (-79.42%)` | :arrow_down: |
| [woodwork/table\_accessor.py](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGFibGVfYWNjZXNzb3IucHk=) | `50.53% <50.00%> (-49.04%)` | :arrow_down: |
| [woodwork/column\_schema.py](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvY29sdW1uX3NjaGVtYS5weQ==) | `61.83% <75.00%> (-38.17%)` | :arrow_down: |
| ... and [66 more](https://codecov.io/gh/alteryx/woodwork/pull/1523/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx) | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx)

Cmancuso: Can you add the lookingglass results when you have those ready? It would also be nice to run EvalML and see what the change is on the data check side (if that's affected by this) 

Patch:
diff --git a/docs/source/api_reference.rst b/docs/source/api_reference.rst
index 3ab9b26..58e7838 100644
--- a/docs/source/api_reference.rst
+++ b/docs/source/api_reference.rst
@@ -41,6 +41,8 @@ WoodworkTableAccessor
     WoodworkTableAccessor.set_index
     WoodworkTableAccessor.set_time_index
     WoodworkTableAccessor.set_types
+    WoodworkTableAccessor.spearman_correlation
+    WoodworkTableAccessor.spearman_correlation_dict
     WoodworkTableAccessor.time_index
     WoodworkTableAccessor.to_disk
     WoodworkTableAccessor.to_dictionary
@@ -212,6 +214,7 @@ General Utils
     concat_columns
     get_valid_mi_types
     get_valid_pearson_types
+    get_valid_spearman_types
     read_file
 
 .. currentmodule:: woodwork.accessor_utils
diff --git a/docs/source/guides/statistical_insights.ipynb b/docs/source/guides/statistical_insights.ipynb
index 9950f55..528a87f 100644
--- a/docs/source/guides/statistical_insights.ipynb
+++ b/docs/source/guides/statistical_insights.ipynb
@@ -104,7 +104,8 @@
     "\n",
     "- `measure` - Which dependence measures to calculate. A list of measures can be provided to calculate multiple measures at once.  Valid measure strings:\n",
     "    - \"pearson\": calculates the Pearson correlation coefficient\n",
-    "    - \"mutual\": calculates the mutual information between columns\n",
+    "    - \"spearman\": calculates the Spearman correlation coefficient\n",
+    "    - \"mutual_info\": calculates the mutual information between columns\n",
     "    - \"max\": calculates both Pearson and mutual information and\n",
     "        returns max(abs(pearson), mutual) for each pair of columns\n",
     "    - \"all\": includes columns for \"pearson\", \"mutual\", and \"max\"\n",
@@ -384,7 +385,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3 (ipykernel)",
+   "display_name": "Python 3",
    "language": "python",
    "name": "python3"
   },
diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst
index ab03b43..f6fd37f 100644
--- a/docs/source/release_notes.rst
+++ b/docs/source/release_notes.rst
@@ -6,6 +6,7 @@ Release Notes
 Future Release
 ==============
     * Enhancements
+        * Added ``Spearman Correlation`` to options for dependence calculations (:pr:`1523`)
         * Added ``ignore_zeros`` as an argument for ``box_plot_dict`` to allow for calculations of outliers without 0 values (:pr:`1524`)
     * Fixes
         * Fix datetime pivot point to be set at current year + 10 rather than the default for two-digit years when ``datetime_format`` provided (:pr:`1512`)
diff --git a/woodwork/column_schema.py b/woodwork/column_schema.py
index 561701a..2e5603d 100644
--- a/woodwork/column_schema.py
+++ b/woodwork/column_schema.py
@@ -3,7 +3,14 @@ from inspect import isclass
 
 import woodwork as ww
 from woodwork.exceptions import DuplicateTagsWarning, StandardTagsChangedWarning
-from woodwork.logical_types import Boolean, BooleanNullable, Datetime, LatLong, Unknown
+from woodwork.logical_types import (
+    Boolean,
+    BooleanNullable,
+    Datetime,
+    LatLong,
+    Ordinal,
+    Unknown,
+)
 from woodwork.utils import _convert_input_to_set
 
 
@@ -159,6 +166,11 @@ class ColumnSchema(object):
         """Whether the ColumnSchema is a Unknown column"""
         return type(self.logical_type) == Unknown
 
+    @property
+    def is_ordinal(self):
+        """Whether the ColumnSchema is a Ordinal column"""
+        return type(self.logical_type) == Ordinal
+
     def _add_semantic_tags(self, new_tags, name):
         """Add the specified semantic tags to the current set of tags
 
diff --git a/woodwork/statistics_utils/__init__.py b/woodwork/statistics_utils/__init__.py
index d22590e..23e7f56 100644
--- a/woodwork/statistics_utils/__init__.py
+++ b/woodwork/statistics_utils/__init__.py
@@ -1,6 +1,9 @@
 from woodwork.statistics_utils._bin_numeric_cols_into_categories import (
     _bin_numeric_cols_into_categories,
 )
+from woodwork.statistics_utils._convert_ordinal_to_numeric import (
+    _convert_ordinal_to_numeric,
+)
 from woodwork.statistics_utils._get_box_plot_info_for_column import (
     _get_box_plot_info_for_column,
 )
diff --git a/woodwork/statistics_utils/_calculate_dependence_measure.py b/woodwork/statistics_utils/_calculate_dependence_measure.py
index 5de94c9..3b2aa1c 100644
--- a/woodwork/statistics_utils/_calculate_dependence_measure.py
+++ b/woodwork/statistics_utils/_calculate_dependence_measure.py
@@ -2,6 +2,7 @@ import itertools
 import warnings
 
 import numpy as np
+from scipy.stats import spearmanr
 from sklearn.metrics.cluster import adjusted_mutual_info_score
 
 from woodwork.exceptions import SparseDataWarning
@@ -75,6 +76,8 @@ def _calculate_dependence_measure(
                     0,
                     1,
                 ]
+            elif measure == "spearman":
+                score, _ = spearmanr(data[a_col][intersect], data[b_col][intersect])
 
             score = score * num_intersect / num_union
             result[measure] = score
diff --git a/woodwork/statistics_utils/_calculate_max_dependence_for_pair.py b/woodwork/statistics_utils/_calculate_max_dependence_for_pair.py
index ca16be4..93dc5da 100644
--- a/woodwork/statistics_utils/_calculate_max_dependence_for_pair.py
+++ b/woodwork/statistics_utils/_calculate_max_dependence_for_pair.py
@@ -22,10 +22,14 @@ def _calculate_max_dependence_for_pair(result, min_shared, extra_stats):
     """
     # if pearson was not measured, mutual info must be max, since columns valid
     # for pearson are a subset of columns valid for mutual info
-    if "pearson" in result:
+    if "pearson" in result or "spearman" in result:
         score = pd.Series(
-            [result["mutual_info"], abs(result["pearson"])],
-            index=["mutual_info", "pearson"],
+            [
+                result["mutual_info"],
+                abs(result.get("pearson", np.nan)),
+                abs(result.get("spearman", np.nan)),
+            ],
+            index=["mutual_info", "pearson", "spearman"],
         )
         # to keep sign, get name of max score and use original value
         measure_used = score.idxmax()
diff --git a/woodwork/statistics_utils/_convert_ordinal_to_numeric.py b/woodwork/statistics_utils/_convert_ordinal_to_numeric.py
new file mode 100644
index 0000000..4fa1818
--- /dev/null
+++ b/woodwork/statistics_utils/_convert_ordinal_to_numeric.py
@@ -0,0 +1,15 @@
+def _convert_ordinal_to_numeric(schema, data):
+    """Transforms ordinal data to numeric data so that dependence can be calculated.
+
+    Args:
+        schema (woodwork.TableSchema): Woodwork typing info for the data.
+        data (dict[pd.Series]): Dictionary of Pandas series to use for
+            calculating dependence.
+
+    Returns:
+        None
+    """
+    columns = [k for k, v in schema.logical_types.items() if "Ordinal" in str(v)]
+    for col_name in columns:
+        order = list(schema.columns[col_name].logical_type.order)
+        data[col_name] = data[col_name].apply(lambda x: order.index(x))
diff --git a/woodwork/statistics_utils/_get_dependence_dict.py b/woodwork/statistics_utils/_get_dependence_dict.py
index 670c4f5..2037d9a 100644
--- a/woodwork/statistics_utils/_get_dependence_dict.py
+++ b/woodwork/statistics_utils/_get_dependence_dict.py
@@ -17,8 +17,16 @@ from woodwork.statistics_utils._calculate_max_dependence_for_pair import (
 from woodwork.statistics_utils._cast_nullable_int_and_datetime_to_int import (
     _cast_nullable_int_and_datetime_to_int,
 )
+from woodwork.statistics_utils._convert_ordinal_to_numeric import (
+    _convert_ordinal_to_numeric,
+)
 from woodwork.statistics_utils._parse_measures import _parse_measures
-from woodwork.utils import CallbackCaller, get_valid_mi_types, get_valid_pearson_types
+from woodwork.utils import (
+    CallbackCaller,
+    get_valid_mi_types,
+    get_valid_pearson_types,
+    get_valid_spearman_types,
+)
 
 
 def _get_dependence_dict(
@@ -47,8 +55,9 @@ def _get_dependence_dict(
 
                 - "pearson": calculates the Pearson correlation coefficient
                 - "mutual_info": calculates the mutual information between columns
-                - "max":  max(abs(pearson), mutual) for each pair of columns
-                - "all": includes columns for "pearson", "mutual_info", and "max"
+                - "spearman": calculates the Spearman corerlation coefficient
+                - "max":  max(abs(pearson), mutual, abs(spearman)) for each pair of columns
+                - "all": includes columns for "pearson", "mutual_info", "spearman", and "max"
         num_bins (int): Determines number of bins to use for converting numeric
             features into categorical.  Default to 10. Pearson calculation does
             not use binning.
@@ -77,8 +86,9 @@ def _get_dependence_dict(
             non-negative. Defaults to 25.
         random_seed (int): Seed for the random number generator. Defaults to 0.
         max_nunique (int): The total maximum number of unique values for all large categorical columns (> 800 unique values).
-                Categorical columns will be dropped until this number is met or until there is only one large categorical column.
-                Defaults to 6000.
+            Categorical columns will be dropped until this number is met or until there is only one large categorical column.
+            Defaults to 6000.
+
     Returns:
         list(dict): A list containing dictionaries that have keys `column_1`,
         `column_2`, and keys for the specified dependence measures. The list is
@@ -98,6 +108,11 @@ def _get_dependence_dict(
         pearson_types = get_valid_pearson_types()
         pearson_columns = _get_valid_columns(dataframe, pearson_types)
         valid_columns = pearson_columns
+    if "spearman" in calc_order:
+        spearman_types = get_valid_spearman_types()
+        spearman_columns = _get_valid_columns(dataframe, spearman_types)
+        # pearson columns are a subset of spearman columns
+        valid_columns = spearman_columns
     if "mutual_info" in calc_order:
         mi_types = get_valid_mi_types()
         cols_to_drop = _find_large_categorical_columns(dataframe, max_nunique)
@@ -113,7 +128,7 @@ def _get_dependence_dict(
             for col in _get_valid_columns(dataframe, mi_types)
             if col not in cols_to_drop
         ]
-        # pearson columns are a subset of mutual columns
+        # pearson/spearman columns are a subset of mutual columns
         valid_columns = mutual_columns
 
     index = dataframe.ww.index
@@ -137,20 +152,26 @@ def _get_dependence_dict(
 
     p = 0  # number of pearson columns
     m = 0  # number of mutual columns
+    s = 0  # number of spearman columns
     if "pearson" in calc_order:
         pearson_columns = [col for col in pearson_columns if col in not_null_col_set]
         p = len(pearson_columns)
+    if "spearman" in calc_order:
+        spearman_columns = [col for col in spearman_columns if col in not_null_col_set]
+        s = len(spearman_columns)
     if "mutual_info" in calc_order:
         mutual_columns = [col for col in mutual_columns if col in not_null_col_set]
         m = len(mutual_columns)
-    n = max(m, p)
+    n = max(m, p, s)
 
     # combinations in a loop is n! / 2 / (n - 2)! which reduces to (n) (n - 1) / 2
     def _num_calc_steps(n):
         return (n * n - n) / 2
 
     # Assume 1 unit for preprocessing, n for handling nulls, m for binning numerics
-    total_loops = 1 + n + m + _num_calc_steps(p) + _num_calc_steps(m)
+    total_loops = (
+        1 + n + m + s + _num_calc_steps(p) + _num_calc_steps(m) + _num_calc_steps(s)
+    )
     callback_caller = CallbackCaller(callback, unit, total_loops, start_time=start_time)
     callback_caller.update(1)
 
@@ -170,6 +191,9 @@ def _get_dependence_dict(
             col_names = mutual_columns
         elif measure == "pearson":
             col_names = pearson_columns
+        elif measure == "spearman":
+            _convert_ordinal_to_numeric(dataframe.ww.schema, data)
+            col_names = spearman_columns
 
         _calculate_dependence_measure(
             measure=measure,
@@ -193,6 +217,8 @@ def _get_dependence_dict(
                 del result["mutual_info"]
                 if "pearson" in result:
                     del result["pearson"]
+                if "spearman" in result:
+                    del result["spearman"]
 
         # Remove cached info not expected in result by user
         if "num_union" in result:
diff --git a/woodwork/statistics_utils/_parse_measures.py b/woodwork/statistics_utils/_parse_measures.py
index 0b16582..1eeb1fc 100644
--- a/woodwork/statistics_utils/_parse_measures.py
+++ b/woodwork/statistics_utils/_parse_measures.py
@@ -17,6 +17,7 @@ def _parse_measures(measures):
 
     calc_pearson = False
     calc_mutual = False
+    calc_spearman = False
     calc_max = False
     calc_order = []
     for measure in measures:
@@ -27,24 +28,30 @@ def _parse_measures(measures):
                         "additional measures to 'all' measure found; 'all' should be used alone",
                     ),
                 )
-            measures = ["max", "pearson", "mutual_info"]
+            measures = ["max", "pearson", "spearman", "mutual_info"]
             calc_pearson = True
             calc_mutual = True
+            calc_spearman = True
             calc_max = True
             break
         elif measure == "pearson":
             calc_pearson = True
         elif measure == "mutual_info":
             calc_mutual = True
+        elif measure == "spearman":
+            calc_spearman = True
         elif measure == "max":
             calc_pearson = True
             calc_mutual = True
+            calc_spearman = True
             calc_max = True
         else:
             raise ValueError("Unrecognized dependence measure %s" % measure)
 
     if calc_pearson:
         calc_order.append("pearson")
+    if calc_spearman:
+        calc_order.append("spearman")
     if calc_mutual:
         calc_order.append("mutual_info")
 
diff --git a/woodwork/table_accessor.py b/woodwork/table_accessor.py
index 77c79ca..a09f86f 100644
--- a/woodwork/table_accessor.py
+++ b/woodwork/table_accessor.py
@@ -940,6 +940,7 @@ class WoodworkTableAccessor:
             max_nunique (int): The total maximum number of unique values for all large categorical columns (> 800 unique values).
                 Categorical columns will be dropped until this number is met or until there is only one large categorical column.
                 Defaults to 6000.
+
         Returns:
             list(dict): A list containing dictionaries that have keys `column_1`,
             `column_2`, and `mutual_info` that is sorted in decending order by mutual info.
@@ -1001,6 +1002,7 @@ class WoodworkTableAccessor:
             max_nunique (int): The total maximum number of unique values for all large categorical columns (> 800 unique values).
                 Categorical columns will be dropped until this number is met or until there is only one large categorical column.
                 Defaults to 6000.
+
         Returns:
             pd.DataFrame: A DataFrame containing mutual information with columns `column_1`,
             `column_2`, and `mutual_info` that is sorted in decending order by mutual info.
@@ -1056,6 +1058,7 @@ class WoodworkTableAccessor:
                 to measure accurately and will return a NaN value. Must be
                 non-negative. Defaults to 25.
             random_seed (int): Seed for the random number generator. Defaults to 0.
+
         Returns:
             list(dict): A list containing dictionaries that have keys `column_1`,
             `column_2`, and `pearson` that is sorted in decending order by correlation coefficient.
@@ -1107,6 +1110,7 @@ class WoodworkTableAccessor:
                 to measure accurately and will return a NaN value. Must be
                 non-negative. Defaults to 25.
             random_seed (int): Seed for the random number generator. Defaults to 0.
+
         Returns:
             pd.DataFrame: A DataFrame containing Pearson correlation coefficients with columns `column_1`,
             `column_2`, and `pearson` that is sorted in decending order by correlation value.
@@ -1122,6 +1126,111 @@ class WoodworkTableAccessor:
         )
         return pd.DataFrame(pearson_dict)
 
+    @_check_table_schema
+    def spearman_correlation_dict(
+        self,
+        nrows=None,
+        include_index=False,
+        callback=None,
+        extra_stats=False,
+        min_shared=25,
+        random_seed=0,
+    ):
+        """
+        Calculates Spearman correlation coefficient between all pairs of columns in the DataFrame that
+        support correlation. Works with numeric, ordinal, and datetime data. Call woodwork.utils.get_valid_spearman_types to
+        see which Logical Types are supported.
+
+        Args:
+            nrows (int): The number of rows to sample for when determining correlation.
+                If specified, samples the desired number of rows from the data.
+                Defaults to using all rows.
+            include_index (bool): If True, the column specified as the index will be
+                included as long as its LogicalType is valid for correlation calculations.
+                If False, the index column will not have the Spearman correlation calculated for it.
+                Defaults to False.
+            callback (callable, optional): Function to be called with incremental updates. Has the following parameters:
+                - update (int): change in progress since last call
+                - progress (int): the progress so far in the calculations
+                - total (int): the total number of calculations to do
+                - unit (str): unit of measurement for progress/total
+                - time_elapsed (float): total time in seconds elapsed since start of call
+            extra_stats (bool):  If True, additional column "shared_rows"
+                recording the number of shared non-null rows for a column
+                pair will be included with the dataframe. Defaults to False.
+            min_shared (int): The number of shared non-null rows needed to
+                calculate. Less rows than this will be considered too sparse
+                to measure accurately and will return a NaN value. Must be
+                non-negative. Defaults to 25.
+            random_seed (int): Seed for the random number generator. Defaults to 0.
+
+        Returns:
+            list(dict): A list containing dictionaries that have keys `column_1`,
+            `column_2`, and `spearman` that is sorted in decending order by correlation coefficient.
+            Correlation coefficient values are between -1 and 1.
+        """
+        return _get_dependence_dict(
+            dataframe=self._dataframe,
+            measures=["spearman"],
+            nrows=nrows,
+            include_index=include_index,
+            callback=callback,
+            extra_stats=extra_stats,
+            min_shared=min_shared,
+            random_seed=random_seed,
+        )
+
+    def spearman_correlation(
+        self,
+        nrows=None,
+        include_index=False,
+        callback=None,
+        extra_stats=False,
+        min_shared=25,
+        random_seed=0,
+    ):
+        """Calculates Spearman correlation coefficient between all pairs of columns in the DataFrame that
+        support correlation. Works with numeric, ordinal, and datetime data. Call woodwork.utils.get_valid_spearman_types to
+        see which Logical Types are supported.
+
+        Args:
+            nrows (int): The number of rows to sample for when determining correlation.
+                If specified, samples the desired number of rows from the data.
+                Defaults to using all rows.
+            include_index (bool): If True, the column specified as the index will be
+                included as long as its LogicalType is valid for correlation calculations.
+                If False, the index column will not have the Spearman correlation calculated for it.
+                Defaults to False.
+            callback (callable, optional): Function to be called with incremental updates. Has the following parameters:
+                - update (int): change in progress since last call
+                - progress (int): the progress so far in the calculations
+                - total (int): the total number of calculations to do
+                - unit (str): unit of measurement for progress/total
+                - time_elapsed (float): total time in seconds elapsed since start of call
+            extra_stats (bool):  If True, additional column "shared_rows"
+                recording the number of shared non-null rows for a column
+                pair will be included with the dataframe. Defaults to False.
+            min_shared (int): The number of shared non-null rows needed to
+                calculate. Less rows than this will be considered too sparse
+                to measure accurately and will return a NaN value. Must be
+                non-negative. Defaults to 25.
+            random_seed (int): Seed for the random number generator. Defaults to 0.
+
+        Returns:
+            pd.DataFrame: A DataFrame containing Spearman correlation coefficients with columns `column_1`,
+            `column_2`, and `spearman` that is sorted in decending order by correlation value.
+            Spearman values are between -1 and 1, with 0 meaning no correlation.
+        """
+        spearman_dict = self.spearman_correlation_dict(
+            nrows=nrows,
+            include_index=include_index,
+            callback=callback,
+            extra_stats=extra_stats,
+            min_shared=min_shared,
+            random_seed=random_seed,
+        )
+        return pd.DataFrame(spearman_dict)
+
     @_check_table_schema
     def dependence_dict(
         self,
@@ -1149,8 +1258,9 @@ class WoodworkTableAccessor:
 
                 - "pearson": calculates the Pearson correlation coefficient
                 - "mutual_info": calculates the mutual information between columns
-                - "max":  max(abs(pearson), mutual) for each pair of columns
-                - "all": includes columns for "pearson", "mutual_info", and "max"
+                - "spearman": calculates the Spearman corerlation coefficient
+                - "max":  max(abs(pearson), abs(spearman), mutual) for each pair of columns
+                - "all": includes columns for "pearson", "mutual_info", "spearman", and "max"
             num_bins (int): Determines number of bins to use for converting
                 numeric features into categorical. Defaults to 10. Pearson
                 calculation does not use binning.
@@ -1181,12 +1291,13 @@ class WoodworkTableAccessor:
             max_nunique (int): The total maximum number of unique values for all large categorical columns (> 800 unique values).
                 Categorical columns will be dropped until this number is met or until there is only one large categorical column.
                 Defaults to 6000.
+
         Returns:
             list(dict): A list containing dictionaries that have keys `column_1`,
             `column_2`, and keys for the specified dependence measures. The list is
             sorted in decending order by the first specified measure.
             Dependence information values are between 0 (no dependence) and 1
-            (perfect dependency). For Pearson, values range from -1 to 1 but 0 is
+            (perfect dependency). For Pearson and Spearman, values range from -1 to 1 but 0 is
             still no dependence.
         """
         return _get_dependence_dict(
@@ -1228,8 +1339,9 @@ class WoodworkTableAccessor:
 
                 - "pearson": calculates the Pearson correlation coefficient
                 - "mutual_info": calculates the mutual information between columns
-                - "max":  max(abs(pearson), mutual) for each pair of columns
-                - "all": includes columns for "pearson", "mutual_info", and "max"
+                - "spearman": calculates the Spearman correlation coefficient
+                - "max":  max(abs(pearson), abs(spearman), mutual) for each pair of columns
+                - "all": includes columns for "pearson", "mutual_info", "spearman", and "max"
             num_bins (int): Determines number of bins to use for converting
                 numeric features into categorical. Defaults to 10. Pearson
                 calculation does not use binning.
@@ -1260,12 +1372,13 @@ class WoodworkTableAccessor:
             max_nunique (int): The maximum number of unique values for large categorical columns (> 800 unique values).
                 Categorical columns will be dropped until this number is met or until there is only one large categorical column.
                 Defaults to 6000.
+
         Returns:
             pd.DataFrame: A DataFrame with the columns `column_1`,
             `column_2`, and keys for the specified dependence measures. The rows
             are sorted in decending order by the first specified measure.
             Dependence information values are between 0 (no dependence) and 1
-            (perfect dependency). For Pearson, values range from -1 to 1 but 0 is
+            (perfect dependency). For Pearson and Spearman, values range from -1 to 1 but 0 is
             still no dependence.  Additional columns will be included if the
             `extra_stats` is True.
         """
@@ -1609,16 +1722,17 @@ def _check_ignore_columns(dataframe_columns, logical_types, schema, ignore_colum
         )
     if logical_types:
         col_ignored_and_set = set(logical_types.keys()).intersection(
-            set(ignore_columns)
+            set(ignore_columns),
         )
         if col_ignored_and_set:
             raise ColumnBothIgnoredAndSetError(
                 "ignore_columns contains columns that are being set "
-                f"in logical_types: {list(col_ignored_and_set)}"
+                f"in logical_types: {list(col_ignored_and_set)}",
             )
     if schema is None:
         raise WoodworkNotInitError(
-            "ignore_columns cannot be set when the dataframe has no existing " "schema."
+            "ignore_columns cannot be set when the dataframe has no existing "
+            "schema.",
         )
 
 
diff --git a/woodwork/utils.py b/woodwork/utils.py
index 944c355..4cbe7f4 100644
--- a/woodwork/utils.py
+++ b/woodwork/utils.py
@@ -389,6 +389,28 @@ def get_valid_pearson_types():
     return valid_types
 
 
+def get_valid_spearman_types():
+    """
+    Generate a list of LogicalTypes that are valid for calculating Spearman correlation. Note that
+    index columns are not valid for calculating dependence, but their types may be
+    returned by this function.
+
+    Args:
+        None
+
+    Returns:
+        list(LogicalType): A list of the LogicalTypes that can be use to calculate Spearman correlation
+    """
+    valid_types = []
+    for ltype in ww.type_system.registered_types:
+        if "numeric" in ltype.standard_tags:
+            valid_types.append(ltype)
+        elif ltype == ww.logical_types.Datetime or ltype == ww.logical_types.Ordinal:
+            valid_types.append(ltype)
+
+    return valid_types
+
+
 def _get_column_logical_type(series, logical_type, name):
     if logical_type:
         return _parse_logical_type(logical_type, name)


Test patch:
diff --git a/woodwork/tests/accessor/test_statistics.py b/woodwork/tests/accessor/test_statistics.py
index eef28b5..5ce95ef 100644
--- a/woodwork/tests/accessor/test_statistics.py
+++ b/woodwork/tests/accessor/test_statistics.py
@@ -38,6 +38,7 @@ from woodwork.logical_types import (
 )
 from woodwork.statistics_utils import (
     _bin_numeric_cols_into_categories,
+    _convert_ordinal_to_numeric,
     _get_describe_dict,
     _get_histogram_values,
     _get_mode,
@@ -123,29 +124,33 @@ def test_dependence_same(df_same_mi, measure):
         _check_close(actual, 1.0)
 
 
-@pytest.mark.parametrize("measure", ["mutual_info", "pearson", "max", "all"])
+@pytest.mark.parametrize(
+    "measure",
+    ["mutual_info", "pearson", "spearman", "max", "all"],
+)
 def test_dependence(df_mi, measure):
     df_mi.ww.init(logical_types={"dates": Datetime(datetime_format="%Y-%m-%d")})
     original_df = df_mi.copy()
     dep_df = df_mi.ww.dependence(measures=measure, min_shared=12)
-    if measure == "pearson":
+    if measure == "pearson" or measure == "spearman":
         assert dep_df.shape[0] == 3
     else:
         assert dep_df.shape[0] == 15
 
     if measure == "all":
-        measure_columns = ["mutual_info", "max", "pearson"]
+        measure_columns = ["mutual_info", "max", "pearson", "spearman"]
     else:
         measure_columns = [measure]
     assert dep_df.columns.tolist() == ["column_1", "column_2"] + measure_columns
 
-    if measure == "pearson":
-        expected_df = pd.DataFrame(data={"pearson": [0.5]}, index=["dates_ints"])
+    if measure == "pearson" or measure == "spearman":
+        expected_df = pd.DataFrame(data={measure: [0.5]}, index=["dates_ints"])
     else:
         expected_df = pd.DataFrame(
             data={
                 "mutual_info": [1.0, 0.0, 0, 0.208, 0.208],
                 "pearson": [np.nan, np.nan, np.nan, 0.5, np.nan],
+                "spearman": [np.nan, np.nan, np.nan, 0.5, np.nan],
                 "max": [1.0, 0.0, 0, 0.5, 0.208],
             },
             index=[
@@ -424,10 +429,11 @@ def test_dependence_min_shared_warns(time_index_df, measure):
 @pytest.mark.parametrize(
     "measure, expected",
     [
-        ("mutual_info", (18, 7, 28)),
-        ("pearson", (5, 4, 7)),
-        ("max", (21, 7, 31)),
-        ("all", (21, 7, 31)),
+        ("mutual_info", (18, 7, 28, 28)),
+        ("pearson", (5, 4, 7, 7)),
+        ("spearman", (5, 4, 10, 7)),
+        ("max", (24, 7, 37, 34)),
+        ("all", (24, 7, 37, 34)),
     ],
 )
 def test_dependence_callback(df_mi, measure, expected, mock_callback):
@@ -435,7 +441,7 @@ def test_dependence_callback(df_mi, measure, expected, mock_callback):
 
     df_mi.ww.dependence(measures=measure, callback=mock_callback)
 
-    total_calls, second_call_progress, total_progress = expected
+    total_calls, second_call_progress, total, total_progress = expected
 
     assert len(mock_callback.progress_history) == total_calls
 
@@ -445,13 +451,16 @@ def test_dependence_callback(df_mi, measure, expected, mock_callback):
     assert mock_callback.progress_history[1] == second_call_progress
 
     # Should be 26 calculations at end with a positive elapsed time
-    assert mock_callback.total == total_progress
+    assert mock_callback.total == total
     assert mock_callback.total_update == total_progress
     assert mock_callback.progress_history[-1] == total_progress
     assert mock_callback.total_elapsed_time > 0
 
 
-@pytest.mark.parametrize("measure", ["mutual_info", "pearson", "max", "all"])
+@pytest.mark.parametrize(
+    "measure",
+    ["mutual_info", "pearson", "spearman", "max", "all"],
+)
 def test_dependence_with_string_index(measure):
     df = pd.DataFrame(
         {
@@ -731,6 +740,52 @@ def test_mutual(df_mi, mock_callback):
     )
 
 
+@patch("woodwork.table_accessor._get_dependence_dict")
+def test_spearman_dict(_get_dependence_dict, df_mi, mock_callback):
+    df_mi.ww.init()
+    df_mi.ww.spearman_correlation_dict(
+        nrows=100,
+        include_index=True,
+        callback=mock_callback,
+        extra_stats=True,
+        min_shared=25,
+        random_seed=5,
+    )
+    assert _get_dependence_dict.called
+    _get_dependence_dict.assert_called_with(
+        dataframe=df_mi,
+        measures=["spearman"],
+        nrows=100,
+        include_index=True,
+        callback=mock_callback,
+        extra_stats=True,
+        min_shared=25,
+        random_seed=5,
+    )
+
+
+def test_spearman(df_mi, mock_callback):
+    df_mi.ww.init()
+    with patch.object(df_mi.ww, "spearman_correlation_dict") as mi_dict_method:
+        df_mi.ww.spearman_correlation(
+            nrows=100,
+            include_index=True,
+            callback=mock_callback,
+            extra_stats=True,
+            min_shared=25,
+            random_seed=5,
+        )
+    assert mi_dict_method.called
+    mi_dict_method.assert_called_with(
+        nrows=100,
+        include_index=True,
+        callback=mock_callback,
+        extra_stats=True,
+        min_shared=25,
+        random_seed=5,
+    )
+
+
 def test_get_valid_mi_columns(df_mi):
     df_mi.ww.init()
     valid_columns = df_mi.ww.get_valid_mi_columns()
@@ -1924,23 +1979,51 @@ def test_infer_temporal_frequencies_errors(datetime_freqs_df_pandas):
     [
         ("pearson", (["pearson"], ["pearson"], False)),
         (["pearson"], (["pearson"], ["pearson"], False)),
+        ("spearman", (["spearman"], ["spearman"], False)),
+        (["spearman"], (["spearman"], ["spearman"], False)),
         ("mutual_info", (["mutual_info"], ["mutual_info"], False)),
         (["mutual_info"], (["mutual_info"], ["mutual_info"], False)),
-        ("max", (["max"], ["pearson", "mutual_info"], True)),
-        (["max"], (["max"], ["pearson", "mutual_info"], True)),
-        ("all", (["max", "pearson", "mutual_info"], ["pearson", "mutual_info"], True)),
+        ("max", (["max"], ["pearson", "spearman", "mutual_info"], True)),
+        (["max"], (["max"], ["pearson", "spearman", "mutual_info"], True)),
+        (
+            "all",
+            (
+                ["max", "pearson", "spearman", "mutual_info"],
+                ["pearson", "spearman", "mutual_info"],
+                True,
+            ),
+        ),
         (
             ["all"],
-            (["max", "pearson", "mutual_info"], ["pearson", "mutual_info"], True),
+            (
+                ["max", "pearson", "spearman", "mutual_info"],
+                ["pearson", "spearman", "mutual_info"],
+                True,
+            ),
         ),
         (
             ["mutual_info", "pearson"],
             (["mutual_info", "pearson"], ["pearson", "mutual_info"], False),
         ),
-        (["pearson", "max"], (["pearson", "max"], ["pearson", "mutual_info"], True)),
+        (
+            ["pearson", "max"],
+            (["pearson", "max"], ["pearson", "spearman", "mutual_info"], True),
+        ),
         (
             ["max", "pearson", "mutual_info"],
-            (["max", "pearson", "mutual_info"], ["pearson", "mutual_info"], True),
+            (
+                ["max", "pearson", "mutual_info"],
+                ["pearson", "spearman", "mutual_info"],
+                True,
+            ),
+        ),
+        (
+            ["pearson", "spearman"],
+            (["pearson", "spearman"], ["pearson", "spearman"], False),
+        ),
+        (
+            ["spearman", "max"],
+            (["spearman", "max"], ["pearson", "spearman", "mutual_info"], True),
         ),
     ],
 )
@@ -1955,8 +2038,8 @@ def test_parse_measures_warns():
     warning = "additional measures to 'all' measure found; 'all' should be used alone"
     with pytest.warns(ParametersIgnoredWarning, match=warning):
         _measures, _calc_order, _calc_max = _parse_measures(["pearson", "all"])
-    assert _measures == ["max", "pearson", "mutual_info"]
-    assert _calc_order == ["pearson", "mutual_info"]
+    assert _measures == ["max", "pearson", "spearman", "mutual_info"]
+    assert _calc_order == ["pearson", "spearman", "mutual_info"]
     assert _calc_max
 
 
@@ -1978,6 +2061,46 @@ def test_parse_measures_bad_string():
         _parse_measures(["mutual_info", "ruler"])
 
 
+@pytest.mark.parametrize("use_ordinal", [True, False])
+def test_spearman_ordinal(df_mi, use_ordinal):
+    if use_ordinal:
+        df_mi.ww.init(logical_types={"strs2": Ordinal(order=["hi", "bye"])})
+    else:
+        df_mi.ww.init()
+    sp = df_mi.ww.dependence(measures=["spearman"])
+    valid_sp_columns = (sp.column_1.append(sp.column_2)).unique()
+    assert "strs" not in valid_sp_columns
+    if use_ordinal:
+        assert "strs2" in valid_sp_columns
+        return
+    assert "strs2" not in valid_sp_columns
+
+
+def test_convert_ordinal_to_numeric():
+    df = pd.DataFrame(
+        {
+            "ints1": pd.Series([1, 2, 3, 2]),
+            "ints2": pd.Series([1, 100, 1, 100]),
+            "strs": pd.Series(["hi", "hi", "hi", "hi"]),
+            "strs2": pd.Series(["bye", "hi", "bye", "bye"]),
+            "bools": pd.Series([True, False, True, False]),
+            "categories": pd.Series(["test", "test2", "test2", "test"]),
+            "dates": pd.Series(
+                ["2020-01-01", "2019-01-02", "2020-08-03", "1997-01-04"],
+            ),
+        },
+    )
+    df.ww.init(logical_types={"strs2": Ordinal(order=["hi", "bye"])})
+    data = {column: df[column] for column in df.copy()}
+    result = [0 if x == "hi" else 1 for x in data["strs2"].values]
+    _convert_ordinal_to_numeric(df.ww.schema, data)
+    for cols in df.columns:
+        if cols != "strs2":
+            assert all(data[cols] == df[cols])
+        else:
+            assert all(data["strs2"].values == result)
+
+
 def test_box_plot_ignore_zeros():
     zeros_df = pd.Series(list(range(1, 100)) + [0] * 100)
     no_zeros_df = pd.Series(range(1, 100))
diff --git a/woodwork/tests/schema/test_column_schema.py b/woodwork/tests/schema/test_column_schema.py
index ea1d980..d22b6ea 100644
--- a/woodwork/tests/schema/test_column_schema.py
+++ b/woodwork/tests/schema/test_column_schema.py
@@ -245,6 +245,26 @@ def test_is_datetime():
     assert not empty_column.is_datetime
 
 
+def test_is_ordinal():
+    ordinal_column = ColumnSchema(logical_type=Ordinal)
+    assert ordinal_column.is_ordinal
+
+    instantiated_ordinal_column = ColumnSchema(logical_type=Ordinal(order=[]))
+    assert instantiated_ordinal_column.is_ordinal
+
+    ordered_ordinal_column = ColumnSchema(logical_type=Ordinal(order=[""]))
+    assert ordered_ordinal_column.is_ordinal
+
+    datetime_column = ColumnSchema(logical_type=Datetime())
+    assert not datetime_column.is_ordinal
+
+    double_column = ColumnSchema(logical_type=Double)
+    assert not double_column.is_ordinal
+
+    empty_column = ColumnSchema()
+    assert not empty_column.is_ordinal
+
+
 def test_set_semantic_tags_no_standard_tags():
     semantic_tags = {"tag1", "tag2"}
     schema = ColumnSchema(logical_type=Categorical, semantic_tags=semantic_tags)