Skip to content

Commit 4c03ace

Browse files
committed
ENH: fill_value in frame+series flex ops
1 parent 452c7fb commit 4c03ace

File tree

4 files changed

+33
-45
lines changed

4 files changed

+33
-45
lines changed

pandas/core/frame.py

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8449,27 +8449,34 @@ def _maybe_align_series_as_frame(self, series: Series, axis: AxisInt):
84498449
blockwise.
84508450
"""
84518451
rvalues = series._values
8452-
if not isinstance(rvalues, np.ndarray):
8453-
# TODO(EA2D): no need to special-case with 2D EAs
8454-
if rvalues.dtype in ("datetime64[ns]", "timedelta64[ns]"):
8455-
# We can losslessly+cheaply cast to ndarray
8456-
rvalues = np.asarray(rvalues)
8452+
if lib.is_np_dtype(rvalues.dtype):
8453+
# We can losslessly+cheaply cast to ndarray
8454+
# i.e. ndarray or dt64[naive], td64
8455+
# TODO(EA2D): no need to special case with 2D EAs
8456+
rvalues = np.asarray(rvalues)
8457+
8458+
if axis == 0:
8459+
rvalues = rvalues.reshape(-1, 1)
84578460
else:
8458-
return series
8461+
rvalues = rvalues.reshape(1, -1)
84598462

8460-
if axis == 0:
8461-
rvalues = rvalues.reshape(-1, 1)
8462-
else:
8463-
rvalues = rvalues.reshape(1, -1)
8463+
rvalues = np.broadcast_to(rvalues, self.shape)
8464+
# pass dtype to avoid doing inference
8465+
df = self._constructor(rvalues, dtype=rvalues.dtype)
84648466

8465-
rvalues = np.broadcast_to(rvalues, self.shape)
8466-
# pass dtype to avoid doing inference
8467-
return self._constructor(
8468-
rvalues,
8469-
index=self.index,
8470-
columns=self.columns,
8471-
dtype=rvalues.dtype,
8472-
).__finalize__(series)
8467+
else:
8468+
# GH#61581
8469+
if axis == 0:
8470+
df = DataFrame(dict.fromkeys(range(self.shape[1]), rvalues))
8471+
else:
8472+
nrows = self.shape[0]
8473+
df = DataFrame(
8474+
{i: rvalues[[i]].repeat(nrows) for i in range(self.shape[1])},
8475+
dtype=rvalues.dtype,
8476+
)
8477+
df.index = self.index
8478+
df.columns = self.columns
8479+
return df.__finalize__(series)
84738480

84748481
def _flex_arith_method(
84758482
self, other, op, *, axis: Axis = "columns", level=None, fill_value=None
@@ -8479,11 +8486,6 @@ def _flex_arith_method(
84798486
if self._should_reindex_frame_op(other, op, axis, fill_value, level):
84808487
return self._arith_method_with_reindex(other, op)
84818488

8482-
if isinstance(other, Series) and fill_value is not None:
8483-
# TODO: We could allow this in cases where we end up going
8484-
# through the DataFrame path
8485-
raise NotImplementedError(f"fill_value {fill_value} not supported.")
8486-
84878489
other = ops.maybe_prepare_scalar_for_op(other, self.shape)
84888490
self, other = self._align_for_op(other, axis, flex=True, level=level)
84898491

pandas/tests/arithmetic/test_period.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,12 +1361,8 @@ def test_period_add_timestamp_raises(self, box_with_array):
13611361
arr + ts
13621362
with pytest.raises(TypeError, match=msg):
13631363
ts + arr
1364-
if box_with_array is pd.DataFrame:
1365-
# TODO: before implementing resolution-inference we got the same
1366-
# message with DataFrame and non-DataFrame. Why did that change?
1367-
msg = "cannot add PeriodArray and Timestamp"
1368-
else:
1369-
msg = "cannot add PeriodArray and DatetimeArray"
1364+
1365+
msg = "cannot add PeriodArray and DatetimeArray"
13701366
with pytest.raises(TypeError, match=msg):
13711367
arr + Series([ts])
13721368
with pytest.raises(TypeError, match=msg):
@@ -1376,16 +1372,9 @@ def test_period_add_timestamp_raises(self, box_with_array):
13761372
with pytest.raises(TypeError, match=msg):
13771373
pd.Index([ts]) + arr
13781374

1379-
if box_with_array is pd.DataFrame:
1380-
msg = "cannot add PeriodArray and DatetimeArray"
1381-
else:
1382-
msg = r"unsupported operand type\(s\) for \+: 'Period' and 'DatetimeArray"
1375+
msg = "cannot add PeriodArray and DatetimeArray"
13831376
with pytest.raises(TypeError, match=msg):
13841377
arr + pd.DataFrame([ts])
1385-
if box_with_array is pd.DataFrame:
1386-
msg = "cannot add PeriodArray and DatetimeArray"
1387-
else:
1388-
msg = r"unsupported operand type\(s\) for \+: 'DatetimeArray' and 'Period'"
13891378
with pytest.raises(TypeError, match=msg):
13901379
pd.DataFrame([ts]) + arr
13911380

pandas/tests/arrays/string_/test_string.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,6 @@ def test_mul(dtype):
249249
tm.assert_extension_array_equal(result, expected)
250250

251251

252-
@pytest.mark.xfail(reason="GH-28527")
253252
def test_add_strings(dtype):
254253
arr = pd.array(["a", "b", "c", "d"], dtype=dtype)
255254
df = pd.DataFrame([["t", "y", "v", "w"]], dtype=object)

pandas/tests/frame/test_arithmetic.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -626,11 +626,9 @@ def test_arith_flex_frame_corner(self, float_frame):
626626
expected = float_frame.sort_index() * np.nan
627627
tm.assert_frame_equal(result, expected)
628628

629-
with pytest.raises(NotImplementedError, match="fill_value"):
630-
float_frame.add(float_frame.iloc[0], fill_value=3)
629+
res = float_frame.add(float_frame.iloc[0], fill_value=3)
631630

632-
with pytest.raises(NotImplementedError, match="fill_value"):
633-
float_frame.add(float_frame.iloc[0], axis="index", fill_value=3)
631+
res = float_frame.add(float_frame.iloc[0], axis="index", fill_value=3)
634632

635633
@pytest.mark.parametrize("op", ["add", "sub", "mul", "mod"])
636634
def test_arith_flex_series_ops(self, simple_frame, op):
@@ -672,11 +670,11 @@ def test_arith_flex_zero_len_raises(self):
672670
df_len0 = DataFrame(columns=["A", "B"])
673671
df = DataFrame([[1, 2], [3, 4]], columns=["A", "B"])
674672

675-
with pytest.raises(NotImplementedError, match="fill_value"):
673+
msg = r"unsupported operand type\(s\) for \+: 'int' and 'str'"
674+
with pytest.raises(TypeError, match=msg):
676675
df.add(ser_len0, fill_value="E")
677676

678-
with pytest.raises(NotImplementedError, match="fill_value"):
679-
df_len0.sub(df["A"], axis=None, fill_value=3)
677+
df_len0.sub(df["A"], axis=None, fill_value=3)
680678

681679
def test_flex_add_scalar_fill_value(self):
682680
# GH#12723

0 commit comments

Comments
 (0)