diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 0834501c4429d..b701051a98ed2 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -2041,15 +2041,41 @@ def _make_plot(self, fig: Figure) -> None: self._append_legend_handles_labels(rect, label) def _post_plot_logic(self, ax: Axes, data) -> None: - if self.use_index: - str_index = [pprint_thing(key) for key in data.index] - else: - str_index = [pprint_thing(key) for key in range(data.shape[0])] - s_edge = self.ax_pos[0] - 0.25 + self.lim_offset e_edge = self.ax_pos[-1] + 0.25 + self.bar_width + self.lim_offset - self._decorate_ticks(ax, self._get_index_name(), str_index, s_edge, e_edge) + # GH#1918: Apply date formatter for time series indices + if self._is_ts_plot(): + freq = data.index.freq + # Set freq on axis for formatter to use, but don't call decorate_axes + # to avoid adding _plot_data attribute (which bar plots shouldn't have) + ax.freq = freq # type: ignore[attr-defined] + xaxis = ax.get_xaxis() + xaxis.freq = freq # type: ignore[attr-defined] + + index = data.index + if isinstance(index, ABCDatetimeIndex): + index = index.to_period(freq=freq) + + if isinstance(index, ABCPeriodIndex): + format_dateaxis(ax, freq, index) + + ax.set_xlim((s_edge, e_edge)) + if self.xticks is not None: + ax.set_xticks(np.array(self.xticks)) + else: + ax.set_xticks(self.tick_pos) + + index_name = self._get_index_name() + if index_name is not None and self.use_index: + ax.set_xlabel(index_name) + else: + if self.use_index: + str_index = [pprint_thing(key) for key in data.index] + else: + str_index = [pprint_thing(key) for key in range(data.shape[0])] + + self._decorate_ticks(ax, self._get_index_name(), str_index, s_edge, e_edge) def _decorate_ticks( self, diff --git a/pandas/tests/plotting/frame/test_frame.py b/pandas/tests/plotting/frame/test_frame.py index 412909b8fadf5..08815d28a4308 100644 --- a/pandas/tests/plotting/frame/test_frame.py +++ b/pandas/tests/plotting/frame/test_frame.py @@ -2162,13 +2162,13 @@ def test_memory_leak(self, kind): df = DataFrame( np.random.default_rng(2).standard_normal((10, 4)), columns=Index(list("ABCD"), dtype=object), - index=date_range("2000-01-01", periods=10, freq="B"), + index=date_range("2000-01-01", periods=10, freq="D"), ).abs() else: df = DataFrame( np.random.default_rng(2).standard_normal((10, 4)), columns=Index(list("ABCD"), dtype=object), - index=date_range("2000-01-01", periods=10, freq="B"), + index=date_range("2000-01-01", periods=10, freq="D"), ) ax = df.plot(kind=kind, **args) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index 8af3c9d996475..13d61227bba0f 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -1264,7 +1264,7 @@ def test_secondary_legend(self): df = DataFrame( np.random.default_rng(2).standard_normal((10, 4)), columns=Index(list("ABCD"), dtype=object), - index=date_range("2000-01-01", periods=10, freq="B"), + index=date_range("2000-01-01", periods=10, freq="D"), ) df.plot(secondary_y=["A", "B"], ax=ax) leg = ax.get_legend() @@ -1285,7 +1285,7 @@ def test_secondary_legend_right(self): df = DataFrame( np.random.default_rng(2).standard_normal((10, 4)), columns=Index(list("ABCD"), dtype=object), - index=date_range("2000-01-01", periods=10, freq="B"), + index=date_range("2000-01-01", periods=10, freq="D"), ) fig = mpl.pyplot.figure() ax = fig.add_subplot(211) @@ -1301,7 +1301,7 @@ def test_secondary_legend_bar(self): df = DataFrame( np.random.default_rng(2).standard_normal((10, 4)), columns=Index(list("ABCD"), dtype=object), - index=date_range("2000-01-01", periods=10, freq="B"), + index=date_range("2000-01-01", periods=10, freq="D"), ) fig, ax = mpl.pyplot.subplots() df.plot(kind="bar", secondary_y=["A"], ax=ax) @@ -1313,7 +1313,7 @@ def test_secondary_legend_bar_right(self): df = DataFrame( np.random.default_rng(2).standard_normal((10, 4)), columns=Index(list("ABCD"), dtype=object), - index=date_range("2000-01-01", periods=10, freq="B"), + index=date_range("2000-01-01", periods=10, freq="D"), ) fig, ax = mpl.pyplot.subplots() df.plot(kind="bar", secondary_y=["A"], mark_right=False, ax=ax) @@ -1325,14 +1325,14 @@ def test_secondary_legend_multi_col(self): df = DataFrame( np.random.default_rng(2).standard_normal((10, 4)), columns=Index(list("ABCD"), dtype=object), - index=date_range("2000-01-01", periods=10, freq="B"), + index=date_range("2000-01-01", periods=10, freq="D"), ) fig = mpl.pyplot.figure() ax = fig.add_subplot(211) df = DataFrame( np.random.default_rng(2).standard_normal((10, 4)), columns=Index(list("ABCD"), dtype=object), - index=date_range("2000-01-01", periods=10, freq="B"), + index=date_range("2000-01-01", periods=10, freq="D"), ) ax = df.plot(secondary_y=["C", "D"], ax=ax) leg = ax.get_legend() @@ -1691,6 +1691,17 @@ def test_pickle_fig(self, temp_file, frame_or_series, idx): with temp_file.open(mode="wb") as path: pickle.dump(fig, path) + def test_bar_plot_with_datetime_index_uses_date_formatter(self): + # GH#1918 - bar plots should use DateFormatter for datetime indices + df = DataFrame( + np.random.default_rng(2).standard_normal((10, 2)), + index=date_range("2020-01-01", periods=10), + columns=["A", "B"], + ) + ax_bar = df.plot(kind="bar") + bar_formatter = ax_bar.get_xaxis().get_major_formatter() + assert isinstance(bar_formatter, conv.TimeSeries_DateFormatter) + def _check_plot_works(f, freq=None, series=None, *args, **kwargs): fig = plt.gcf()