From 6852a22e6cd89d8c22f2fbc914c94c5b02b87685 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 9 Nov 2022 15:17:46 -0800 Subject: [PATCH 1/6] API: Series(floaty, dtype=inty) --- doc/source/whatsnew/v2.0.0.rst | 1 + pandas/core/construction.py | 59 ++-------------------- pandas/tests/frame/test_constructors.py | 61 ++++++++++------------ pandas/tests/series/test_constructors.py | 64 +++++++++++++----------- pandas/tests/test_downstream.py | 20 ++++---- 5 files changed, 78 insertions(+), 127 deletions(-) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 07a4e4af3dbe7..35a367eb6a670 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -293,6 +293,7 @@ Other API changes - Passing a sequence containing a type that cannot be converted to :class:`Timedelta` to :func:`to_timedelta` or to the :class:`Series` or :class:`DataFrame` constructor with ``dtype="timedelta64[ns]"`` or to :class:`TimedeltaIndex` now raises ``TypeError`` instead of ``ValueError`` (:issue:`49525`) - Changed behavior of :class:`Index` constructor with sequence containing at least one ``NaT`` and everything else either ``None`` or ``NaN`` to infer ``datetime64[ns]`` dtype instead of ``object``, matching :class:`Series` behavior (:issue:`49340`) - Changed behavior of :class:`Index` constructor with an object-dtype ``numpy.ndarray`` containing all-``bool`` values or all-complex values, this will now retain object dtype, consistent with the :class:`Series` behavior (:issue:`49594`) +- Changed behavior of :class:`Series` and :class:`DataFrame` constructors when given an integer dtype and floating-point data that is not round numbers, this now raises ``ValueError`` instead of silently retaining the float dtype (:issue:`49599`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/construction.py b/pandas/core/construction.py index 3ae509e74074e..73e0fc47da46c 100644 --- a/pandas/core/construction.py +++ b/pandas/core/construction.py @@ -14,7 +14,6 @@ cast, overload, ) -import warnings import numpy as np from numpy import ma @@ -28,8 +27,6 @@ DtypeObj, T, ) -from pandas.errors import IntCastingNaNError -from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.base import ( ExtensionDtype, @@ -48,7 +45,6 @@ is_datetime64_ns_dtype, is_dtype_equal, is_extension_array_dtype, - is_float_dtype, is_integer_dtype, is_list_like, is_object_dtype, @@ -566,39 +562,7 @@ def sanitize_array( if isinstance(data, np.matrix): data = data.A - if dtype is not None and is_float_dtype(data.dtype) and is_integer_dtype(dtype): - # possibility of nan -> garbage - try: - # GH 47391 numpy > 1.24 will raise a RuntimeError for nan -> int - # casting aligning with IntCastingNaNError below - with np.errstate(invalid="ignore"): - # GH#15832: Check if we are requesting a numeric dtype and - # that we can convert the data to the requested dtype. - subarr = maybe_cast_to_integer_array(data, dtype) - - except IntCastingNaNError: - warnings.warn( - "In a future version, passing float-dtype values containing NaN " - "and an integer dtype will raise IntCastingNaNError " - "(subclass of ValueError) instead of silently ignoring the " - "passed dtype. To retain the old behavior, call Series(arr) or " - "DataFrame(arr) without passing a dtype.", - FutureWarning, - stacklevel=find_stack_level(), - ) - subarr = np.array(data, copy=copy) - except ValueError: - # Pre-2.0, we would have different behavior for Series vs DataFrame. - # DataFrame would call np.array(data, dtype=dtype, copy=copy), - # which would cast to the integer dtype even if the cast is lossy. - # See GH#40110. - - # We ignore the dtype arg and return floating values, - # e.g. test_constructor_floating_data_int_dtype - # TODO: where is the discussion that documents the reason for this? - subarr = np.array(data, copy=copy) - - elif dtype is None: + if dtype is None: subarr = data if data.dtype == object: subarr = maybe_infer_to_datetimelike(data) @@ -631,25 +595,8 @@ def sanitize_array( subarr = np.array([], dtype=np.float64) elif dtype is not None: - try: - subarr = _try_cast(data, dtype, copy) - except ValueError: - if is_integer_dtype(dtype): - casted = np.array(data, copy=False) - if casted.dtype.kind == "f": - # GH#40110 match the behavior we have if we passed - # a ndarray[float] to begin with - return sanitize_array( - casted, - index, - dtype, - copy=False, - allow_2d=allow_2d, - ) - else: - raise - else: - raise + subarr = _try_cast(data, dtype, copy) + else: subarr = maybe_convert_platform(data) if subarr.dtype == object: diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py index 17a76decce3c7..26c5b721a454b 100644 --- a/pandas/tests/frame/test_constructors.py +++ b/pandas/tests/frame/test_constructors.py @@ -18,6 +18,7 @@ import pytest import pytz +from pandas.errors import IntCastingNaNError import pandas.util._test_decorators as td from pandas.core.dtypes.common import is_integer_dtype @@ -105,16 +106,13 @@ def test_constructor_dict_with_tzaware_scalar(self): def test_construct_ndarray_with_nas_and_int_dtype(self): # GH#26919 match Series by not casting np.nan to meaningless int arr = np.array([[1, np.nan], [2, 3]]) - with tm.assert_produces_warning(FutureWarning): - df = DataFrame(arr, dtype="i8") - assert df.values.dtype == arr.dtype - assert isna(df.iloc[0, 1]) + msg = r"Cannot convert non-finite values \(NA or inf\) to integer" + with pytest.raises(IntCastingNaNError, match=msg): + DataFrame(arr, dtype="i8") # check this matches Series behavior - with tm.assert_produces_warning(FutureWarning): - ser = Series(arr[0], dtype="i8", name=0) - expected = df.iloc[0] - tm.assert_series_equal(ser, expected) + with pytest.raises(IntCastingNaNError, match=msg): + Series(arr[0], dtype="i8", name=0) def test_construct_from_list_of_datetimes(self): df = DataFrame([datetime.now(), datetime.now()]) @@ -966,21 +964,16 @@ def _check_basic_constructor(self, empty): assert len(frame.index) == 3 assert len(frame.columns) == 1 - warn = None if empty is np.ones else FutureWarning - with tm.assert_produces_warning(warn): + if empty is not np.ones: + msg = r"Cannot convert non-finite values \(NA or inf\) to integer" + with pytest.raises(IntCastingNaNError, match=msg): + DataFrame(mat, columns=["A", "B", "C"], index=[1, 2], dtype=np.int64) + return + else: frame = DataFrame( mat, columns=["A", "B", "C"], index=[1, 2], dtype=np.int64 ) - if empty is np.ones: - # passing dtype casts assert frame.values.dtype == np.int64 - else: - # i.e. ma.masked_all - # Since we have NaNs, refuse to cast to int dtype, which would take NaN - # to meaningless integers. This matches Series behavior. GH#26919 - assert frame.isna().all().all() - assert frame.values.dtype == np.float64 - assert isna(frame.values).all() # wrong size axis labels msg = r"Shape of passed values is \(2, 3\), indices imply \(1, 3\)" @@ -1741,11 +1734,10 @@ def test_constructor_mix_series_nonseries(self, float_frame): DataFrame({"A": float_frame["A"], "B": list(float_frame["B"])[:-2]}) def test_constructor_miscast_na_int_dtype(self): - msg = "float-dtype values containing NaN and an integer dtype" - with tm.assert_produces_warning(FutureWarning, match=msg): - df = DataFrame([[np.nan, 1], [1, 0]], dtype=np.int64) - expected = DataFrame([[np.nan, 1], [1, 0]]) - tm.assert_frame_equal(df, expected) + msg = r"Cannot convert non-finite values \(NA or inf\) to integer" + + with pytest.raises(IntCastingNaNError, match=msg): + DataFrame([[np.nan, 1], [1, 0]], dtype=np.int64) def test_constructor_column_duplicates(self): # it works! #2079 @@ -2724,27 +2716,28 @@ def test_floating_values_integer_dtype(self): arr = np.random.randn(10, 5) - # as of 2.0, we match Series behavior by retaining float dtype instead - # of doing a lossy conversion here. Below we _do_ do the conversion - # since it is lossless. - df = DataFrame(arr, dtype="i8") - assert (df.dtypes == "f8").all() + # GH#49599 in 2.0 we raise instead of either + # a) silently ignoring dtype and returningfloat (the old Series behavior) or + # b) rounding (the old DataFrame behavior) + msg = "Trying to coerce float values to integers" + with pytest.raises(ValueError, match=msg): + DataFrame(arr, dtype="i8") df = DataFrame(arr.round(), dtype="i8") assert (df.dtypes == "i8").all() # with NaNs, we go through a different path with a different warning arr[0, 0] = np.nan - msg = "passing float-dtype values containing NaN" - with tm.assert_produces_warning(FutureWarning, match=msg): + msg = r"Cannot convert non-finite values \(NA or inf\) to integer" + with pytest.raises(IntCastingNaNError, match=msg): DataFrame(arr, dtype="i8") - with tm.assert_produces_warning(FutureWarning, match=msg): + with pytest.raises(IntCastingNaNError, match=msg): Series(arr[0], dtype="i8") # The future (raising) behavior matches what we would get via astype: msg = r"Cannot convert non-finite values \(NA or inf\) to integer" - with pytest.raises(ValueError, match=msg): + with pytest.raises(IntCastingNaNError, match=msg): DataFrame(arr).astype("i8") - with pytest.raises(ValueError, match=msg): + with pytest.raises(IntCastingNaNError, match=msg): Series(arr[0]).astype("i8") diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py index 37348bb743537..e9e88982a1bdc 100644 --- a/pandas/tests/series/test_constructors.py +++ b/pandas/tests/series/test_constructors.py @@ -15,6 +15,7 @@ lib, ) from pandas.compat import is_numpy_dev +from pandas.errors import IntCastingNaNError import pandas.util._test_decorators as td from pandas.core.dtypes.common import ( @@ -670,10 +671,9 @@ def test_constructor_sanitize(self): s = Series(np.array([1.0, 1.0, 8.0]), dtype="i8") assert s.dtype == np.dtype("i8") - msg = "float-dtype values containing NaN and an integer dtype" - with tm.assert_produces_warning(FutureWarning, match=msg): - ser = Series(np.array([1.0, 1.0, np.nan]), copy=True, dtype="i8") - assert ser.dtype == np.dtype("f8") + msg = r"Cannot convert non-finite values \(NA or inf\) to integer" + with pytest.raises(IntCastingNaNError, match=msg): + Series(np.array([1.0, 1.0, np.nan]), copy=True, dtype="i8") def test_constructor_copy(self): # GH15125 @@ -803,24 +803,30 @@ def test_constructor_floating_data_int_dtype(self, frame_or_series): # not clear if this is what we want long-term expected = frame_or_series(arr) - res = frame_or_series(arr, dtype="i8") - tm.assert_equal(res, expected) + # GH#49599 as of 2.0 we raise instead of silently retaining float dtype + msg = "Trying to coerce float values to integer" + with pytest.raises(ValueError, match=msg): + frame_or_series(arr, dtype="i8") - res = frame_or_series(list(arr), dtype="i8") - tm.assert_equal(res, expected) + with pytest.raises(ValueError, match=msg): + frame_or_series(list(arr), dtype="i8") - # When we have NaNs, we silently ignore the integer dtype + # pre-2.0, when we had NaNs, we silently ignored the integer dtype arr[0] = np.nan expected = frame_or_series(arr) - msg = "passing float-dtype values containing NaN and an integer dtype" - with tm.assert_produces_warning(FutureWarning, match=msg): - obj = frame_or_series(arr, dtype="i8") - tm.assert_equal(obj, expected) - with tm.assert_produces_warning(FutureWarning, match=msg): + msg = r"Cannot convert non-finite values \(NA or inf\) to integer" + with pytest.raises(IntCastingNaNError, match=msg): + frame_or_series(arr, dtype="i8") + + exc = IntCastingNaNError + if frame_or_series is Series: + # TODO: try to align these + exc = ValueError + msg = "cannot convert float NaN to integer" + with pytest.raises(exc, match=msg): # same behavior if we pass list instead of the ndarray - obj = frame_or_series(list(arr), dtype="i8") - tm.assert_equal(obj, expected) + frame_or_series(list(arr), dtype="i8") # float array that can be losslessly cast to integers arr = np.array([1.0, 2.0], dtype="float64") @@ -836,13 +842,14 @@ def test_constructor_coerce_float_fail(self, any_int_numpy_dtype): # see gh-15832 # Updated: make sure we treat this list the same as we would treat # the equivalent ndarray + # GH#49599 pre-2.0 we silently retained float dtype, in 2.0 we raise vals = [1, 2, 3.5] - res = Series(vals, dtype=any_int_numpy_dtype) - expected = Series(np.array(vals), dtype=any_int_numpy_dtype) - tm.assert_series_equal(res, expected) - alt = Series(np.array(vals)) # i.e. we ignore the dtype kwd - tm.assert_series_equal(alt, expected) + msg = "Trying to coerce float values to integer" + with pytest.raises(ValueError, match=msg): + Series(vals, dtype=any_int_numpy_dtype) + with pytest.raises(ValueError, match=msg): + Series(np.array(vals), dtype=any_int_numpy_dtype) def test_constructor_coerce_float_valid(self, float_numpy_dtype): s = Series([1, 2, 3.5], dtype=float_numpy_dtype) @@ -854,13 +861,14 @@ def test_constructor_invalid_coerce_ints_with_float_nan(self, any_int_numpy_dtyp # Updated: make sure we treat this list the same as we would treat the # equivalent ndarray vals = [1, 2, np.nan] - msg = "In a future version, passing float-dtype values containing NaN" - with tm.assert_produces_warning(FutureWarning, match=msg): - res = Series(vals, dtype=any_int_numpy_dtype) - with tm.assert_produces_warning(FutureWarning, match=msg): - expected = Series(np.array(vals), dtype=any_int_numpy_dtype) - tm.assert_series_equal(res, expected) - assert np.isnan(expected.iloc[-1]) + # pre-2.0 this would return with a float dtype, in 2.0 we raise + + msg = "cannot convert float NaN to integer" + with pytest.raises(ValueError, match=msg): + Series(vals, dtype=any_int_numpy_dtype) + msg = r"Cannot convert non-finite values \(NA or inf\) to integer" + with pytest.raises(IntCastingNaNError, match=msg): + Series(np.array(vals), dtype=any_int_numpy_dtype) def test_constructor_dtype_no_cast(self): # see gh-1572 diff --git a/pandas/tests/test_downstream.py b/pandas/tests/test_downstream.py index a7f4269fa62b1..3d18f120e35d9 100644 --- a/pandas/tests/test_downstream.py +++ b/pandas/tests/test_downstream.py @@ -8,6 +8,7 @@ import numpy as np import pytest +from pandas.errors import IntCastingNaNError import pandas.util._test_decorators as td import pandas as pd @@ -96,17 +97,18 @@ def test_construct_dask_float_array_int_dtype_match_ndarray(): expected = Series(arr) tm.assert_series_equal(res, expected) - res = Series(darr, dtype="i8") - expected = Series(arr, dtype="i8") - tm.assert_series_equal(res, expected) + # GH#49599 in 2.0 we raise instead of silently ignoring the dtype + msg = "Trying to coerce float values to integers" + with pytest.raises(ValueError, match=msg): + Series(darr, dtype="i8") - msg = "In a future version, passing float-dtype values containing NaN" + msg = r"Cannot convert non-finite values \(NA or inf\) to integer" arr[2] = np.nan - with tm.assert_produces_warning(FutureWarning, match=msg): - res = Series(darr, dtype="i8") - with tm.assert_produces_warning(FutureWarning, match=msg): - expected = Series(arr, dtype="i8") - tm.assert_series_equal(res, expected) + with pytest.raises(IntCastingNaNError, match=msg): + Series(darr, dtype="i8") + # which is the same as we get with a numpy input + with pytest.raises(IntCastingNaNError, match=msg): + Series(arr, dtype="i8") def test_xarray(df): From 34bbafe66caeb52db763fb798b5801931963584a Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 10 Nov 2022 07:43:46 -0800 Subject: [PATCH 2/6] update asv --- asv_bench/benchmarks/groupby.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/asv_bench/benchmarks/groupby.py b/asv_bench/benchmarks/groupby.py index 6dff4a017e2a9..db2031a276e0e 100644 --- a/asv_bench/benchmarks/groupby.py +++ b/asv_bench/benchmarks/groupby.py @@ -608,22 +608,27 @@ class Cumulative: def setup(self, dtype, method): N = 500_000 + keys = np.random.randint(0, 100, size=N) vals = np.random.randint(-10, 10, (N, 5)) null_vals = vals.astype(float, copy=True) null_vals[::2, :] = np.nan null_vals[::3, :] = np.nan df = DataFrame(vals, columns=list("abcde"), dtype=dtype) - null_df = DataFrame(null_vals, columns=list("abcde"), dtype=dtype) - keys = np.random.randint(0, 100, size=N) df["key"] = keys - null_df["key"] = keys self.df = df - self.null_df = null_df + + if dtype != "int64": + # Would raise on DataFrame construction with int64 + null_df = DataFrame(null_vals, columns=list("abcde"), dtype=dtype) + null_df["key"] = keys + self.null_df = null_df def time_frame_transform(self, dtype, method): self.df.groupby("key").transform(method) def time_frame_transform_many_nulls(self, dtype, method): + if dtype == "int64": + raise NotImplementedError self.null_df.groupby("key").transform(method) From cf65aa0453b62f2ed87334399b3a087ee5ade857 Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 10 Nov 2022 08:48:24 -0800 Subject: [PATCH 3/6] troubleshoot asv --- asv_bench/benchmarks/groupby.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/asv_bench/benchmarks/groupby.py b/asv_bench/benchmarks/groupby.py index db2031a276e0e..bb56fbc026dc7 100644 --- a/asv_bench/benchmarks/groupby.py +++ b/asv_bench/benchmarks/groupby.py @@ -628,7 +628,8 @@ def time_frame_transform(self, dtype, method): def time_frame_transform_many_nulls(self, dtype, method): if dtype == "int64": - raise NotImplementedError + # We can't instantiate null_df, so skip this case + return self.null_df.groupby("key").transform(method) From 18cbc48a0bbe13f5affe1ed0201f2b69c4bd886e Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 10 Nov 2022 11:38:39 -0800 Subject: [PATCH 4/6] update asv --- asv_bench/benchmarks/groupby.py | 38 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/asv_bench/benchmarks/groupby.py b/asv_bench/benchmarks/groupby.py index bb56fbc026dc7..6f0bb3091133f 100644 --- a/asv_bench/benchmarks/groupby.py +++ b/asv_bench/benchmarks/groupby.py @@ -600,38 +600,36 @@ def time_frame_agg(self, dtype, method): class Cumulative: - param_names = ["dtype", "method"] + param_names = ["dtype", "method", "with_nans"] params = [ ["float64", "int64", "Float64", "Int64"], ["cummin", "cummax", "cumsum"], + [True, False], ] - def setup(self, dtype, method): + def setup(self, dtype, method, with_nans): + if with_nans and dtype == "int64": + raise NotImplementedError("Construction of df would raise") + N = 500_000 keys = np.random.randint(0, 100, size=N) vals = np.random.randint(-10, 10, (N, 5)) - null_vals = vals.astype(float, copy=True) - null_vals[::2, :] = np.nan - null_vals[::3, :] = np.nan - df = DataFrame(vals, columns=list("abcde"), dtype=dtype) - df["key"] = keys - self.df = df - if dtype != "int64": - # Would raise on DataFrame construction with int64 - null_df = DataFrame(null_vals, columns=list("abcde"), dtype=dtype) - null_df["key"] = keys - self.null_df = null_df + if with_nans: + null_vals = vals.astype(float, copy=True) + null_vals[::2, :] = np.nan + null_vals[::3, :] = np.nan + df = DataFrame(null_vals, columns=list("abcde"), dtype=dtype) + df["key"] = keys + self.df = df + else: + df = DataFrame(vals, columns=list("abcde")).astype(dtype, copy=False) + df["key"] = keys + self.df = df - def time_frame_transform(self, dtype, method): + def time_frame_transform(self, dtype, method, with_nans): self.df.groupby("key").transform(method) - def time_frame_transform_many_nulls(self, dtype, method): - if dtype == "int64": - # We can't instantiate null_df, so skip this case - return - self.null_df.groupby("key").transform(method) - class RankWithTies: # GH 21237 From f556cb589430c0aa1aac86ac03d4c260ebb703c2 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 23 Nov 2022 16:41:05 -0800 Subject: [PATCH 5/6] add alternative to whatsnew --- doc/source/whatsnew/v2.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 8f60b30713f54..7bec728502347 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -348,7 +348,7 @@ Other API changes - Changed behavior of :class:`Index` constructor with sequence containing at least one ``NaT`` and everything else either ``None`` or ``NaN`` to infer ``datetime64[ns]`` dtype instead of ``object``, matching :class:`Series` behavior (:issue:`49340`) - :func:`read_stata` with parameter ``index_col`` set to ``None`` (the default) will now set the index on the returned :class:`DataFrame` to a :class:`RangeIndex` instead of a :class:`Int64Index` (:issue:`49745`) - Changed behavior of :class:`Index` constructor with an object-dtype ``numpy.ndarray`` containing all-``bool`` values or all-complex values, this will now retain object dtype, consistent with the :class:`Series` behavior (:issue:`49594`) -- Changed behavior of :class:`Series` and :class:`DataFrame` constructors when given an integer dtype and floating-point data that is not round numbers, this now raises ``ValueError`` instead of silently retaining the float dtype (:issue:`49599`) +- Changed behavior of :class:`Series` and :class:`DataFrame` constructors when given an integer dtype and floating-point data that is not round numbers, this now raises ``ValueError`` instead of silently retaining the float dtype; do ``Series(data).astype(dtype)`` or ``DataFrame(data).astype(dtype)`` to get the old behavior (:issue:`49599`) - Changed behavior of :meth:`DataFrame.shift` with ``axis=1``, an integer ``fill_value``, and homogeneous datetime-like dtype, this now fills new columns with integer dtypes instead of casting to datetimelike (:issue:`49842`) - :meth:`DataFrame.values`, :meth:`DataFrame.to_numpy`, :meth:`DataFrame.xs`, :meth:`DataFrame.reindex`, :meth:`DataFrame.fillna`, and :meth:`DataFrame.replace` no longer silently consolidate the underlying arrays; do ``df = df.copy()`` to ensure consolidation (:issue:`49356`) - From f36bbe095920485d78e53ed8dbc380e5b1fe9932 Mon Sep 17 00:00:00 2001 From: Brock Date: Tue, 13 Dec 2022 15:12:46 -0800 Subject: [PATCH 6/6] update wahtsnew --- doc/source/whatsnew/v2.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 97c14ec609ea5..9bcffdfefb359 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -470,7 +470,7 @@ Other API changes - :func:`read_stata` with parameter ``index_col`` set to ``None`` (the default) will now set the index on the returned :class:`DataFrame` to a :class:`RangeIndex` instead of a :class:`Int64Index` (:issue:`49745`) - Changed behavior of :class:`Index`, :class:`Series`, and :class:`DataFrame` arithmetic methods when working with object-dtypes, the results no longer do type inference on the result of the array operations, use ``result.infer_objects()`` to do type inference on the result (:issue:`49999`) - Changed behavior of :class:`Index` constructor with an object-dtype ``numpy.ndarray`` containing all-``bool`` values or all-complex values, this will now retain object dtype, consistent with the :class:`Series` behavior (:issue:`49594`) -- Changed behavior of :class:`Series` and :class:`DataFrame` constructors when given an integer dtype and floating-point data that is not round numbers, this now raises ``ValueError`` instead of silently retaining the float dtype; do ``Series(data).astype(dtype)`` or ``DataFrame(data).astype(dtype)`` to get the old behavior (:issue:`49599`) +- Changed behavior of :class:`Series` and :class:`DataFrame` constructors when given an integer dtype and floating-point data that is not round numbers, this now raises ``ValueError`` instead of silently retaining the float dtype; do ``Series(data)`` or ``DataFrame(data)`` to get the old behavior, and ``Series(data).astype(dtype)`` or ``DataFrame(data).astype(dtype)`` to get the specified dtype (:issue:`49599`) - Changed behavior of :meth:`DataFrame.shift` with ``axis=1``, an integer ``fill_value``, and homogeneous datetime-like dtype, this now fills new columns with integer dtypes instead of casting to datetimelike (:issue:`49842`) - Files are now closed when encountering an exception in :func:`read_json` (:issue:`49921`) - Changed behavior of :func:`read_csv`, :func:`read_json` & :func:`read_fwf`, where the index will now always be a :class:`RangeIndex`, when no index is specified. Previously the index would be a :class:`Index` with dtype ``object`` if the new DataFrame/Series has length 0 (:issue:`49572`)