diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 55a53ba135275..47f67e9c2a4b3 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -752,7 +752,8 @@ Sparse ExtensionArray ^^^^^^^^^^^^^^ -- Fixed bug where :meth:`Serires.value_counts` would raise on empty input of ``Int64`` dtype (:issue:`33317`) +- Fixed bug where :meth:`Series.value_counts` would raise on empty input of ``Int64`` dtype (:issue:`33317`) +- Fixed bug in :class:`Series` construction with EA dtype and index but no data or scalar data fails (:issue:`26469`) - Fixed bug that caused :meth:`Series.__repr__()` to crash for extension types whose elements are multidimensional arrays (:issue:`33770`). diff --git a/pandas/core/construction.py b/pandas/core/construction.py index 351ef1d0429da..e6e26f0eec597 100644 --- a/pandas/core/construction.py +++ b/pandas/core/construction.py @@ -450,6 +450,11 @@ def sanitize_array( subarr = _try_cast(arr, dtype, copy, raise_cast_failure) elif isinstance(data, abc.Set): raise TypeError("Set type is unordered") + elif lib.is_scalar(data) and index is not None and dtype is not None: + data = maybe_cast_to_datetime(data, dtype) + if not lib.is_scalar(data): + data = data[0] + subarr = construct_1d_arraylike_from_scalar(data, len(index), dtype) else: subarr = _try_cast(data, dtype, copy, raise_cast_failure) @@ -516,7 +521,7 @@ def _try_cast( Parameters ---------- - arr : ndarray, list, tuple, iterator (catchall) + arr : ndarray, scalar, list, tuple, iterator (catchall) Excludes: ExtensionArray, Series, Index. dtype : np.dtype, ExtensionDtype or None copy : bool diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index ad307fd99ec9c..b91cfde45f079 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -1384,7 +1384,9 @@ def maybe_cast_to_datetime(value, dtype, errors: str = "raise"): pass # coerce datetimelike to object - elif is_datetime64_dtype(value) and not is_datetime64_dtype(dtype): + elif is_datetime64_dtype( + getattr(value, "dtype", None) + ) and not is_datetime64_dtype(dtype): if is_object_dtype(dtype): if value.dtype != DT64NS_DTYPE: value = value.astype(DT64NS_DTYPE) diff --git a/pandas/tests/extension/arrow/test_bool.py b/pandas/tests/extension/arrow/test_bool.py index 94dd09d3eb053..681c6f9a19dc5 100644 --- a/pandas/tests/extension/arrow/test_bool.py +++ b/pandas/tests/extension/arrow/test_bool.py @@ -1,6 +1,8 @@ import numpy as np import pytest +from pandas.compat import PY37 + import pandas as pd import pandas._testing as tm from pandas.tests.extension import base @@ -55,6 +57,18 @@ def test_from_dtype(self, data): def test_from_sequence_from_cls(self, data): super().test_from_sequence_from_cls(data) + @pytest.mark.skipif(not PY37, reason="timeout on Linux py36_locale") + @pytest.mark.xfail(reason="pa.NULL is not recognised as scalar, GH-33899") + def test_series_constructor_no_data_with_index(self, dtype, na_value): + # pyarrow.lib.ArrowInvalid: only handle 1-dimensional arrays + super().test_series_constructor_no_data_with_index(dtype, na_value) + + @pytest.mark.skipif(not PY37, reason="timeout on Linux py36_locale") + @pytest.mark.xfail(reason="pa.NULL is not recognised as scalar, GH-33899") + def test_series_constructor_scalar_na_with_index(self, dtype, na_value): + # pyarrow.lib.ArrowInvalid: only handle 1-dimensional arrays + super().test_series_constructor_scalar_na_with_index(dtype, na_value) + class TestReduce(base.BaseNoReduceTests): def test_reduce_series_boolean(self): diff --git a/pandas/tests/extension/base/constructors.py b/pandas/tests/extension/base/constructors.py index 1ddc7af0f6268..52e29cffc79c4 100644 --- a/pandas/tests/extension/base/constructors.py +++ b/pandas/tests/extension/base/constructors.py @@ -33,6 +33,31 @@ def test_series_constructor(self, data): assert result2.dtype == data.dtype assert isinstance(result2._mgr.blocks[0], ExtensionBlock) + def test_series_constructor_no_data_with_index(self, dtype, na_value): + result = pd.Series(index=[1, 2, 3], dtype=dtype) + expected = pd.Series([na_value] * 3, index=[1, 2, 3], dtype=dtype) + self.assert_series_equal(result, expected) + + # GH 33559 - empty index + result = pd.Series(index=[], dtype=dtype) + expected = pd.Series([], index=pd.Index([], dtype="object"), dtype=dtype) + self.assert_series_equal(result, expected) + + def test_series_constructor_scalar_na_with_index(self, dtype, na_value): + result = pd.Series(na_value, index=[1, 2, 3], dtype=dtype) + expected = pd.Series([na_value] * 3, index=[1, 2, 3], dtype=dtype) + self.assert_series_equal(result, expected) + + def test_series_constructor_scalar_with_index(self, data, dtype): + scalar = data[0] + result = pd.Series(scalar, index=[1, 2, 3], dtype=dtype) + expected = pd.Series([scalar] * 3, index=[1, 2, 3], dtype=dtype) + self.assert_series_equal(result, expected) + + result = pd.Series(scalar, index=["foo"], dtype=dtype) + expected = pd.Series([scalar], index=["foo"], dtype=dtype) + self.assert_series_equal(result, expected) + @pytest.mark.parametrize("from_series", [True, False]) def test_dataframe_constructor_from_dict(self, data, from_series): if from_series: diff --git a/pandas/tests/extension/json/test_json.py b/pandas/tests/extension/json/test_json.py index 745488770e09c..d79769208ab56 100644 --- a/pandas/tests/extension/json/test_json.py +++ b/pandas/tests/extension/json/test_json.py @@ -150,6 +150,21 @@ def test_from_dtype(self, data): # construct from our dtype & string dtype pass + @pytest.mark.xfail(reason="RecursionError, GH-33900") + def test_series_constructor_no_data_with_index(self, dtype, na_value): + # RecursionError: maximum recursion depth exceeded in comparison + super().test_series_constructor_no_data_with_index(dtype, na_value) + + @pytest.mark.xfail(reason="RecursionError, GH-33900") + def test_series_constructor_scalar_na_with_index(self, dtype, na_value): + # RecursionError: maximum recursion depth exceeded in comparison + super().test_series_constructor_scalar_na_with_index(dtype, na_value) + + @pytest.mark.xfail(reason="collection as scalar, GH-33901") + def test_series_constructor_scalar_with_index(self, data, dtype): + # TypeError: All values must be of type + super().test_series_constructor_scalar_with_index(data, dtype) + class TestReshaping(BaseJSON, base.BaseReshapingTests): @pytest.mark.skip(reason="Different definitions of NA") diff --git a/pandas/tests/extension/test_numpy.py b/pandas/tests/extension/test_numpy.py index 1c887cc4371b6..bb3595f4aef68 100644 --- a/pandas/tests/extension/test_numpy.py +++ b/pandas/tests/extension/test_numpy.py @@ -151,6 +151,11 @@ def test_array_from_scalars(self, data): # ValueError: PandasArray must be 1-dimensional. super().test_array_from_scalars(data) + @skip_nested + def test_series_constructor_scalar_with_index(self, data, dtype): + # ValueError: Length of passed values is 1, index implies 3. + super().test_series_constructor_scalar_with_index(data, dtype) + class TestDtype(BaseNumPyTests, base.BaseDtypeTests): @pytest.mark.skip(reason="Incorrect expected.") diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py index 22076eb05db88..85f47d0f6f5a4 100644 --- a/pandas/tests/series/test_constructors.py +++ b/pandas/tests/series/test_constructors.py @@ -1444,3 +1444,9 @@ def test_constructor_datetime64(self): series = Series(dates) assert np.issubdtype(series.dtype, np.dtype("M8[ns]")) + + def test_constructor_datetimelike_scalar_to_string_dtype(self): + # https://github.com/pandas-dev/pandas/pull/33846 + result = Series("M", index=[1, 2, 3], dtype="string") + expected = pd.Series(["M", "M", "M"], index=[1, 2, 3], dtype="string") + tm.assert_series_equal(result, expected)