From 2c36e6adae5dd5437db524bc99089c6c2460ec59 Mon Sep 17 00:00:00 2001 From: Jeremy Schendel Date: Wed, 11 Sep 2019 22:49:33 -0600 Subject: [PATCH] BUG: Fix Series(List[Interval]) to infer interval dtype --- doc/source/whatsnew/v0.25.2.rst | 2 +- pandas/core/construction.py | 11 ++---- pandas/tests/series/test_constructors.py | 47 ++++++++++++++++-------- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/doc/source/whatsnew/v0.25.2.rst b/doc/source/whatsnew/v0.25.2.rst index 69f324211e5b2..8c5052fec439c 100644 --- a/doc/source/whatsnew/v0.25.2.rst +++ b/doc/source/whatsnew/v0.25.2.rst @@ -44,7 +44,7 @@ Conversion Interval ^^^^^^^^ -- +- Bug in :class:`Series` constructor where constructing a ``Series`` from a ``list`` of :class:`Interval` objects resulted in ``object`` dtype instead of :class:`IntervalDtype` (:issue:`23563`) Indexing ^^^^^^^^ diff --git a/pandas/core/construction.py b/pandas/core/construction.py index 5bd2a2b69deb1..3933471d3c584 100644 --- a/pandas/core/construction.py +++ b/pandas/core/construction.py @@ -10,7 +10,7 @@ import numpy.ma as ma from pandas._libs import lib, tslibs -from pandas._libs.tslibs import IncompatibleFrequency, OutOfBoundsDatetime +from pandas._libs.tslibs import OutOfBoundsDatetime from pandas.core.dtypes.cast import ( construct_1d_arraylike_from_scalar, @@ -475,13 +475,8 @@ def sanitize_array(data, index, dtype=None, copy=False, raise_cast_failure=False if is_object_dtype(subarr.dtype) and not is_object_dtype(dtype): inferred = lib.infer_dtype(subarr, skipna=False) - if inferred == "period": - from pandas.core.arrays import period_array - - try: - subarr = period_array(subarr) - except IncompatibleFrequency: - pass + if inferred in {"interval", "period"}: + subarr = array(subarr) return subarr diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py index 2f09d777e719c..02f1987a102e3 100644 --- a/pandas/tests/series/test_constructors.py +++ b/pandas/tests/series/test_constructors.py @@ -28,7 +28,7 @@ period_range, timedelta_range, ) -from pandas.core.arrays import period_array +from pandas.core.arrays import IntervalArray, period_array import pandas.util.testing as tm from pandas.util.testing import assert_series_equal @@ -970,16 +970,34 @@ def test_constructor_with_naive_string_and_datetimetz_dtype(self, arg): expected = Series(pd.Timestamp(arg)).dt.tz_localize("CET") assert_series_equal(result, expected) - def test_construction_interval(self): + @pytest.mark.parametrize("interval_constructor", [IntervalIndex, IntervalArray]) + def test_construction_interval(self, interval_constructor): # construction from interval & array of intervals - index = IntervalIndex.from_breaks(np.arange(3), closed="right") - result = Series(index) - repr(result) - str(result) - tm.assert_index_equal(Index(result.values), index) + intervals = interval_constructor.from_breaks(np.arange(3), closed="right") + result = Series(intervals) + assert result.dtype == "interval[int64]" + tm.assert_index_equal(Index(result.values), Index(intervals)) - result = Series(index.values) - tm.assert_index_equal(Index(result.values), index) + @pytest.mark.parametrize( + "data_constructor", [list, np.array], ids=["list", "ndarray[object]"] + ) + def test_constructor_infer_interval(self, data_constructor): + # GH 23563: consistent closed results in interval dtype + data = [pd.Interval(0, 1), pd.Interval(0, 2), None] + result = pd.Series(data_constructor(data)) + expected = pd.Series(IntervalArray(data)) + assert result.dtype == "interval[float64]" + tm.assert_series_equal(result, expected) + + @pytest.mark.parametrize( + "data_constructor", [list, np.array], ids=["list", "ndarray[object]"] + ) + def test_constructor_interval_mixed_closed(self, data_constructor): + # GH 23563: mixed closed results in object dtype (not interval dtype) + data = [pd.Interval(0, 1, closed="both"), pd.Interval(0, 2, closed="neither")] + result = Series(data_constructor(data)) + assert result.dtype == object + assert result.tolist() == data def test_construction_consistency(self): @@ -996,17 +1014,16 @@ def test_construction_consistency(self): result = Series(s.values, dtype=s.dtype) tm.assert_series_equal(result, s) - def test_constructor_infer_period(self): + @pytest.mark.parametrize( + "data_constructor", [list, np.array], ids=["list", "ndarray[object]"] + ) + def test_constructor_infer_period(self, data_constructor): data = [pd.Period("2000", "D"), pd.Period("2001", "D"), None] - result = pd.Series(data) + result = pd.Series(data_constructor(data)) expected = pd.Series(period_array(data)) tm.assert_series_equal(result, expected) assert result.dtype == "Period[D]" - data = np.asarray(data, dtype=object) - tm.assert_series_equal(result, expected) - assert result.dtype == "Period[D]" - def test_constructor_period_incompatible_frequency(self): data = [pd.Period("2000", "D"), pd.Period("2001", "A")] result = pd.Series(data)