Skip to content

Commit bd9a6f0

Browse files
authored
BUG: Series.setitem losing precision when enlarging (#47342)
1 parent 55d9dcf commit bd9a6f0

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

doc/source/whatsnew/v1.5.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,7 @@ Indexing
884884
- Bug in :meth:`DataFrame.loc` when setting values to a column and right hand side is a dictionary (:issue:`47216`)
885885
- Bug in :meth:`Series.__setitem__` with ``datetime64[ns]`` dtype, an all-``False`` boolean mask, and an incompatible value incorrectly casting to ``object`` instead of retaining ``datetime64[ns]`` dtype (:issue:`45967`)
886886
- Bug in :meth:`Index.__getitem__` raising ``ValueError`` when indexer is from boolean dtype with ``NA`` (:issue:`45806`)
887+
- Bug in :meth:`Series.__setitem__` losing precision when enlarging :class:`Series` with scalar (:issue:`32346`)
887888
- Bug in :meth:`Series.mask` with ``inplace=True`` or setting values with a boolean mask with small integer dtypes incorrectly raising (:issue:`45750`)
888889
- Bug in :meth:`DataFrame.mask` with ``inplace=True`` and ``ExtensionDtype`` columns incorrectly raising (:issue:`45577`)
889890
- Bug in getting a column from a DataFrame with an object-dtype row index with datetime-like values: the resulting Series now preserves the exact object-dtype Index from the parent DataFrame (:issue:`42950`)

pandas/core/indexing.py

+23-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
from pandas.util._decorators import doc
2323
from pandas.util._exceptions import find_stack_level
2424

25-
from pandas.core.dtypes.cast import can_hold_element
25+
from pandas.core.dtypes.cast import (
26+
can_hold_element,
27+
maybe_promote,
28+
)
2629
from pandas.core.dtypes.common import (
2730
is_array_like,
2831
is_bool_dtype,
@@ -42,7 +45,9 @@
4245
)
4346
from pandas.core.dtypes.missing import (
4447
infer_fill_value,
48+
is_valid_na_for_dtype,
4549
isna,
50+
na_value_for_dtype,
4651
)
4752

4853
from pandas.core import algorithms as algos
@@ -2087,8 +2092,23 @@ def _setitem_with_indexer_missing(self, indexer, value):
20872092
# We get only here with loc, so can hard code
20882093
return self._setitem_with_indexer(new_indexer, value, "loc")
20892094

2090-
# this preserves dtype of the value
2091-
new_values = Series([value])._values
2095+
# this preserves dtype of the value and of the object
2096+
if is_valid_na_for_dtype(value, self.obj.dtype):
2097+
value = na_value_for_dtype(self.obj.dtype, compat=False)
2098+
new_dtype = maybe_promote(self.obj.dtype, value)[0]
2099+
elif isna(value):
2100+
new_dtype = None
2101+
elif not self.obj.empty and not is_object_dtype(self.obj.dtype):
2102+
# We should not cast, if we have object dtype because we can
2103+
# set timedeltas into object series
2104+
curr_dtype = self.obj.dtype
2105+
curr_dtype = getattr(curr_dtype, "numpy_dtype", curr_dtype)
2106+
new_dtype = maybe_promote(curr_dtype, value)[0]
2107+
else:
2108+
new_dtype = None
2109+
2110+
new_values = Series([value], dtype=new_dtype)._values
2111+
20922112
if len(self.obj._values):
20932113
# GH#22717 handle casting compatibility that np.concatenate
20942114
# does incorrectly

pandas/tests/series/indexing/test_setitem.py

+27
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,33 @@ def test_setitem_not_contained(self, string_series):
535535
expected = concat([string_series, app])
536536
tm.assert_series_equal(ser, expected)
537537

538+
def test_setitem_keep_precision(self, any_numeric_ea_dtype):
539+
# GH#32346
540+
ser = Series([1, 2], dtype=any_numeric_ea_dtype)
541+
ser[2] = 10
542+
expected = Series([1, 2, 10], dtype=any_numeric_ea_dtype)
543+
tm.assert_series_equal(ser, expected)
544+
545+
@pytest.mark.parametrize("indexer", [1, 2])
546+
@pytest.mark.parametrize(
547+
"na, target_na, dtype, target_dtype",
548+
[
549+
(NA, NA, "Int64", "Int64"),
550+
(NA, np.nan, "int64", "float64"),
551+
(NaT, NaT, "int64", "object"),
552+
(np.nan, NA, "Int64", "Int64"),
553+
(np.nan, NA, "Float64", "Float64"),
554+
(np.nan, np.nan, "int64", "float64"),
555+
],
556+
)
557+
def test_setitem_enlarge_with_na(self, na, target_na, dtype, target_dtype, indexer):
558+
# GH#32346
559+
ser = Series([1, 2], dtype=dtype)
560+
ser[indexer] = na
561+
expected_values = [1, target_na] if indexer == 1 else [1, 2, target_na]
562+
expected = Series(expected_values, dtype=target_dtype)
563+
tm.assert_series_equal(ser, expected)
564+
538565

539566
def test_setitem_scalar_into_readonly_backing_data():
540567
# GH#14359: test that you cannot mutate a read only buffer

0 commit comments

Comments
 (0)