Skip to content

Commit a63e410

Browse files
committed
wip
1 parent b527654 commit a63e410

22 files changed

+644
-64
lines changed

pandas/_testing/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
assert_indexing_slices_equivalent,
5858
assert_interval_array_equal,
5959
assert_is_sorted,
60+
assert_maybe_raises,
6061
assert_metadata_equivalent,
6162
assert_numpy_array_equal,
6263
assert_period_array_equal,
@@ -557,6 +558,7 @@ def shares_memory(left, right) -> bool:
557558
"assert_indexing_slices_equivalent",
558559
"assert_interval_array_equal",
559560
"assert_is_sorted",
561+
"assert_maybe_raises",
560562
"assert_metadata_equivalent",
561563
"assert_numpy_array_equal",
562564
"assert_period_array_equal",

pandas/_testing/asserters.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import contextlib
34
import operator
45
from typing import (
56
TYPE_CHECKING,
@@ -9,6 +10,7 @@
910
)
1011

1112
import numpy as np
13+
import pytest
1214

1315
from pandas._libs import lib
1416
from pandas._libs.missing import is_matching_na
@@ -1443,3 +1445,9 @@ def assert_metadata_equivalent(
14431445
assert val is None
14441446
else:
14451447
assert val == getattr(right, attr, None)
1448+
1449+
1450+
def assert_maybe_raises(condition, exception, match):
1451+
if condition:
1452+
return pytest.raises(exception, match=match)
1453+
return contextlib.nullcontext()

pandas/tests/copy_view/test_indexing.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -716,11 +716,24 @@ def test_column_as_series_set_with_upcast(backend):
716716
# Case: selecting a single column now also uses Copy-on-Write -> when
717717
# setting a value causes an upcast, we don't need to update the parent
718718
# DataFrame through the cache mechanism
719-
_dtype_backend, DataFrame, _Series = backend
719+
dtype_backend, DataFrame, Series = backend
720720
df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [0.1, 0.2, 0.3]})
721+
df_orig = df.copy()
722+
721723
s = df["a"]
722-
with pytest.raises(TypeError, match="Invalid value"):
723-
s[0] = "foo"
724+
if dtype_backend == "nullable":
725+
with pytest.raises(TypeError, match="Invalid value"):
726+
s[0] = "foo"
727+
expected = Series([1, 2, 3], name="a")
728+
else:
729+
with pytest.raises(TypeError, match="Invalid value"):
730+
s[0] = "foo"
731+
return
732+
733+
tm.assert_series_equal(s, expected)
734+
tm.assert_frame_equal(df, df_orig)
735+
# ensure cached series on getitem is not the changed series
736+
tm.assert_series_equal(df["a"], df_orig["a"])
724737

725738

726739
@pytest.mark.parametrize(
@@ -769,6 +782,7 @@ def test_dataframe_add_column_from_series(backend):
769782
tm.assert_frame_equal(df, expected)
770783

771784

785+
@pytest.mark.parametrize("val", [100, "a"])
772786
@pytest.mark.parametrize(
773787
"indexer_func, indexer",
774788
[
@@ -783,13 +797,20 @@ def test_dataframe_add_column_from_series(backend):
783797
@pytest.mark.parametrize(
784798
"col", [[0.1, 0.2, 0.3], [7, 8, 9]], ids=["mixed-block", "single-block"]
785799
)
786-
def test_set_value_copy_only_necessary_column(indexer_func, indexer, col):
800+
def test_set_value_copy_only_necessary_column(indexer_func, indexer, val, col):
787801
# When setting inplace, only copy column that is modified instead of the whole
788802
# block (by splitting the block)
789803
df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": col})
790804
df_orig = df.copy()
791805
view = df[:]
792-
indexer_func(df)[indexer] = 100
806+
807+
if val == "a":
808+
with tm.assert_produces_warning(
809+
FutureWarning, match="Setting an item of incompatible dtype is deprecated"
810+
):
811+
indexer_func(df)[indexer] = val
812+
813+
indexer_func(df)[indexer] = val
793814

794815
assert np.shares_memory(get_array(df, "b"), get_array(view, "b"))
795816
assert not np.shares_memory(get_array(df, "a"), get_array(view, "a"))

pandas/tests/copy_view/test_methods.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,32 @@ def test_putmask_aligns_rhs_no_reference(dtype):
11051105
assert np.shares_memory(arr_a, get_array(df, "a"))
11061106

11071107

1108+
@pytest.mark.parametrize(
1109+
"val, exp, warn", [(5.5, True, FutureWarning), (5, False, None)]
1110+
)
1111+
def test_putmask_dont_copy_some_blocks(val, exp, warn):
1112+
df = DataFrame({"a": [1, 2], "b": 1, "c": 1.5})
1113+
view = df[:]
1114+
df_orig = df.copy()
1115+
indexer = DataFrame(
1116+
[[True, False, False], [True, False, False]], columns=list("abc")
1117+
)
1118+
if warn:
1119+
with pytest.raises(TypeError, match="Invalid value"):
1120+
df[indexer] = val
1121+
return
1122+
else:
1123+
df[indexer] = val
1124+
1125+
assert not np.shares_memory(get_array(view, "a"), get_array(df, "a"))
1126+
# TODO(CoW): Could split blocks to avoid copying the whole block
1127+
assert np.shares_memory(get_array(view, "b"), get_array(df, "b")) is exp
1128+
assert np.shares_memory(get_array(view, "c"), get_array(df, "c"))
1129+
assert df._mgr._has_no_reference(1) is not exp
1130+
assert not df._mgr._has_no_reference(2)
1131+
tm.assert_frame_equal(view, df_orig)
1132+
1133+
11081134
@pytest.mark.parametrize("dtype", ["int64", "Int64"])
11091135
@pytest.mark.parametrize(
11101136
"func",

pandas/tests/frame/indexing/test_coercion.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,40 @@ def test_loc_setitem_multiindex_columns(self, consolidate):
4646
# TODO: i think this isn't about MultiIndex and could be done with iloc?
4747

4848

49+
def test_37477():
50+
# fixed by GH#45121
51+
orig = DataFrame({"A": [1, 2, 3], "B": [3, 4, 5]})
52+
expected = DataFrame({"A": [1, 2, 3], "B": [3, 1.2, 5]})
53+
54+
df = orig.copy()
55+
with tm.assert_produces_warning(
56+
FutureWarning, match="Setting an item of incompatible dtype"
57+
):
58+
df.at[1, "B"] = 1.2
59+
tm.assert_frame_equal(df, expected)
60+
61+
df = orig.copy()
62+
with tm.assert_produces_warning(
63+
FutureWarning, match="Setting an item of incompatible dtype"
64+
):
65+
df.loc[1, "B"] = 1.2
66+
tm.assert_frame_equal(df, expected)
67+
68+
df = orig.copy()
69+
with tm.assert_produces_warning(
70+
FutureWarning, match="Setting an item of incompatible dtype"
71+
):
72+
df.iat[1, 1] = 1.2
73+
tm.assert_frame_equal(df, expected)
74+
75+
df = orig.copy()
76+
with tm.assert_produces_warning(
77+
FutureWarning, match="Setting an item of incompatible dtype"
78+
):
79+
df.iloc[1, 1] = 1.2
80+
tm.assert_frame_equal(df, expected)
81+
82+
4983
def test_6942(indexer_al):
5084
# check that the .at __setitem__ after setting "Live" actually sets the data
5185
start = Timestamp("2014-04-01")
@@ -73,6 +107,20 @@ def test_26395(indexer_al):
73107
expected = DataFrame({"D": [0, 0, 2]}, index=["A", "B", "C"], dtype=np.int64)
74108
tm.assert_frame_equal(df, expected)
75109

110+
with tm.assert_produces_warning(
111+
FutureWarning, match="Setting an item of incompatible dtype"
112+
):
113+
indexer_al(df)["C", "D"] = 44.5
114+
expected = DataFrame({"D": [0, 0, 44.5]}, index=["A", "B", "C"], dtype=np.float64)
115+
tm.assert_frame_equal(df, expected)
116+
117+
with tm.assert_produces_warning(
118+
FutureWarning, match="Setting an item of incompatible dtype"
119+
):
120+
indexer_al(df)["C", "D"] = "hello"
121+
expected = DataFrame({"D": [0, 0, "hello"]}, index=["A", "B", "C"], dtype=object)
122+
tm.assert_frame_equal(df, expected)
123+
76124

77125
@pytest.mark.xfail(reason="unwanted upcast")
78126
def test_15231():

pandas/tests/frame/indexing/test_indexing.py

Lines changed: 101 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import numpy as np
1010
import pytest
1111

12+
from pandas._libs import iNaT
1213
from pandas.errors import InvalidIndexError
1314

1415
from pandas.core.dtypes.common import is_integer
@@ -24,6 +25,7 @@
2425
Timestamp,
2526
date_range,
2627
isna,
28+
notna,
2729
to_datetime,
2830
)
2931
import pandas._testing as tm
@@ -830,6 +832,15 @@ def test_setitem_single_column_mixed_datetime(self):
830832
)
831833
tm.assert_series_equal(result, expected)
832834

835+
# GH#16674 iNaT is treated as an integer when given by the user
836+
with tm.assert_produces_warning(
837+
FutureWarning, match="Setting an item of incompatible dtype"
838+
):
839+
df.loc["b", "timestamp"] = iNaT
840+
assert not isna(df.loc["b", "timestamp"])
841+
assert df["timestamp"].dtype == np.object_
842+
assert df.loc["b", "timestamp"] == iNaT
843+
833844
# allow this syntax (as of GH#3216)
834845
df.loc["c", "timestamp"] = np.nan
835846
assert isna(df.loc["c", "timestamp"])
@@ -838,6 +849,38 @@ def test_setitem_single_column_mixed_datetime(self):
838849
df.loc["d", :] = np.nan
839850
assert not isna(df.loc["c", :]).all()
840851

852+
def test_setitem_mixed_datetime(self):
853+
# GH 9336
854+
expected = DataFrame(
855+
{
856+
"a": [0, 0, 0, 0, 13, 14],
857+
"b": [
858+
datetime(2012, 1, 1),
859+
1,
860+
"x",
861+
"y",
862+
datetime(2013, 1, 1),
863+
datetime(2014, 1, 1),
864+
],
865+
}
866+
)
867+
df = DataFrame(0, columns=list("ab"), index=range(6))
868+
df["b"] = pd.NaT
869+
df.loc[0, "b"] = datetime(2012, 1, 1)
870+
with tm.assert_produces_warning(
871+
FutureWarning, match="Setting an item of incompatible dtype"
872+
):
873+
df.loc[1, "b"] = 1
874+
df.loc[[2, 3], "b"] = "x", "y"
875+
A = np.array(
876+
[
877+
[13, np.datetime64("2013-01-01T00:00:00")],
878+
[14, np.datetime64("2014-01-01T00:00:00")],
879+
]
880+
)
881+
df.loc[[4, 5], ["a", "b"]] = A
882+
tm.assert_frame_equal(df, expected)
883+
841884
def test_setitem_frame_float(self, float_frame):
842885
piece = float_frame.loc[float_frame.index[:2], ["A", "B"]]
843886
float_frame.loc[float_frame.index[-2] :, ["A", "B"]] = piece.values
@@ -889,6 +932,17 @@ def test_setitem_frame_mixed_ndarray(self, float_string_frame):
889932
f.loc[key] = piece.values
890933
tm.assert_almost_equal(f.loc[f.index[-2:], ["A", "B"]].values, piece.values)
891934

935+
def test_setitem_frame_upcast(self):
936+
# needs upcasting
937+
df = DataFrame([[1, 2, "foo"], [3, 4, "bar"]], columns=["A", "B", "C"])
938+
df2 = df.copy()
939+
with tm.assert_produces_warning(FutureWarning, match="incompatible dtype"):
940+
df2.loc[:, ["A", "B"]] = df.loc[:, ["A", "B"]] + 0.5
941+
expected = df.reindex(columns=["A", "B"])
942+
expected += 0.5
943+
expected["C"] = df["C"]
944+
tm.assert_frame_equal(df2, expected)
945+
892946
def test_setitem_frame_align(self, float_frame):
893947
piece = float_frame.loc[float_frame.index[:2], ["A", "B"]]
894948
piece.index = float_frame.index[-2:]
@@ -1299,6 +1353,26 @@ def test_loc_expand_empty_frame_keep_midx_names(self):
12991353
)
13001354
tm.assert_frame_equal(df, expected)
13011355

1356+
@pytest.mark.parametrize(
1357+
"val, idxr",
1358+
[
1359+
("x", "a"),
1360+
("x", ["a"]),
1361+
(1, "a"),
1362+
(1, ["a"]),
1363+
],
1364+
)
1365+
def test_loc_setitem_rhs_frame(self, idxr, val):
1366+
# GH#47578
1367+
df = DataFrame({"a": [1, 2]})
1368+
1369+
with tm.assert_produces_warning(
1370+
FutureWarning, match="Setting an item of incompatible dtype"
1371+
):
1372+
df.loc[:, idxr] = DataFrame({"a": [val, 11]}, index=[1, 2])
1373+
expected = DataFrame({"a": [np.nan, val]})
1374+
tm.assert_frame_equal(df, expected)
1375+
13021376
def test_iloc_setitem_enlarge_no_warning(self):
13031377
# GH#47381
13041378
df = DataFrame(columns=["a", "b"])
@@ -1502,6 +1576,22 @@ def test_setitem(self):
15021576
tm.assert_series_equal(df["D"], Series(idx, name="D"))
15031577
del df["D"]
15041578

1579+
# With NaN: because uint64 has no NaN element,
1580+
# the column should be cast to object.
1581+
df2 = df.copy()
1582+
with tm.assert_produces_warning(FutureWarning, match="incompatible dtype"):
1583+
df2.iloc[1, 1] = pd.NaT
1584+
df2.iloc[1, 2] = pd.NaT
1585+
result = df2["B"]
1586+
tm.assert_series_equal(notna(result), Series([True, False, True], name="B"))
1587+
tm.assert_series_equal(
1588+
df2.dtypes,
1589+
Series(
1590+
[np.dtype("uint64"), np.dtype("O"), np.dtype("O")],
1591+
index=["A", "B", "C"],
1592+
),
1593+
)
1594+
15051595

15061596
def test_object_casting_indexing_wraps_datetimelike():
15071597
# GH#31649, check the indexing methods all the way down the stack
@@ -1836,31 +1926,22 @@ def test_add_new_column_infer_string():
18361926
class TestSetitemValidation:
18371927
# This is adapted from pandas/tests/arrays/masked/test_indexing.py
18381928
# but checks for warnings instead of errors.
1839-
def _check_setitem_invalid(self, df, invalid, indexer):
1929+
def _check_setitem_invalid(self, df, invalid, indexer, warn):
1930+
msg = "Setting an item of incompatible dtype is deprecated"
1931+
msg = re.escape(msg)
1932+
18401933
orig_df = df.copy()
18411934

18421935
# iloc
1843-
with pytest.raises(TypeError, match="Invalid value"):
1936+
with tm.assert_produces_warning(warn, match=msg):
18441937
df.iloc[indexer, 0] = invalid
18451938
df = orig_df.copy()
18461939

18471940
# loc
1848-
with pytest.raises(TypeError, match="Invalid value"):
1941+
with tm.assert_produces_warning(warn, match=msg):
18491942
df.loc[indexer, "a"] = invalid
18501943
df = orig_df.copy()
18511944

1852-
def _check_setitem_valid(self, df, invalid, indexer):
1853-
# Just execute, verify no error is raised
1854-
orig_df = df.copy()
1855-
1856-
# iloc
1857-
df.iloc[indexer, 0] = invalid
1858-
df = orig_df.copy()
1859-
1860-
# loc
1861-
df.loc[indexer, "a"] = invalid
1862-
df = orig_df.copy()
1863-
18641945
_invalid_scalars = [
18651946
1 + 2j,
18661947
"True",
@@ -1878,19 +1959,20 @@ def _check_setitem_valid(self, df, invalid, indexer):
18781959
@pytest.mark.parametrize("indexer", _indexers)
18791960
def test_setitem_validation_scalar_bool(self, invalid, indexer):
18801961
df = DataFrame({"a": [True, False, False]}, dtype="bool")
1881-
self._check_setitem_invalid(df, invalid, indexer)
1962+
self._check_setitem_invalid(df, invalid, indexer, FutureWarning)
18821963

18831964
@pytest.mark.parametrize("invalid", _invalid_scalars + [True, 1.5, np.float64(1.5)])
18841965
@pytest.mark.parametrize("indexer", _indexers)
18851966
def test_setitem_validation_scalar_int(self, invalid, any_int_numpy_dtype, indexer):
18861967
df = DataFrame({"a": [1, 2, 3]}, dtype=any_int_numpy_dtype)
18871968
if isna(invalid) and invalid is not pd.NaT and not np.isnat(invalid):
1888-
self._check_setitem_valid(df, invalid, indexer)
1969+
warn = None
18891970
else:
1890-
self._check_setitem_invalid(df, invalid, indexer)
1971+
warn = FutureWarning
1972+
self._check_setitem_invalid(df, invalid, indexer, warn)
18911973

18921974
@pytest.mark.parametrize("invalid", _invalid_scalars + [True])
18931975
@pytest.mark.parametrize("indexer", _indexers)
18941976
def test_setitem_validation_scalar_float(self, invalid, float_numpy_dtype, indexer):
18951977
df = DataFrame({"a": [1, 2, None]}, dtype=float_numpy_dtype)
1896-
self._check_setitem_invalid(df, invalid, indexer)
1978+
self._check_setitem_invalid(df, invalid, indexer, FutureWarning)

0 commit comments

Comments
 (0)