Skip to content

Commit 99ae672

Browse files
jbrockmendeleicchen
authored andcommitted
ENH: fill_value in frame+series flex ops
1 parent 98c9c7f commit 99ae672

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
@@ -8468,27 +8468,34 @@ def _maybe_align_series_as_frame(self, series: Series, axis: AxisInt):
84688468
blockwise.
84698469
"""
84708470
rvalues = series._values
8471-
if not isinstance(rvalues, np.ndarray):
8472-
# TODO(EA2D): no need to special-case with 2D EAs
8473-
if rvalues.dtype in ("datetime64[ns]", "timedelta64[ns]"):
8474-
# We can losslessly+cheaply cast to ndarray
8475-
rvalues = np.asarray(rvalues)
8471+
if lib.is_np_dtype(rvalues.dtype):
8472+
# We can losslessly+cheaply cast to ndarray
8473+
# i.e. ndarray or dt64[naive], td64
8474+
# TODO(EA2D): no need to special case with 2D EAs
8475+
rvalues = np.asarray(rvalues)
8476+
8477+
if axis == 0:
8478+
rvalues = rvalues.reshape(-1, 1)
84768479
else:
8477-
return series
8480+
rvalues = rvalues.reshape(1, -1)
84788481

8479-
if axis == 0:
8480-
rvalues = rvalues.reshape(-1, 1)
8481-
else:
8482-
rvalues = rvalues.reshape(1, -1)
8482+
rvalues = np.broadcast_to(rvalues, self.shape)
8483+
# pass dtype to avoid doing inference
8484+
df = self._constructor(rvalues, dtype=rvalues.dtype)
84838485

8484-
rvalues = np.broadcast_to(rvalues, self.shape)
8485-
# pass dtype to avoid doing inference
8486-
return self._constructor(
8487-
rvalues,
8488-
index=self.index,
8489-
columns=self.columns,
8490-
dtype=rvalues.dtype,
8491-
).__finalize__(series)
8486+
else:
8487+
# GH#61581
8488+
if axis == 0:
8489+
df = DataFrame(dict.fromkeys(range(self.shape[1]), rvalues))
8490+
else:
8491+
nrows = self.shape[0]
8492+
df = DataFrame(
8493+
{i: rvalues[[i]].repeat(nrows) for i in range(self.shape[1])},
8494+
dtype=rvalues.dtype,
8495+
)
8496+
df.index = self.index
8497+
df.columns = self.columns
8498+
return df.__finalize__(series)
84928499

84938500
def _flex_arith_method(
84948501
self, other, op, *, axis: Axis = "columns", level=None, fill_value=None
@@ -8498,11 +8505,6 @@ def _flex_arith_method(
84988505
if self._should_reindex_frame_op(other, op, axis, fill_value, level):
84998506
return self._arith_method_with_reindex(other, op)
85008507

8501-
if isinstance(other, Series) and fill_value is not None:
8502-
# TODO: We could allow this in cases where we end up going
8503-
# through the DataFrame path
8504-
raise NotImplementedError(f"fill_value {fill_value} not supported.")
8505-
85068508
other = ops.maybe_prepare_scalar_for_op(other, self.shape)
85078509
self, other = self._align_for_op(other, axis, flex=True, level=level)
85088510

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)