From 57c791cb5095cdc95595eac585e4c6a9cab45033 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 24 Sep 2025 07:32:54 -0700 Subject: [PATCH 1/6] REF: privatize get_datevalue --- pandas/plotting/_matplotlib/converter.py | 8 ++++---- pandas/tests/plotting/test_datetimelike.py | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pandas/plotting/_matplotlib/converter.py b/pandas/plotting/_matplotlib/converter.py index 914f6343d6345..44d391e48b7e3 100644 --- a/pandas/plotting/_matplotlib/converter.py +++ b/pandas/plotting/_matplotlib/converter.py @@ -252,21 +252,21 @@ def _convert_1d(values, freq): or is_integer(values) or is_float(values) ): - return get_datevalue(values, freq) + return _get_datevalue(values, freq) elif isinstance(values, PeriodIndex): return values.asfreq(freq).asi8 elif isinstance(values, Index): - return values.map(lambda x: get_datevalue(x, freq)) + return values.map(lambda x: _get_datevalue(x, freq)) elif lib.infer_dtype(values, skipna=False) == "period": # https://github.com/pandas-dev/pandas/issues/24304 # convert ndarray[period] -> PeriodIndex return PeriodIndex(values, freq=freq).asi8 elif isinstance(values, (list, tuple, np.ndarray, Index)): - return [get_datevalue(x, freq) for x in values] + return [_get_datevalue(x, freq) for x in values] return values -def get_datevalue(date, freq): +def _get_datevalue(date, freq): if isinstance(date, Period): return date.asfreq(freq).ordinal elif isinstance(date, (str, datetime, pydt.date, pydt.time, np.datetime64)): diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index 1275f3d6f7d6d..46894fbaa6d5b 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -165,12 +165,13 @@ def test_high_freq(self, freq): _check_plot_works(ser.plot, ax=ax) def test_get_datevalue(self): - assert conv.get_datevalue(None, "D") is None - assert conv.get_datevalue(1987, "Y") == 1987 + assert conv._get_datevalue(None, "D") is None + assert conv._get_datevalue(1987, "Y") == 1987 assert ( - conv.get_datevalue(Period(1987, "Y"), "M") == Period("1987-12", "M").ordinal + conv._get_datevalue(Period(1987, "Y"), "M") + == Period("1987-12", "M").ordinal ) - assert conv.get_datevalue("1/1/1987", "D") == Period("1987-1-1", "D").ordinal + assert conv._get_datevalue("1/1/1987", "D") == Period("1987-1-1", "D").ordinal @pytest.mark.parametrize( "freq, expected_string", From 0b66b92ee7e1a79223d9f4f01655d8ccc481a42f Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 24 Sep 2025 07:45:43 -0700 Subject: [PATCH 2/6] CLN: remove checks for nonsensical types --- pandas/plotting/_matplotlib/converter.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pandas/plotting/_matplotlib/converter.py b/pandas/plotting/_matplotlib/converter.py index 44d391e48b7e3..d69bc20eda6ad 100644 --- a/pandas/plotting/_matplotlib/converter.py +++ b/pandas/plotting/_matplotlib/converter.py @@ -239,7 +239,7 @@ def convert_from_freq(values, freq): @staticmethod def _convert_1d(values, freq): - valid_types = (str, datetime, Period, pydt.date, pydt.time, np.datetime64) + valid_types = (str, datetime, Period, pydt.date, np.datetime64) with warnings.catch_warnings(): warnings.filterwarnings( "ignore", "Period with BDay freq is deprecated", category=FutureWarning @@ -261,7 +261,7 @@ def _convert_1d(values, freq): # https://github.com/pandas-dev/pandas/issues/24304 # convert ndarray[period] -> PeriodIndex return PeriodIndex(values, freq=freq).asi8 - elif isinstance(values, (list, tuple, np.ndarray, Index)): + elif isinstance(values, (list, tuple, np.ndarray)): return [_get_datevalue(x, freq) for x in values] return values @@ -269,13 +269,9 @@ def _convert_1d(values, freq): def _get_datevalue(date, freq): if isinstance(date, Period): return date.asfreq(freq).ordinal - elif isinstance(date, (str, datetime, pydt.date, pydt.time, np.datetime64)): + elif isinstance(date, (str, datetime, pydt.date, np.datetime64)): return Period(date, freq).ordinal - elif ( - is_integer(date) - or is_float(date) - or (isinstance(date, (np.ndarray, Index)) and (date.size == 1)) - ): + elif is_integer(date) or is_float(date): return date elif date is None: return None From 15b1f7cb99fe81f3c3930ae4cd66b85d31e539fa Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 24 Sep 2025 08:05:26 -0700 Subject: [PATCH 3/6] TYP: axis, freq in plotting code --- pandas/plotting/_matplotlib/converter.py | 25 ++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/pandas/plotting/_matplotlib/converter.py b/pandas/plotting/_matplotlib/converter.py index d69bc20eda6ad..c8a6a61af60ac 100644 --- a/pandas/plotting/_matplotlib/converter.py +++ b/pandas/plotting/_matplotlib/converter.py @@ -224,13 +224,20 @@ def __call__(self, x, pos: int | None = 0) -> str: class PeriodConverter(mdates.DateConverter): @staticmethod - def convert(values, units, axis): + def convert(values, unit, axis: Axis): + # Reached via e.g. `ax.set_xlim` + + # In tests as of 2025-09-24, unit is always None except for 3 tests + # that directly call this with unit=""; + # axis is always specifically a matplotlib.axis.XAxis + if not hasattr(axis, "freq"): raise TypeError("Axis must have `freq` set to convert to Periods") - return PeriodConverter.convert_from_freq(values, axis.freq) + freq = to_offset(axis.freq, is_period=True) + return PeriodConverter.convert_from_freq(values, freq) @staticmethod - def convert_from_freq(values, freq): + def convert_from_freq(values, freq: BaseOffset): if is_nested_list_like(values): values = [PeriodConverter._convert_1d(v, freq) for v in values] else: @@ -238,7 +245,7 @@ def convert_from_freq(values, freq): return values @staticmethod - def _convert_1d(values, freq): + def _convert_1d(values, freq: BaseOffset): valid_types = (str, datetime, Period, pydt.date, np.datetime64) with warnings.catch_warnings(): warnings.filterwarnings( @@ -266,7 +273,7 @@ def _convert_1d(values, freq): return values -def _get_datevalue(date, freq): +def _get_datevalue(date, freq: BaseOffset): if isinstance(date, Period): return date.asfreq(freq).ordinal elif isinstance(date, (str, datetime, pydt.date, np.datetime64)): @@ -281,7 +288,13 @@ def _get_datevalue(date, freq): # Datetime Conversion class DatetimeConverter(mdates.DateConverter): @staticmethod - def convert(values, unit, axis): + def convert(values, unit, axis: Axis): + # Reached via e.g. `ax.set_xlim` + + # In tests as of 2025-09-24, unit is always None except for 3 tests + # that directly call this with unit=""; + # axis is always specifically a matplotlib.axis.XAxis + # values might be a 1-d array, or a list-like of arrays. if is_nested_list_like(values): values = [DatetimeConverter._convert_1d(v, unit, axis) for v in values] From 56def6fb09477af43a8754934ce814a3f601be43 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 24 Sep 2025 08:49:04 -0700 Subject: [PATCH 4/6] CLN: remove unreachable --- pandas/plotting/_matplotlib/timeseries.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/pandas/plotting/_matplotlib/timeseries.py b/pandas/plotting/_matplotlib/timeseries.py index beaf5b6259ef3..b7d3304eb757b 100644 --- a/pandas/plotting/_matplotlib/timeseries.py +++ b/pandas/plotting/_matplotlib/timeseries.py @@ -10,8 +10,6 @@ ) import warnings -import numpy as np - from pandas._libs.tslibs import ( BaseOffset, Period, @@ -265,13 +263,7 @@ def _get_index_freq(index: Index) -> BaseOffset | None: freq = getattr(index, "freq", None) if freq is None: freq = getattr(index, "inferred_freq", None) - if freq == "B": - # error: "Index" has no attribute "dayofweek" - weekdays = np.unique(index.dayofweek) # type: ignore[attr-defined] - if (5 in weekdays) or (6 in weekdays): - freq = None - - freq = to_offset(freq) + freq = to_offset(freq) return freq From e63efa06c84c255dac744c5c103509dce04f757d Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 24 Sep 2025 08:56:08 -0700 Subject: [PATCH 5/6] annotation --- pandas/plotting/_matplotlib/timeseries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/plotting/_matplotlib/timeseries.py b/pandas/plotting/_matplotlib/timeseries.py index b7d3304eb757b..09eb3122b2618 100644 --- a/pandas/plotting/_matplotlib/timeseries.py +++ b/pandas/plotting/_matplotlib/timeseries.py @@ -307,7 +307,7 @@ def maybe_convert_index(ax: Axes, data: NDFrameT) -> NDFrameT: # Patch methods for subplot. -def _format_coord(freq, t, y) -> str: +def _format_coord(freq: BaseOffset, t, y) -> str: time_period = Period(ordinal=int(t), freq=freq) return f"t = {time_period} y = {y:8f}" From 9a39fba2bb10c0671c66719fdbe05ba2612ae6fe Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 24 Sep 2025 09:09:12 -0700 Subject: [PATCH 6/6] CLN: de-duplicate --- pandas/plotting/_matplotlib/timeseries.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/pandas/plotting/_matplotlib/timeseries.py b/pandas/plotting/_matplotlib/timeseries.py index 09eb3122b2618..98ac394f4e51e 100644 --- a/pandas/plotting/_matplotlib/timeseries.py +++ b/pandas/plotting/_matplotlib/timeseries.py @@ -6,7 +6,6 @@ from typing import ( TYPE_CHECKING, Any, - cast, ) import warnings @@ -271,13 +270,7 @@ def maybe_convert_index(ax: Axes, data: NDFrameT) -> NDFrameT: # tsplot converts automatically, but don't want to convert index # over and over for DataFrames if isinstance(data.index, (ABCDatetimeIndex, ABCPeriodIndex)): - freq: str | BaseOffset | None = data.index.freq - - if freq is None: - # We only get here for DatetimeIndex - data.index = cast("DatetimeIndex", data.index) - freq = data.index.inferred_freq - freq = to_offset(freq) + freq = _get_index_freq(data.index) if freq is None: freq = _get_ax_freq(ax)