Skip to content
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v1.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ Plotting

- Bug in :meth:`DataFrame.plot` was rotating xticklabels when ``subplots=True``, even if the x-axis wasn't an irregular time series (:issue:`29460`)
- Bug in :meth:`DataFrame.plot` where a marker letter in the ``style`` keyword sometimes caused a ``ValueError`` (:issue:`21003`)
- Bug in :meth:`DataFrame.plot.bar` and :meth:`Series.plot.bar` where ticks positions were assigned by value order instead of using the actual value for numeric or a smart ordering for string (:issue:`26186`, :issue:`11465`)
- Bug in :meth:`DataFrame.plot.bar` and :meth:`Series.plot.bar` where ticks positions were assigned by value order instead of using the actual value for numeric or a smart ordering for string (:issue:`26186`, :issue:`11465`). This fix has been reverted in pandas 1.2.1, see :doc:`v1.2.1`
- Twinned axes were losing their tick labels which should only happen to all but the last row or column of 'externally' shared axes (:issue:`33819`)
- Bug in :meth:`Series.plot` and :meth:`DataFrame.plot` was throwing a :exc:`ValueError` when the Series or DataFrame was
indexed by a :class:`.TimedeltaIndex` with a fixed frequency and the x-axis lower limit was greater than the upper limit (:issue:`37454`)
Expand Down
3 changes: 3 additions & 0 deletions doc/source/whatsnew/v1.2.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ Fixed regressions
- Fixed regression in comparisons between ``NaT`` and ``datetime.date`` objects incorrectly returning ``True`` (:issue:`39151`)
- Fixed regression in :func:`pandas.testing.assert_index_equal` raising ``TypeError`` with ``check_order=False`` when :class:`Index` has mixed dtype (:issue:`39168`)

We have reverted a commit that resulted in several plotting related regressions in pandas 1.2 (:issue:`38969`, :issue:`38736`, :issue:`38865`, :issue:`38947` and :issue:`39126`).
As a result, bugs reported as fixed in pandas 1.2 related to inconsistent tick labeling in bar plots are again present (:issue:`26186` and :issue:`11465`)

.. ---------------------------------------------------------------------------

.. _whatsnew_121.bug_fixes:
Expand Down
25 changes: 4 additions & 21 deletions pandas/plotting/_matplotlib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1369,6 +1369,7 @@ def __init__(self, data, **kwargs):
self.bar_width = kwargs.pop("width", 0.5)
pos = kwargs.pop("position", 0.5)
kwargs.setdefault("align", "center")
self.tick_pos = np.arange(len(data))

self.bottom = kwargs.pop("bottom", 0)
self.left = kwargs.pop("left", 0)
Expand All @@ -1391,16 +1392,7 @@ def __init__(self, data, **kwargs):
self.tickoffset = self.bar_width * pos
self.lim_offset = 0

if isinstance(self.data.index, ABCMultiIndex):
if kwargs["ax"] is not None and kwargs["ax"].has_data():
warnings.warn(
"Redrawing a bar plot with a MultiIndex is not supported "
+ "and may lead to inconsistent label positions.",
UserWarning,
)
self.ax_index = np.arange(len(data))
else:
self.ax_index = self.data.index
self.ax_pos = self.tick_pos - self.tickoffset

def _args_adjust(self):
if is_list_like(self.bottom):
Expand All @@ -1427,15 +1419,6 @@ def _make_plot(self):

for i, (label, y) in enumerate(self._iter_data(fillna=0)):
ax = self._get_ax(i)

if self.orientation == "vertical":
ax.xaxis.update_units(self.ax_index)
self.tick_pos = ax.convert_xunits(self.ax_index).astype(np.int)
elif self.orientation == "horizontal":
ax.yaxis.update_units(self.ax_index)
self.tick_pos = ax.convert_yunits(self.ax_index).astype(np.int)
self.ax_pos = self.tick_pos - self.tickoffset

kwds = self.kwds.copy()
if self._is_series:
kwds["color"] = colors
Expand Down Expand Up @@ -1507,8 +1490,8 @@ def _post_plot_logic(self, ax: "Axes", data):
str_index = [pprint_thing(key) for key in range(data.shape[0])]
name = self._get_index_name()

s_edge = self.ax_pos.min() - 0.25 + self.lim_offset
e_edge = self.ax_pos.max() + 0.25 + self.bar_width + self.lim_offset
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, name, str_index, s_edge, e_edge)

Expand Down
74 changes: 0 additions & 74 deletions pandas/tests/plotting/frame/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -2191,80 +2191,6 @@ def test_xlabel_ylabel_dataframe_plane_plot(self, kind, xlabel, ylabel):
assert ax.get_xlabel() == (xcol if xlabel is None else xlabel)
assert ax.get_ylabel() == (ycol if ylabel is None else ylabel)

@pytest.mark.parametrize("method", ["bar", "barh"])
def test_bar_ticklabel_consistence(self, method):
# Draw two consecutiv bar plot with consistent ticklabels
# The labels positions should not move between two drawing on the same axis
# GH: 26186
def get_main_axis(ax):
if method == "barh":
return ax.yaxis
elif method == "bar":
return ax.xaxis

# Plot the first bar plot
data = {"A": 0, "B": 3, "C": -4}
df = DataFrame.from_dict(data, orient="index", columns=["Value"])
ax = getattr(df.plot, method)()
ax.get_figure().canvas.draw()

# Retrieve the label positions for the first drawing
xticklabels = [t.get_text() for t in get_main_axis(ax).get_ticklabels()]
label_positions_1 = dict(zip(xticklabels, get_main_axis(ax).get_ticklocs()))

# Modify the dataframe order and values and plot on same axis
df = df.sort_values("Value") * -2
ax = getattr(df.plot, method)(ax=ax, color="red")
ax.get_figure().canvas.draw()

# Retrieve the label positions for the second drawing
xticklabels = [t.get_text() for t in get_main_axis(ax).get_ticklabels()]
label_positions_2 = dict(zip(xticklabels, get_main_axis(ax).get_ticklocs()))

# Assert that the label positions did not change between the plotting
assert label_positions_1 == label_positions_2

def test_bar_numeric(self):
# Bar plot with numeric index have tick location values equal to index
# values
# GH: 11465
df = DataFrame(np.random.rand(10), index=np.arange(10, 20))
ax = df.plot.bar()
ticklocs = ax.xaxis.get_ticklocs()
expected = np.arange(10, 20, dtype=np.int64)
tm.assert_numpy_array_equal(ticklocs, expected)

def test_bar_multiindex(self):
# Test from pandas/doc/source/user_guide/visualization.rst
# at section Plotting With Error Bars
# Related to issue GH: 26186

ix3 = pd.MultiIndex.from_arrays(
[
["a", "a", "a", "a", "b", "b", "b", "b"],
["foo", "foo", "bar", "bar", "foo", "foo", "bar", "bar"],
],
names=["letter", "word"],
)

df3 = DataFrame(
{"data1": [3, 2, 4, 3, 2, 4, 3, 2], "data2": [6, 5, 7, 5, 4, 5, 6, 5]},
index=ix3,
)

# Group by index labels and take the means and standard deviations
# for each group
gp3 = df3.groupby(level=("letter", "word"))
means = gp3.mean()
errors = gp3.std()

# No assertion we just ensure that we can plot a MultiIndex bar plot
# and are getting a UserWarning if redrawing
with tm.assert_produces_warning(None):
ax = means.plot.bar(yerr=errors, capsize=4)
with tm.assert_produces_warning(UserWarning):
means.plot.bar(yerr=errors, capsize=4, ax=ax)


def _generate_4_axes_via_gridspec():
import matplotlib as mpl
Expand Down