Repository: alteryx/woodwork
Pull request #1566 (2022-11-03 07:57:33)

Change sampling for type inference to random

codecov[bot]: # [Codecov](https://codecov.io/gh/alteryx/woodwork/pull/1566?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx) Report
> Merging [#1566](https://codecov.io/gh/alteryx/woodwork/pull/1566?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx) (771501b) into [main](https://codecov.io/gh/alteryx/woodwork/commit/363271374a046bbe57db814ba3944794e9cfc90d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx) (3632713) will **decrease** coverage by `81.79%`.
> The diff coverage is `0.00%`.

```diff
@@             Coverage Diff             @@
##             main    #1566       +/-   ##
===========================================
- Coverage   98.92%   17.13%   -81.80%     
===========================================
  Files          91       91               
  Lines       10891    10927       +36     
===========================================
- Hits        10774     1872     -8902     
- Misses        117     9055     +8938     
```


| [Impacted Files](https://codecov.io/gh/alteryx/woodwork/pull/1566?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx) | Coverage Δ | |
|---|---|---|
| [woodwork/tests/type\_system/conftest.py](https://codecov.io/gh/alteryx/woodwork/pull/1566/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvdHlwZV9zeXN0ZW0vY29uZnRlc3QucHk=) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [woodwork/tests/type\_system/test\_ltype\_inference.py](https://codecov.io/gh/alteryx/woodwork/pull/1566/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvdHlwZV9zeXN0ZW0vdGVzdF9sdHlwZV9pbmZlcmVuY2UucHk=) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [woodwork/type\_sys/type\_system.py](https://codecov.io/gh/alteryx/woodwork/pull/1566/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdHlwZV9zeXMvdHlwZV9zeXN0ZW0ucHk=) | `29.55% <0.00%> (-70.45%)` | :arrow_down: |
| [woodwork/tests/test\_config.py](https://codecov.io/gh/alteryx/woodwork/pull/1566/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvdGVzdF9jb25maWcucHk=) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [woodwork/tests/test\_version.py](https://codecov.io/gh/alteryx/woodwork/pull/1566/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvdGVzdF92ZXJzaW9uLnB5) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [woodwork/tests/utils/test\_utils.py](https://codecov.io/gh/alteryx/woodwork/pull/1566/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvdXRpbHMvdGVzdF91dGlscy5weQ==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [woodwork/tests/utils/test\_concat.py](https://codecov.io/gh/alteryx/woodwork/pull/1566/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvdXRpbHMvdGVzdF9jb25jYXQucHk=) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [woodwork/tests/utils/test\_read\_file.py](https://codecov.io/gh/alteryx/woodwork/pull/1566/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvdXRpbHMvdGVzdF9yZWFkX2ZpbGUucHk=) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [woodwork/tests/accessor/test\_indexers.py](https://codecov.io/gh/alteryx/woodwork/pull/1566/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvYWNjZXNzb3IvdGVzdF9pbmRleGVycy5weQ==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [woodwork/tests/demo\_tests/test\_retail.py](https://codecov.io/gh/alteryx/woodwork/pull/1566/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alteryx#diff-d29vZHdvcmsvdGVzdHMvZGVtb190ZXN0cy90ZXN0X3JldGFpbC5weQ==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| ... and [58 more](https://codecov.io/gh/alteryx/woodwork/pull/1566/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)


Patch:
diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst
index 0231698..db64177 100644
--- a/docs/source/release_notes.rst
+++ b/docs/source/release_notes.rst
@@ -12,6 +12,7 @@ Future Release
         * Fixes error message in validate method in logical_types.py (:pr:`1565`)
     * Changes
         * Unpin dask dependency (:pr:`1561`)
+        * Changed the sampling strategy for type inference from ``head`` to random (:pr:`1566`)
     * Documentation Changes
     * Testing Changes
 
diff --git a/woodwork/type_sys/type_system.py b/woodwork/type_sys/type_system.py
index 0d13c06..86f9e83 100644
--- a/woodwork/type_sys/type_system.py
+++ b/woodwork/type_sys/type_system.py
@@ -290,17 +290,34 @@ class TypeSystem(object):
         Args:
             series (pandas.Series): The series for which to infer the LogicalType.
         """
+
+        def get_random_sample(series_, **kwargs):
+            if len(series_) > INFERENCE_SAMPLE_SIZE:
+                sampled_series = series_.sample(**kwargs)
+                return sampled_series
+            else:
+                return series_
+
+        kw_args_sampling = {
+            "replace": False,
+            "random_state": 42,
+        }
         if isinstance(series, pd.Series):
             # Special case for series with no valid values
             if series.count() == 0:
                 return Unknown()
-
-            series = series.head(INFERENCE_SAMPLE_SIZE)
+            kw_args_sampling["n"] = INFERENCE_SAMPLE_SIZE
+            series = get_random_sample(series, **kw_args_sampling)
         else:
+            # Dask and Spark don't accept the n argument
+            kw_args_sampling["frac"] = INFERENCE_SAMPLE_SIZE / len(series)
             if _is_dask_series(series):
-                series = series.head(INFERENCE_SAMPLE_SIZE, npartitions=-1)
+                series = get_random_sample(
+                    series.head(len(series), npartitions=-1), **kw_args_sampling
+                )
             elif _is_spark_series(series):
-                series = series.head(INFERENCE_SAMPLE_SIZE).to_pandas()
+                series = get_random_sample(series, **kw_args_sampling)
+                series = series.to_pandas()
             else:
                 raise ValueError(
                     f"Unsupported series type `{type(series)}`",


Test patch:
diff --git a/woodwork/tests/type_system/conftest.py b/woodwork/tests/type_system/conftest.py
index 0bf2d6c..e0df680 100644
--- a/woodwork/tests/type_system/conftest.py
+++ b/woodwork/tests/type_system/conftest.py
@@ -9,7 +9,7 @@ from woodwork.type_sys.inference_functions import (
     double_func,
     integer_func,
 )
-from woodwork.type_sys.type_system import TypeSystem
+from woodwork.type_sys.type_system import INFERENCE_SAMPLE_SIZE, TypeSystem
 
 
 # Integer Inference Fixtures
@@ -359,6 +359,31 @@ def nulls(request):
     return request.getfixturevalue(request.param)
 
 
+@pytest.fixture
+def pandas_large_df():
+    df = pd.DataFrame()
+    df["int_nullable"] = [int(i) for i in range(INFERENCE_SAMPLE_SIZE)] + [np.nan]
+    df["bool_nullable"] = [True, False] * int(INFERENCE_SAMPLE_SIZE // 2) + [pd.NA]
+    df["floats"] = [int(i) for i in range(INFERENCE_SAMPLE_SIZE)] + [1.2]
+    df["constant"] = [None] * (INFERENCE_SAMPLE_SIZE + 1)
+    return df
+
+
+@pytest.fixture
+def dask_large_df(pandas_large_df):
+    return pd_to_dask(pandas_large_df)
+
+
+@pytest.fixture
+def spark_large_df(pandas_large_df):
+    return pd_to_spark(pandas_large_df)
+
+
+@pytest.fixture(params=["pandas_large_df", "dask_large_df", "spark_large_df"])
+def large_df(request):
+    return request.getfixturevalue(request.param)
+
+
 @pytest.fixture
 def default_inference_functions():
     return {
diff --git a/woodwork/tests/type_system/test_ltype_inference.py b/woodwork/tests/type_system/test_ltype_inference.py
index 3050d25..414907a 100644
--- a/woodwork/tests/type_system/test_ltype_inference.py
+++ b/woodwork/tests/type_system/test_ltype_inference.py
@@ -283,3 +283,15 @@ def test_updated_ltype_inference(integers, type_sys):
             inferred_type = type_sys.infer_logical_type(series.astype(dtype))
             assert isinstance(inferred_type, Integer)
             assert inferred_type.primary_dtype == "string"
+
+
+def test_inference_randomly_sampled(large_df, type_sys):
+    large_df.ww.init()
+
+    inferred_type = large_df.ww.logical_types["int_nullable"]
+    assert isinstance(inferred_type, IntegerNullable)
+    inferred_type = large_df.ww.logical_types["bool_nullable"]
+    assert isinstance(inferred_type, BooleanNullable)
+    inferred_type = large_df.ww.logical_types["floats"]
+    assert isinstance(inferred_type, Double)
+    inferred_type = large_df.ww.logical_types["constant"]