Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REF: avoid FutureWarning about using deprecates loc.__setitem__ non-inplace usage #48254

Merged
merged 5 commits into from
Sep 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 42 additions & 8 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -6885,14 +6885,48 @@ def fillna(
if not is_dict
else downcast.get(k) # type: ignore[union-attr]
)
# GH47649
result.loc[:, k] = (
result[k].fillna(v, limit=limit, downcast=downcast_k).values
)
# TODO: result.loc[:, k] = result.loc[:, k].fillna(
# v, limit=limit, downcast=downcast_k
# )
# Revert when GH45751 is fixed
mroeschke marked this conversation as resolved.
Show resolved Hide resolved

res_k = result[k].fillna(v, limit=limit, downcast=downcast_k)

if not inplace:
result[k] = res_k
else:
# We can write into our existing column(s) iff dtype
# was preserved.
if isinstance(res_k, ABCSeries):
# i.e. 'k' only shows up once in self.columns
if res_k.dtype == result[k].dtype:
result.loc[:, k] = res_k
else:
# Different dtype -> no way to do inplace.
result[k] = res_k
else:
# see test_fillna_dict_inplace_nonunique_columns
locs = result.columns.get_loc(k)
if isinstance(locs, slice):
locs = np.arange(self.shape[1])[locs]
elif (
isinstance(locs, np.ndarray) and locs.dtype.kind == "b"
):
locs = locs.nonzero()[0]
elif not (
isinstance(locs, np.ndarray) and locs.dtype.kind == "i"
):
# Should never be reached, but let's cover our bases
raise NotImplementedError(
"Unexpected get_loc result, please report a bug at "
"https://github.com/pandas-dev/pandas"
)

for i, loc in enumerate(locs):
res_loc = res_k.iloc[:, i]
target = self.iloc[:, loc]

if res_loc.dtype == target.dtype:
result.iloc[:, loc] = res_loc
else:
result.isetitem(loc, res_loc)

return result if not inplace else None

elif not is_list_like(value):
Expand Down
25 changes: 24 additions & 1 deletion pandas/tests/frame/methods/test_fillna.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@


class TestFillNA:
@td.skip_array_manager_not_yet_implemented
def test_fillna_dict_inplace_nonunique_columns(self, using_copy_on_write):
df = DataFrame(
{"A": [np.nan] * 3, "B": [NaT, Timestamp(1), NaT], "C": [np.nan, "foo", 2]}
)
df.columns = ["A", "A", "A"]
orig = df[:]

df.fillna({"A": 2}, inplace=True)
# The first and third columns can be set inplace, while the second cannot.

expected = DataFrame(
{"A": [2.0] * 3, "B": [2, Timestamp(1), 2], "C": [2, "foo", 2]}
)
expected.columns = ["A", "A", "A"]
tm.assert_frame_equal(df, expected)

# TODO: what's the expected/desired behavior with CoW?
if not using_copy_on_write:
assert tm.shares_memory(df.iloc[:, 0], orig.iloc[:, 0])
assert not tm.shares_memory(df.iloc[:, 1], orig.iloc[:, 1])
if not using_copy_on_write:
assert tm.shares_memory(df.iloc[:, 2], orig.iloc[:, 2])

@td.skip_array_manager_not_yet_implemented
def test_fillna_on_column_view(self, using_copy_on_write):
# GH#46149 avoid unnecessary copies
Expand Down Expand Up @@ -287,7 +311,6 @@ def test_fillna_downcast_noop(self, frame_or_series):
res3 = obj2.fillna("foo", downcast=np.dtype(np.int32))
tm.assert_equal(res3, expected)

@td.skip_array_manager_not_yet_implemented
@pytest.mark.parametrize("columns", [["A", "A", "B"], ["A", "A"]])
def test_fillna_dictlike_value_duplicate_colnames(self, columns):
# GH#43476
Expand Down