diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 983768e0f67da..190b515981098 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -208,6 +208,7 @@ Removal of prior version deprecations/changes - All arguments except ``name`` in :meth:`Index.rename` are now keyword only (:issue:`56493`) - All arguments except the first ``path``-like argument in IO writers are now keyword only (:issue:`54229`) - Disallow calling :meth:`Series.replace` or :meth:`DataFrame.replace` without a ``value`` and with non-dict-like ``to_replace`` (:issue:`33302`) +- Disallow constructing a :class:`arrays.SparseArray` with scalar data (:issue:`53039`) - Disallow non-standard (``np.ndarray``, :class:`Index`, :class:`ExtensionArray`, or :class:`Series`) to :func:`isin`, :func:`unique`, :func:`factorize` (:issue:`52986`) - Disallow passing a pandas type to :meth:`Index.view` (:issue:`55709`) - Disallow units other than "s", "ms", "us", "ns" for datetime64 and timedelta64 dtypes in :func:`array` (:issue:`53817`) @@ -216,6 +217,7 @@ Removal of prior version deprecations/changes - Removed deprecated "method" and "limit" keywords from :meth:`Series.replace` and :meth:`DataFrame.replace` (:issue:`53492`) - Removed extension test classes ``BaseNoReduceTests``, ``BaseNumericReduceTests``, ``BaseBooleanReduceTests`` (:issue:`54663`) - Removed the "closed" and "normalize" keywords in :meth:`DatetimeIndex.__new__` (:issue:`52628`) +- Require :meth:`SparseDtype.fill_value` to be a valid value for the :meth:`SparseDtype.subtype` (:issue:`53043`) - Stopped performing dtype inference with in :meth:`Index.insert` with object-dtype index; this often affects the index/columns that result when setting new entries into an empty :class:`Series` or :class:`DataFrame` (:issue:`51363`) - Removed the "closed" and "unit" keywords in :meth:`TimedeltaIndex.__new__` (:issue:`52628`, :issue:`55499`) - All arguments in :meth:`Index.sort_values` are now keyword only (:issue:`56493`) diff --git a/pandas/core/arrays/sparse/array.py b/pandas/core/arrays/sparse/array.py index 2a96423017bb7..134702099371d 100644 --- a/pandas/core/arrays/sparse/array.py +++ b/pandas/core/arrays/sparse/array.py @@ -40,7 +40,6 @@ from pandas.core.dtypes.astype import astype_array from pandas.core.dtypes.cast import ( - construct_1d_arraylike_from_scalar, find_common_type, maybe_box_datetimelike, ) @@ -399,19 +398,10 @@ def __init__( dtype = dtype.subtype if is_scalar(data): - warnings.warn( - f"Constructing {type(self).__name__} with scalar data is deprecated " - "and will raise in a future version. Pass a sequence instead.", - FutureWarning, - stacklevel=find_stack_level(), + raise TypeError( + f"Cannot construct {type(self).__name__} from scalar data. " + "Pass a sequence instead." ) - if sparse_index is None: - npoints = 1 - else: - npoints = sparse_index.length - - data = construct_1d_arraylike_from_scalar(data, npoints, dtype=None) - dtype = data.dtype if dtype is not None: dtype = pandas_dtype(dtype) diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index f94d32a3b8547..2d8e490f02d52 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -1762,24 +1762,18 @@ def _check_fill_value(self) -> None: val = self._fill_value if isna(val): if not is_valid_na_for_dtype(val, self.subtype): - warnings.warn( - "Allowing arbitrary scalar fill_value in SparseDtype is " - "deprecated. In a future version, the fill_value must be " - "a valid value for the SparseDtype.subtype.", - FutureWarning, - stacklevel=find_stack_level(), + raise ValueError( + # GH#53043 + "fill_value must be a valid value for the SparseDtype.subtype" ) else: dummy = np.empty(0, dtype=self.subtype) dummy = ensure_wrapped_if_datetimelike(dummy) if not can_hold_element(dummy, val): - warnings.warn( - "Allowing arbitrary scalar fill_value in SparseDtype is " - "deprecated. In a future version, the fill_value must be " - "a valid value for the SparseDtype.subtype.", - FutureWarning, - stacklevel=find_stack_level(), + raise ValueError( + # GH#53043 + "fill_value must be a valid value for the SparseDtype.subtype" ) @property diff --git a/pandas/tests/arrays/sparse/test_array.py b/pandas/tests/arrays/sparse/test_array.py index 883d6ea3959ff..c35e8204f3437 100644 --- a/pandas/tests/arrays/sparse/test_array.py +++ b/pandas/tests/arrays/sparse/test_array.py @@ -52,10 +52,11 @@ def test_set_fill_value(self): arr.fill_value = 2 assert arr.fill_value == 2 - msg = "Allowing arbitrary scalar fill_value in SparseDtype is deprecated" - with tm.assert_produces_warning(FutureWarning, match=msg): + msg = "fill_value must be a valid value for the SparseDtype.subtype" + with pytest.raises(ValueError, match=msg): + # GH#53043 arr.fill_value = 3.1 - assert arr.fill_value == 3.1 + assert arr.fill_value == 2 arr.fill_value = np.nan assert np.isnan(arr.fill_value) @@ -64,8 +65,9 @@ def test_set_fill_value(self): arr.fill_value = True assert arr.fill_value is True - with tm.assert_produces_warning(FutureWarning, match=msg): + with pytest.raises(ValueError, match=msg): arr.fill_value = 0 + assert arr.fill_value is True arr.fill_value = np.nan assert np.isnan(arr.fill_value) diff --git a/pandas/tests/arrays/sparse/test_constructors.py b/pandas/tests/arrays/sparse/test_constructors.py index 2831c8abdaf13..012ff1da0d431 100644 --- a/pandas/tests/arrays/sparse/test_constructors.py +++ b/pandas/tests/arrays/sparse/test_constructors.py @@ -144,20 +144,12 @@ def test_constructor_spindex_dtype(self): @pytest.mark.parametrize("sparse_index", [None, IntIndex(1, [0])]) def test_constructor_spindex_dtype_scalar(self, sparse_index): # scalar input - msg = "Constructing SparseArray with scalar data is deprecated" - with tm.assert_produces_warning(FutureWarning, match=msg): - arr = SparseArray(data=1, sparse_index=sparse_index, dtype=None) - exp = SparseArray([1], dtype=None) - tm.assert_sp_array_equal(arr, exp) - assert arr.dtype == SparseDtype(np.int64) - assert arr.fill_value == 0 + msg = "Cannot construct SparseArray from scalar data. Pass a sequence instead" + with pytest.raises(TypeError, match=msg): + SparseArray(data=1, sparse_index=sparse_index, dtype=None) - with tm.assert_produces_warning(FutureWarning, match=msg): - arr = SparseArray(data=1, sparse_index=IntIndex(1, [0]), dtype=None) - exp = SparseArray([1], dtype=None) - tm.assert_sp_array_equal(arr, exp) - assert arr.dtype == SparseDtype(np.int64) - assert arr.fill_value == 0 + with pytest.raises(TypeError, match=msg): + SparseArray(data=1, sparse_index=IntIndex(1, [0]), dtype=None) def test_constructor_spindex_dtype_scalar_broadcasts(self): arr = SparseArray( diff --git a/pandas/tests/arrays/sparse/test_dtype.py b/pandas/tests/arrays/sparse/test_dtype.py index 6f0d41333f2fd..1819744d9a9ae 100644 --- a/pandas/tests/arrays/sparse/test_dtype.py +++ b/pandas/tests/arrays/sparse/test_dtype.py @@ -84,7 +84,6 @@ def test_nans_not_equal(): (SparseDtype("float64"), SparseDtype("float32")), (SparseDtype("float64"), SparseDtype("float64", 0)), (SparseDtype("float64"), SparseDtype("datetime64[ns]", np.nan)), - (SparseDtype(int, pd.NaT), SparseDtype(float, pd.NaT)), (SparseDtype("float64"), np.dtype("float64")), ]