Repository: alteryx/woodwork (python)

Issue:
Handle pd.ExtensionArray inputs into Woodwork
With the DataTables/DataColumns api, we allowed `pd.ExtensionArrays` as inputs so that users wouldn't have to manually convert to a `pd.Series` before creating a DataColumn.

With the Schema/Accessor api, there's no ExtensionArray accessor, so we won't be able to allow creating an initialized column from a ExtensionArray directly. We should determine whether Woodwork should provide a utility function that converts an ExtensionArray to a series object with the accessor already initialized or whether converting from an ExtensionArray to series is left up to the user.

This issue is closely related to #501 and can likely share a common solution. Also, the proposed series creation utility function discussed in #554 could possibly be extended to provide this functionality.


Pull request:
Handle pd.ExtensionArray inputs into Woodwork
- Handle pd.ExtensionArray inputs into Woodwork
- Closes #570 

Patch:
diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst
index 15978f9..e87c99c 100644
--- a/docs/source/release_notes.rst
+++ b/docs/source/release_notes.rst
@@ -10,6 +10,8 @@ Future Release
     * Changes
         * Remove lines in ``LogicalType.transform`` that raise error if dtype conflicts (:pr:`1012`)
         * Add ``infer_datetime_format`` param to speed up ``to_datetime`` calls (:pr:`1016`)
+        * Add support for numpy array inputs to Woodwork (:pr:`1023`)
+        * Add support for pandas.api.extensions.ExtensionArray inputs to Woodwork (:pr:`1026`)
         * The default logical type is now the ``Unknown`` type instead of the ``NaturalLanguage`` type (:pr:`992`)
         * Add support for numpy inputs to Woodwork (:pr:`1023`)
     * Documentation Changes
diff --git a/woodwork/accessor_utils.py b/woodwork/accessor_utils.py
index 68b9429..28b07a2 100644
--- a/woodwork/accessor_utils.py
+++ b/woodwork/accessor_utils.py
@@ -9,12 +9,13 @@ ks = import_or_none('databricks.koalas')
 
 def init_series(series, logical_type=None, semantic_tags=None,
                 use_standard_tags=True, description=None, metadata=None):
-    """Initializes Woodwork typing information for a Series, returning a new Series. The dtype
-    of the returned series will be converted to match the dtype associated with the LogicalType.
+    """Initializes Woodwork typing information for a series, numpy.ndarray or pd.api.extensions.
+    ExtensionArray, returning a new Series. The dtype of the returned series will be converted
+    to match the dtype associated with the LogicalType.
 
     Args:
-        series (pd.Series, dd.Series, or ks.Series): The original series from which to create
-            the Woodwork initialized series.
+        series (pd.Series, dd.Series, ks.Series, numpy.ndarray or pd.api.extensions.ExtensionArray):
+            The original series from which to create the Woodwork initialized series.
         logical_type (LogicalType or str, optional): The logical type that should be assigned
             to the series. If no value is provided, the LogicalType for the series will
             be inferred.
@@ -32,8 +33,8 @@ def init_series(series, logical_type=None, semantic_tags=None,
         Series: A series with Woodwork typing information initialized
     """
     if not _is_series(series):
-        if isinstance(series, np.ndarray) and series.ndim == 1:
-            series = pd.Series(series, dtype=series.dtype)
+        if isinstance(series, (np.ndarray, pd.api.extensions.ExtensionArray)) and series.ndim == 1:
+            series = pd.Series(series)
         elif isinstance(series, np.ndarray) and series.ndim != 1:
             raise ValueError(f'np.ndarray input must be 1 dimensional. Current np.ndarray is {series.ndim} dimensional')
         else:


Test patch:
diff --git a/woodwork/tests/utils/test_accessor_utils.py b/woodwork/tests/utils/test_accessor_utils.py
index c4d9d71..ca53adf 100644
--- a/woodwork/tests/utils/test_accessor_utils.py
+++ b/woodwork/tests/utils/test_accessor_utils.py
@@ -38,6 +38,22 @@ def test_init_series_valid_conversion_specified_ltype(sample_series):
     assert series.ww.semantic_tags == set()
 
 
+def test_init_series_with_pd_extension_array():
+    extension_categories = pd.Categorical([1, 2, 3])
+    series = init_series(extension_categories)
+    pd_reference_series = init_series(pd.Series([1, 2, 3], dtype='category'))
+    assert series.equals(pd_reference_series)
+    assert series.ww.logical_type == pd_reference_series.ww.logical_type
+    assert series.ww.semantic_tags == pd_reference_series.ww.semantic_tags
+
+    extension_ints = pd.array(np.array([1, 2, 3, 4], dtype="int64"))
+    series = init_series(extension_ints)
+    pd_reference_series = init_series(pd.Series([1, 2, 3, 4], dtype='Int64'))
+    assert series.equals(pd_reference_series)
+    assert series.ww.logical_type == pd_reference_series.ww.logical_type
+    assert series.ww.semantic_tags == pd_reference_series.ww.semantic_tags
+
+
 def test_init_series_with_invalid_type(sample_df):
     inputs = [sample_df, 1, "string", None]
     for input_ in inputs: