Skip to content
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ Other enhancements
- :meth:`.DataFrameGroupBy.transform`, :meth:`.SeriesGroupBy.transform`, :meth:`.DataFrameGroupBy.agg`, :meth:`.SeriesGroupBy.agg`, :meth:`.SeriesGroupBy.apply`, :meth:`.DataFrameGroupBy.apply` now support ``kurt`` (:issue:`40139`)
- :meth:`DataFrame.apply` supports using third-party execution engines like the Bodo.ai JIT compiler (:issue:`60668`)
- :meth:`DataFrame.iloc` and :meth:`Series.iloc` now support boolean masks in ``__getitem__`` for more consistent indexing behavior (:issue:`60994`)
- :meth:`DataFrame.plot.bar` and :meth:`Series.plot.bar` now use ``TimeSeries_DateFormatter`` for datetime and period indices, matching the formatting behavior of line plots (:issue:`1918`)
- :meth:`DataFrame.to_csv` and :meth:`Series.to_csv` now support Python's new-style format strings (e.g., ``"{:.6f}"``) for the ``float_format`` parameter, in addition to old-style ``%`` format strings and callables. This allows for more flexible and modern formatting of floating point numbers when exporting to CSV. (:issue:`49580`)
- :meth:`DataFrameGroupBy.transform`, :meth:`SeriesGroupBy.transform`, :meth:`DataFrameGroupBy.agg`, :meth:`SeriesGroupBy.agg`, :meth:`RollingGroupby.apply`, :meth:`ExpandingGroupby.apply`, :meth:`Rolling.apply`, :meth:`Expanding.apply`, :meth:`DataFrame.apply` with ``engine="numba"`` now supports positional arguments passed as kwargs (:issue:`58995`)
- :meth:`Rolling.agg`, :meth:`Expanding.agg` and :meth:`ExponentialMovingWindow.agg` now accept :class:`NamedAgg` aggregations through ``**kwargs`` (:issue:`28333`)
Expand Down
35 changes: 29 additions & 6 deletions pandas/plotting/_matplotlib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
from pandas.plotting._matplotlib.misc import unpack_single_str_list
from pandas.plotting._matplotlib.style import get_standard_colors
from pandas.plotting._matplotlib.timeseries import (
decorate_axes,
format_dateaxis,
maybe_convert_index,
prepare_ts_data,
Expand Down Expand Up @@ -2041,15 +2042,37 @@ 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():
decorate_axes(ax, data.index.freq)
freq = data.index.freq

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,
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/plotting/frame/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -2164,13 +2164,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"),
)

# Use a weakref so we can see if the object gets collected without
Expand Down
23 changes: 17 additions & 6 deletions pandas/tests/plotting/test_datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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()
Expand Down Expand Up @@ -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()
Expand Down
Loading