Repository: alteryx/featuretools

Issue:
Infer Variable Types Always Returns Numeric For Input Numeric Fields, Regardless of Number of Unique Values.
https://github.com/alteryx/featuretools/blob/753615c89360910768811680a7a71e08ab83ad79/featuretools/utils/entity_utils.py#L85

This bit of logic creates a sample based on the min of 10,000 and the number of unique values in a column. For example, in the titanic dataset the Pclass column has three unique values. Therefore the sample will have three rows in it. 

Then it gets the unique values in the sample set, which can be any combination of [1, 2, 3]. For example, when I ran this the sample had [2, 3, 3] in it. 

Then it calculates the percent_unique by taking the sample.size (three in this case) and dividing by the number of unique values in the sample set, which can be 1, 2 or 3

**percent_unique will never be less than .05 since the number of elements in the sample set is always greater than the number of unique values in it. The conditional if percent_unique < .05 will always be greater than or equal to 1.0 so the type will always be Numeric.**

This was identified by Roland in the code, and Alan has identified this behavior in the Feature Types tool as well. 

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/alteryx/featuretools/pull/1273?src=pr&el=h1) Report
> Merging [#1273](https://codecov.io/gh/alteryx/featuretools/pull/1273?src=pr&el=desc) (9809d91) into [main](https://codecov.io/gh/alteryx/featuretools/commit/753615c89360910768811680a7a71e08ab83ad79?el=desc) (753615c) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/alteryx/featuretools/pull/1273/graphs/tree.svg?width=650&height=150&src=pr&token=PqDLD96eXJ)](https://codecov.io/gh/alteryx/featuretools/pull/1273?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##             main    #1273   +/-   ##
=======================================
  Coverage   98.34%   98.34%           
=======================================
  Files         134      134           
  Lines       14430    14430           
=======================================
  Hits        14191    14191           
  Misses        239      239           
```


| [Impacted Files](https://codecov.io/gh/alteryx/featuretools/pull/1273?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [featuretools/utils/entity\_utils.py](https://codecov.io/gh/alteryx/featuretools/pull/1273/diff?src=pr&el=tree#diff-ZmVhdHVyZXRvb2xzL3V0aWxzL2VudGl0eV91dGlscy5weQ==) | `98.41% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/alteryx/featuretools/pull/1273?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/alteryx/featuretools/pull/1273?src=pr&el=footer). Last update [753615c...9809d91](https://codecov.io/gh/alteryx/featuretools/pull/1273?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst
index be456dac..14090441 100644
--- a/docs/source/release_notes.rst
+++ b/docs/source/release_notes.rst
@@ -5,6 +5,7 @@ Release Notes
 **Future Release**
     * Enhancements
     * Fixes
+        * Fix logic for inferring variable type from unusual dtype (:pr:`1273`)
     * Changes
         * Move ``query_by_values`` method from ``Entity`` to ``EntitySet`` (:pr:`1251`)
         * Move ``_handle_time`` method from ``Entity`` to ``EntitySet`` (:pr:`1276`)
diff --git a/featuretools/utils/entity_utils.py b/featuretools/utils/entity_utils.py
index 3ffe3090..1ef1f6a7 100644
--- a/featuretools/utils/entity_utils.py
+++ b/featuretools/utils/entity_utils.py
@@ -83,11 +83,10 @@ def infer_variable_types(df, link_vars, variable_types, time_index, secondary_ti
             inferred_type = vtypes.Datetime
 
         elif len(df[variable]):
-            sample = df[variable] \
-                .sample(min(10000, df[variable].nunique(dropna=False)))
-
-            unique = sample.unique()
-            percent_unique = sample.size / len(unique)
+            n = min(10000, len(df[variable]))
+            sample = df[variable].sample(n=n)
+            n_unique = len(sample.unique())
+            percent_unique = n_unique / sample.size
 
             if percent_unique < .05:
                 inferred_type = vtypes.Categorical


Test patch:
diff --git a/featuretools/tests/utils_tests/test_entity_utils.py b/featuretools/tests/utils_tests/test_entity_utils.py
index 3efbb6ab..537218f5 100644
--- a/featuretools/tests/utils_tests/test_entity_utils.py
+++ b/featuretools/tests/utils_tests/test_entity_utils.py
@@ -175,3 +175,37 @@ def test_replace_latlong_nan():
     assert result[0] == values[0]
     assert result[1] == (np.nan, np.nan)
     assert result[2] == values[2]
+
+
+def test_inference_by_sample():
+    period = pd.period_range(start=1970, periods=2, freq='T')
+    duplicate = pd.PeriodIndex([period[0]] * 48)
+    period = period.append(duplicate)
+    df = pd.DataFrame({'id': range(period.size), 'period': period})
+
+    inferred_variable_types = infer_variable_types(
+        df=df,
+        link_vars=["link_var"],
+        variable_types={},
+        time_index=None,
+        secondary_time_index={},
+    )
+
+    vtype = inferred_variable_types['period']
+    info = 'inference by sample must return categorical'
+    assert vtype == vtypes.Categorical, info
+
+    period = pd.period_range(start=1970, periods=5, freq='T')
+    df = pd.DataFrame({'id': range(period.size), 'period': period})
+
+    inferred_variable_types = infer_variable_types(
+        df=df,
+        link_vars=["link_var"],
+        variable_types={},
+        time_index=None,
+        secondary_time_index={},
+    )
+
+    vtype = inferred_variable_types['period']
+    info = 'inference by sample must return numeric'
+    assert vtype == vtypes.Numeric, info