Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Inconsistent indexes for tick label plotting (#28733)" #39235

Merged
merged 10 commits into from
Jan 18, 2021
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`)
simonjayhawkins marked this conversation as resolved.
Show resolved Hide resolved
- 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
4 changes: 4 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,10 @@ 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`)

simonjayhawkins marked this conversation as resolved.
Show resolved Hide resolved
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