Skip to content

Commit 21d8cdd

Browse files
Backport PR #55189 on branch 2.1.x (Revert "DEPR: Deprecate returning a DataFrame in SeriesApply.apply_standard) (#55205)
Backport PR #55189: Revert "DEPR: Deprecate returning a DataFrame in SeriesApply.apply_standard Co-authored-by: Patrick Hoefler <[email protected]>
1 parent 34cc5b7 commit 21d8cdd

File tree

8 files changed

+45
-59
lines changed

8 files changed

+45
-59
lines changed

Diff for: doc/source/user_guide/cookbook.rst

+5-5
Original file line numberDiff line numberDiff line change
@@ -794,12 +794,12 @@ Apply
794794
index=["I", "II", "III"],
795795
)
796796
797-
def make_df(ser):
798-
new_vals = [pd.Series(value, name=name) for name, value in ser.items()]
799-
return pd.DataFrame(new_vals)
800-
801-
df_orgz = pd.concat({ind: row.pipe(make_df) for ind, row in df.iterrows()})
797+
def SeriesFromSubList(aList):
798+
return pd.Series(aList)
802799
800+
df_orgz = pd.concat(
801+
{ind: row.apply(SeriesFromSubList) for ind, row in df.iterrows()}
802+
)
803803
df_orgz
804804
805805
`Rolling apply with a DataFrame returning a Series

Diff for: doc/source/user_guide/groupby.rst

+13
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,19 @@ The dimension of the returned result can also change:
12071207
12081208
grouped.apply(f)
12091209
1210+
``apply`` on a Series can operate on a returned value from the applied function
1211+
that is itself a series, and possibly upcast the result to a DataFrame:
1212+
1213+
.. ipython:: python
1214+
1215+
def f(x):
1216+
return pd.Series([x, x ** 2], index=["x", "x^2"])
1217+
1218+
1219+
s = pd.Series(np.random.rand(5))
1220+
s
1221+
s.apply(f)
1222+
12101223
Similar to :ref:`groupby.aggregate.agg`, the resulting dtype will reflect that of the
12111224
apply function. If the results from different groups have different dtypes, then
12121225
a common dtype will be determined in the same way as ``DataFrame`` construction.

Diff for: doc/source/whatsnew/v0.10.0.rst

+9-20
Original file line numberDiff line numberDiff line change
@@ -244,26 +244,15 @@ Convenience methods ``ffill`` and ``bfill`` have been added:
244244
function, that is itself a series, and possibly upcast the result to a
245245
DataFrame
246246

247-
.. code-block:: python
248-
249-
>>> def f(x):
250-
... return pd.Series([x, x ** 2], index=["x", "x^2"])
251-
>>>
252-
>>> s = pd.Series(np.random.rand(5))
253-
>>> s
254-
0 0.340445
255-
1 0.984729
256-
2 0.919540
257-
3 0.037772
258-
4 0.861549
259-
dtype: float64
260-
>>> s.apply(f)
261-
x x^2
262-
0 0.340445 0.115903
263-
1 0.984729 0.969691
264-
2 0.919540 0.845555
265-
3 0.037772 0.001427
266-
4 0.861549 0.742267
247+
.. ipython:: python
248+
249+
def f(x):
250+
return pd.Series([x, x ** 2], index=["x", "x^2"])
251+
252+
253+
s = pd.Series(np.random.rand(5))
254+
s
255+
s.apply(f)
267256
268257
- New API functions for working with pandas options (:issue:`2097`):
269258

Diff for: doc/source/whatsnew/v2.1.0.rst

-1
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,6 @@ Other Deprecations
555555
- Deprecated behavior of :func:`concat` when :class:`DataFrame` has columns that are all-NA, in a future version these will not be discarded when determining the resulting dtype (:issue:`40893`)
556556
- Deprecated behavior of :meth:`Series.dt.to_pydatetime`, in a future version this will return a :class:`Series` containing python ``datetime`` objects instead of an ``ndarray`` of datetimes; this matches the behavior of other :attr:`Series.dt` properties (:issue:`20306`)
557557
- Deprecated logical operations (``|``, ``&``, ``^``) between pandas objects and dtype-less sequences (e.g. ``list``, ``tuple``), wrap a sequence in a :class:`Series` or NumPy array before operating instead (:issue:`51521`)
558-
- Deprecated making :meth:`Series.apply` return a :class:`DataFrame` when the passed-in callable returns a :class:`Series` object. In the future this will return a :class:`Series` whose values are themselves :class:`Series`. This pattern was very slow and it's recommended to use alternative methods to archive the same goal (:issue:`52116`)
559558
- Deprecated parameter ``convert_type`` in :meth:`Series.apply` (:issue:`52140`)
560559
- Deprecated passing a dictionary to :meth:`.SeriesGroupBy.agg`; pass a list of aggregations instead (:issue:`50684`)
561560
- Deprecated the ``fastpath`` keyword in :class:`Categorical` constructor, use :meth:`Categorical.from_codes` instead (:issue:`20110`)

Diff for: doc/source/whatsnew/v2.1.1.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Bug fixes
4444

4545
Other
4646
~~~~~
47-
-
47+
- Reverted the deprecation that disallowed :meth:`Series.apply` returning a :class:`DataFrame` when the passed-in callable returns a :class:`Series` object (:issue:`52116`)
4848

4949
.. ---------------------------------------------------------------------------
5050
.. _whatsnew_211.contributors:

Diff for: pandas/core/apply.py

-8
Original file line numberDiff line numberDiff line change
@@ -1289,14 +1289,6 @@ def curried(x):
12891289
)
12901290

12911291
if len(mapped) and isinstance(mapped[0], ABCSeries):
1292-
warnings.warn(
1293-
"Returning a DataFrame from Series.apply when the supplied function "
1294-
"returns a Series is deprecated and will be removed in a future "
1295-
"version.",
1296-
FutureWarning,
1297-
stacklevel=find_stack_level(),
1298-
) # GH52116
1299-
13001292
# GH#43986 Need to do list(mapped) in order to get treated as nested
13011293
# See also GH#25959 regarding EA support
13021294
return obj._constructor_expanddim(list(mapped), index=obj.index)

Diff for: pandas/core/series.py

-5
Original file line numberDiff line numberDiff line change
@@ -4634,11 +4634,6 @@ def apply(
46344634
"""
46354635
Invoke function on values of Series.
46364636
4637-
.. deprecated:: 2.1.0
4638-
4639-
If the result from ``func`` is a ``Series``, wrapping the output in a
4640-
``DataFrame`` instead of a ``Series`` has been deprecated.
4641-
46424637
Can be ufunc (a NumPy function that applies to the entire Series)
46434638
or a Python function that only works on single values.
46444639

Diff for: pandas/tests/apply/test_series_apply.py

+17-19
Original file line numberDiff line numberDiff line change
@@ -420,15 +420,20 @@ def test_agg_evaluate_lambdas(string_series):
420420
def test_with_nested_series(datetime_series, op_name):
421421
# GH 2316
422422
# .agg with a reducer and a transform, what to do
423-
msg = "Returning a DataFrame from Series.apply when the supplied function"
424-
with tm.assert_produces_warning(FutureWarning, match=msg):
423+
msg = "cannot aggregate"
424+
warning = FutureWarning if op_name == "agg" else None
425+
with tm.assert_produces_warning(warning, match=msg):
425426
# GH52123
426427
result = getattr(datetime_series, op_name)(
427428
lambda x: Series([x, x**2], index=["x", "x^2"])
428429
)
429430
expected = DataFrame({"x": datetime_series, "x^2": datetime_series**2})
430431
tm.assert_frame_equal(result, expected)
431432

433+
with tm.assert_produces_warning(FutureWarning, match=msg):
434+
result = datetime_series.agg(lambda x: Series([x, x**2], index=["x", "x^2"]))
435+
tm.assert_frame_equal(result, expected)
436+
432437

433438
def test_replicate_describe(string_series):
434439
# this also tests a result set that is all scalars
@@ -512,10 +517,7 @@ def test_apply_series_on_date_time_index_aware_series(dti, exp, aware):
512517
index = dti.tz_localize("UTC").index
513518
else:
514519
index = dti.index
515-
msg = "Returning a DataFrame from Series.apply when the supplied function"
516-
with tm.assert_produces_warning(FutureWarning, match=msg):
517-
# GH52123
518-
result = Series(index).apply(lambda x: Series([1, 2]))
520+
result = Series(index).apply(lambda x: Series([1, 2]))
519521
tm.assert_frame_equal(result, exp)
520522

521523

@@ -662,19 +664,7 @@ def test_apply_dictlike_lambda(ops, by_row, expected):
662664
def test_apply_retains_column_name(by_row):
663665
# GH 16380
664666
df = DataFrame({"x": range(3)}, Index(range(3), name="x"))
665-
func = lambda x: Series(range(x + 1), Index(range(x + 1), name="y"))
666-
667-
if not by_row:
668-
# GH53400
669-
msg = "'Series' object cannot be interpreted as an integer"
670-
with pytest.raises(TypeError, match=msg):
671-
df.x.apply(func, by_row=by_row)
672-
return
673-
674-
msg = "Returning a DataFrame from Series.apply when the supplied function"
675-
with tm.assert_produces_warning(FutureWarning, match=msg):
676-
# GH52123
677-
result = df.x.apply(func, by_row=by_row)
667+
result = df.x.apply(lambda x: Series(range(x + 1), Index(range(x + 1), name="y")))
678668
expected = DataFrame(
679669
[[0.0, np.nan, np.nan], [0.0, 1.0, np.nan], [0.0, 1.0, 2.0]],
680670
columns=Index(range(3), name="y"),
@@ -689,3 +679,11 @@ def test_apply_type():
689679
result = s.apply(type)
690680
expected = Series([int, str, type], index=["a", "b", "c"])
691681
tm.assert_series_equal(result, expected)
682+
683+
684+
def test_series_apply_unpack_nested_data():
685+
# GH#55189
686+
ser = Series([[1, 2, 3], [4, 5, 6, 7]])
687+
result = ser.apply(lambda x: Series(x))
688+
expected = DataFrame({0: [1.0, 4.0], 1: [2.0, 5.0], 2: [3.0, 6.0], 3: [np.nan, 7]})
689+
tm.assert_frame_equal(result, expected)

0 commit comments

Comments
 (0)