Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
38 changes: 32 additions & 6 deletions pandas/plotting/_matplotlib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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 @@ -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)
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