-
-
Notifications
You must be signed in to change notification settings - Fork 19.1k
DEPR: implicit resample in plotting #62433
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,8 @@ | |
OFFSET_TO_PERIOD_FREQSTR, | ||
FreqGroup, | ||
) | ||
from pandas.errors import Pandas4Warning | ||
from pandas.util._exceptions import find_stack_level | ||
|
||
from pandas.core.dtypes.generic import ( | ||
ABCDatetimeIndex, | ||
|
@@ -77,6 +79,13 @@ def maybe_resample(series: Series, ax: Axes, kwargs: dict[str, Any]): | |
series = series.to_period(freq=freq) | ||
|
||
if ax_freq is not None and freq != ax_freq: | ||
warnings.warn( | ||
"Plotting with mixed-frequency series is deprecated and " | ||
"will raise in a future version. Align series frequencies " | ||
"before plotting instead.", | ||
Pandas4Warning, | ||
stacklevel=find_stack_level(), | ||
) | ||
if is_superperiod(freq, ax_freq): # upsample input | ||
series = series.copy() | ||
# error: "Index" has no attribute "asfreq" | ||
|
@@ -308,7 +317,13 @@ def maybe_convert_index(ax: Axes, data: NDFrameT) -> NDFrameT: | |
if isinstance(data.index, ABCDatetimeIndex): | ||
data = data.tz_localize(None).to_period(freq=freq_str) | ||
elif isinstance(data.index, ABCPeriodIndex): | ||
data.index = data.index.asfreq(freq=freq_str, how="start") | ||
# This will convert e.g. freq="60min" to freq="min", but will | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this bit is unrelated, can revert and do it in a separate branch |
||
# retain type(freq). It is not clear to @jbrockmendel why | ||
# this is necessary as of 2025-09-24, but 18 tests fail | ||
# without it. | ||
new_freq = to_offset(freq_str, is_period=True) | ||
assert type(new_freq) is type(data.index.freq) | ||
data.index = data.index.asfreq(freq=new_freq, how="start") | ||
return data | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
BaseOffset, | ||
to_offset, | ||
) | ||
from pandas.errors import Pandas4Warning | ||
|
||
from pandas.core.dtypes.dtypes import PeriodDtype | ||
|
||
|
@@ -627,7 +628,10 @@ def test_gap_upsample(self): | |
|
||
idxh = date_range(low.index[0], low.index[-1], freq="12h") | ||
s = Series(np.random.default_rng(2).standard_normal(len(idxh)), idxh) | ||
s.plot(secondary_y=True) | ||
|
||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
s.plot(secondary_y=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it isn't clear to me why the warning is emitted here since |
||
lines = ax.get_lines() | ||
assert len(lines) == 1 | ||
assert len(ax.right_ax.get_lines()) == 1 | ||
|
@@ -820,7 +824,9 @@ def test_mixed_freq_hf_first(self): | |
low = Series(np.random.default_rng(2).standard_normal(len(idxl)), idxl) | ||
_, ax = mpl.pyplot.subplots() | ||
high.plot(ax=ax) | ||
low.plot(ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
low.plot(ax=ax) | ||
for line in ax.get_lines(): | ||
assert PeriodIndex(data=line.get_xdata()).freq == "D" | ||
|
||
|
@@ -833,7 +839,9 @@ def test_mixed_freq_alignment(self): | |
|
||
_, ax = mpl.pyplot.subplots() | ||
ax = ts.plot(ax=ax) | ||
ts2.plot(style="r", ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
ts2.plot(style="r", ax=ax) | ||
|
||
assert ax.lines[0].get_xdata()[0] == ax.lines[1].get_xdata()[0] | ||
|
||
|
@@ -844,7 +852,9 @@ def test_mixed_freq_lf_first(self): | |
low = Series(np.random.default_rng(2).standard_normal(len(idxl)), idxl) | ||
_, ax = mpl.pyplot.subplots() | ||
low.plot(legend=True, ax=ax) | ||
high.plot(legend=True, ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
high.plot(legend=True, ax=ax) | ||
for line in ax.get_lines(): | ||
assert PeriodIndex(data=line.get_xdata()).freq == "D" | ||
leg = ax.get_legend() | ||
|
@@ -858,7 +868,9 @@ def test_mixed_freq_lf_first_hourly(self): | |
low = Series(np.random.default_rng(2).standard_normal(len(idxl)), idxl) | ||
_, ax = mpl.pyplot.subplots() | ||
low.plot(ax=ax) | ||
high.plot(ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
high.plot(ax=ax) | ||
for line in ax.get_lines(): | ||
assert PeriodIndex(data=line.get_xdata()).freq == "min" | ||
|
||
|
@@ -951,7 +963,9 @@ def test_to_weekly_resampling(self): | |
low = Series(np.random.default_rng(2).standard_normal(len(idxl)), idxl) | ||
_, ax = mpl.pyplot.subplots() | ||
high.plot(ax=ax) | ||
low.plot(ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
low.plot(ax=ax) | ||
for line in ax.get_lines(): | ||
assert PeriodIndex(data=line.get_xdata()).freq == idxh.freq | ||
|
||
|
@@ -962,7 +976,9 @@ def test_from_weekly_resampling(self): | |
low = Series(np.random.default_rng(2).standard_normal(len(idxl)), idxl) | ||
_, ax = mpl.pyplot.subplots() | ||
low.plot(ax=ax) | ||
high.plot(ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
high.plot(ax=ax) | ||
|
||
expected_h = idxh.to_period().asi8.astype(np.float64) | ||
expected_l = np.array( | ||
|
@@ -994,7 +1010,9 @@ def test_from_resampling_area_line_mixed(self, kind1, kind2): | |
|
||
_, ax = mpl.pyplot.subplots() | ||
low.plot(kind=kind1, stacked=True, ax=ax) | ||
high.plot(kind=kind2, stacked=True, ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
high.plot(kind=kind2, stacked=True, ax=ax) | ||
|
||
# check low dataframe result | ||
expected_x = np.array( | ||
|
@@ -1049,7 +1067,9 @@ def test_from_resampling_area_line_mixed_high_to_low(self, kind1, kind2): | |
) | ||
_, ax = mpl.pyplot.subplots() | ||
high.plot(kind=kind1, stacked=True, ax=ax) | ||
low.plot(kind=kind2, stacked=True, ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
low.plot(kind=kind2, stacked=True, ax=ax) | ||
|
||
# check high dataframe result | ||
expected_x = idxh.to_period().asi8.astype(np.float64) | ||
|
@@ -1096,7 +1116,9 @@ def test_mixed_freq_second_millisecond(self): | |
# high to low | ||
_, ax = mpl.pyplot.subplots() | ||
high.plot(ax=ax) | ||
low.plot(ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
low.plot(ax=ax) | ||
assert len(ax.get_lines()) == 2 | ||
for line in ax.get_lines(): | ||
assert PeriodIndex(data=line.get_xdata()).freq == "ms" | ||
|
@@ -1110,7 +1132,9 @@ def test_mixed_freq_second_millisecond_low_to_high(self): | |
# low to high | ||
_, ax = mpl.pyplot.subplots() | ||
low.plot(ax=ax) | ||
high.plot(ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
high.plot(ax=ax) | ||
assert len(ax.get_lines()) == 2 | ||
for line in ax.get_lines(): | ||
assert PeriodIndex(data=line.get_xdata()).freq == "ms" | ||
|
@@ -1247,7 +1271,9 @@ def test_secondary_upsample(self): | |
low = Series(np.random.default_rng(2).standard_normal(len(idxl)), idxl) | ||
_, ax = mpl.pyplot.subplots() | ||
low.plot(ax=ax) | ||
ax = high.plot(secondary_y=True, ax=ax) | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
ax = high.plot(secondary_y=True, ax=ax) | ||
for line in ax.get_lines(): | ||
assert PeriodIndex(line.get_xdata()).freq == "D" | ||
assert hasattr(ax, "left_ax") | ||
|
@@ -1482,7 +1508,11 @@ def test_secondary_y_mixed_freq_ts_xlim(self): | |
_, ax = mpl.pyplot.subplots() | ||
ts.plot(ax=ax) | ||
left_before, right_before = ax.get_xlim() | ||
ts.resample("D").mean().plot(secondary_y=True, ax=ax) | ||
|
||
rs = ts.resample("D").mean() | ||
msg = "Plotting with mixed-frequency series is deprecated" | ||
with tm.assert_produces_warning(Pandas4Warning, match=msg): | ||
rs.plot(secondary_y=True, ax=ax) | ||
left_after, right_after = ax.get_xlim() | ||
|
||
# a downsample should not have changed either limit | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could go as far to suggest
f"Call series.resample('{freq}') ..."
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
But now im wondering how much this will even help in moving towards #54485. In order to check if we're in this situation we still need the state information attached somehow.
I know "nuke it from space" isn't a viable approach to the plotting code, but it is really really bad.