Repository: altair-viz/altair
Pull request #1611 (2019-07-14 20:44:10)

BUG: make infer_dtype utility work with pandas 0.20
Currently Altair's type inference fails with pandas version 0.20.X (it works with older or newer versions)

Confirmed that this fix works with all pandas versions back to at least 0.14.X (and probably further, but I didn't check): https://colab.research.google.com/drive/1SaigNiN-W8DY54zzKoou0cPc5XvjxMSt#scrollTo=UyS-QGDzIQCL

Replaces #1610


Patch:
diff --git a/altair/utils/core.py b/altair/utils/core.py
index 0e440a37..c1deed47 100644
--- a/altair/utils/core.py
+++ b/altair/utils/core.py
@@ -14,16 +14,33 @@ import six
 import pandas as pd
 import numpy as np
 
+from .schemapi import SchemaBase, Undefined
+
 try:
-    from pandas.api.types import infer_dtype
-    # keywords required; see https://github.com/altair-viz/altair/issues/1314
-    _infer_dtype_kwds = {'skipna': False}
+    from pandas.api.types import infer_dtype as _infer_dtype
 except ImportError:
-    # This is the appropriate import for pandas < 0.20.0
-    from pandas.lib import infer_dtype
-    _infer_dtype_kwds = {}  # no keywords allowed in pandas < 0.20
+    # Import for pandas < 0.20.0
+    from pandas.lib import infer_dtype as _infer_dtype
 
-from .schemapi import SchemaBase, Undefined
+
+def infer_dtype(value):
+    """Infer the dtype of the value.
+
+    This is a compatibility function for pandas infer_dtype,
+    with skipna=False regardless of the pandas version.
+    """
+    if not hasattr(infer_dtype, '_supports_skipna'):
+        try:
+            _ = _infer_dtype([1], skipna=False)
+        except TypeError:
+            # pandas < 0.21.0 don't support skipna keyword
+            infer_dtype._supports_skipna = False
+        else:
+            infer_dtype._supports_skipna = True
+    if infer_dtype._supports_skipna:
+        return _infer_dtype(value, skipna=False)
+    else:
+        return _infer_dtype(value)
 
 
 TYPECODE_MAP = {'ordinal': 'O',
@@ -74,7 +91,7 @@ def infer_vegalite_type(data):
     data: Numpy array or Pandas Series
     """
     # Otherwise, infer based on the dtype of the input
-    typ = infer_dtype(data, **_infer_dtype_kwds)
+    typ = infer_dtype(data)
 
     # TODO: Once this returns 'O', please update test_select_x and test_select_y in test_api.py
 


Test patch:
diff --git a/altair/utils/tests/test_core.py b/altair/utils/tests/test_core.py
index ed94b8b1..bbd0aaab 100644
--- a/altair/utils/tests/test_core.py
+++ b/altair/utils/tests/test_core.py
@@ -1,10 +1,12 @@
 import types
 
+import numpy as np
 import pandas as pd
 import pytest
 
 import altair as alt
 from .. import parse_shorthand, update_nested, infer_encoding_types
+from ..core import infer_dtype
 
 FAKE_CHANNELS_MODULE = '''
 """Fake channels module for utility tests."""
@@ -54,6 +56,16 @@ class StrokeWidthValue(ValueChannel, schemapi.SchemaBase):
     _encoding_name = "strokeWidth"
 '''
 
+@pytest.mark.parametrize('value,expected_type', [
+    ([1, 2, 3], 'integer'),
+    ([1.0, 2.0, 3.0], 'floating'),
+    ([1, 2.0, 3], 'mixed-integer-float'),
+    (['a', 'b', 'c'], 'string'),
+    (['a', 'b', np.nan], 'mixed'),
+])
+def test_infer_dtype(value, expected_type):
+    assert infer_dtype(value) == expected_type
+
 
 def test_parse_shorthand():
     def check(s, **kwargs):