From 3699e11711fb2b38988937365f4364d37674f333 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Tue, 1 Oct 2019 18:29:01 +0200 Subject: [PATCH 01/18] TST: Test for issues #26186 and #11465 --- pandas/tests/plotting/test_frame.py | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index d2b22c7a4c2e3..a711743cfbae2 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -3440,6 +3440,40 @@ def test_xlabel_ylabel_dataframe_subplots( assert all(ax.get_ylabel() == str(new_label) for ax in axes) assert all(ax.get_xlabel() == str(new_label) for ax in axes) + @pytest.mark.slow + @pytest.mark.parametrize("method", ["bar", "barh"]) + def test_bar_ticklabel_consistence(self, method): + # Draw two consecutiv bar plot with consistent ticklabels + # GH: 26186 + def get_main_axis(ax): + if method == "barh": + return ax.yaxis + elif method == "bar": + return ax.xaxis + + data = {"A": 0, "B": 3, "C": -4} + df = pd.DataFrame.from_dict(data, orient="index", columns=["Value"]) + ax = getattr(df.plot, method)() + ax.get_figure().canvas.draw() + 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())) + df = df.sort_values("Value") * -2 + ax = getattr(df.plot, method)(ax=ax, color="red") + ax.get_figure().canvas.draw() + 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 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 + index = np.arange(10, 20) + df = pd.DataFrame(np.random.rand(10), index=np.arange(10, 20)) + ax = df.plot.bar() + ticklocs = ax.xaxis.get_ticklocs() + tm.assert_numpy_array_equal(ticklocs, index) + def _generate_4_axes_via_gridspec(): import matplotlib as mpl From 82a3110613d233a34cf468c8c5ae297015e7c1dd Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Tue, 1 Oct 2019 18:29:01 +0200 Subject: [PATCH 02/18] BUG: Generate the tick position in BarPlot using convert tools from matlab. Generate the tick position in BarPlot using convert tools from matlab. --- pandas/plotting/_matplotlib/core.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 8275c0991e464..4d4631af8d21b 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -1333,7 +1333,6 @@ 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) @@ -1356,7 +1355,7 @@ def __init__(self, data, **kwargs): self.tickoffset = self.bar_width * pos self.lim_offset = 0 - self.ax_pos = self.tick_pos - self.tickoffset + self.ax_index = self.data.index def _args_adjust(self): if is_list_like(self.bottom): @@ -1383,6 +1382,16 @@ 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) + self.ax_pos = self.tick_pos - self.tickoffset + elif self.orientation == 'horizontal': + ax.yaxis.update_units(self.ax_index) + self.tick_pos = ax.convert_yunits(self.ax_index) + self.ax_pos = self.tick_pos - self.tickoffset + kwds = self.kwds.copy() if self._is_series: kwds["color"] = colors @@ -1454,8 +1463,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[0] - 0.25 + self.lim_offset - e_edge = self.ax_pos[-1] + 0.25 + self.bar_width + self.lim_offset + 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 self._decorate_ticks(ax, name, str_index, s_edge, e_edge) From c449d59fcc4f1f81960476715ac763503ab155be Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Tue, 1 Oct 2019 18:29:02 +0200 Subject: [PATCH 03/18] TST: Modify tests/plotting/test_frame.test_bar_categorical Ticklocs are now float also for categorical bar data (as they are position on the axis). The test is changed to compare to a array of np.float. --- pandas/plotting/_matplotlib/core.py | 4 ++-- pandas/tests/plotting/test_frame.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 4d4631af8d21b..1a5b09f718f35 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -1383,11 +1383,11 @@ def _make_plot(self): for i, (label, y) in enumerate(self._iter_data(fillna=0)): ax = self._get_ax(i) - if self.orientation == 'vertical': + if self.orientation == "vertical": ax.xaxis.update_units(self.ax_index) self.tick_pos = ax.convert_xunits(self.ax_index) self.ax_pos = self.tick_pos - self.tickoffset - elif self.orientation == 'horizontal': + elif self.orientation == "horizontal": ax.yaxis.update_units(self.ax_index) self.tick_pos = ax.convert_yunits(self.ax_index) self.ax_pos = self.tick_pos - self.tickoffset diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index a711743cfbae2..94600a9180cde 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -1148,14 +1148,18 @@ def test_bar_categorical(self): for df in [df1, df2]: ax = df.plot.bar() ticks = ax.xaxis.get_ticklocs() - tm.assert_numpy_array_equal(ticks, np.array([0, 1, 2, 3, 4, 5])) + tm.assert_numpy_array_equal( + ticks, np.array([0, 1, 2, 3, 4, 5], dtype=np.float) + ) assert ax.get_xlim() == (-0.5, 5.5) # check left-edge of bars assert ax.patches[0].get_x() == -0.25 assert ax.patches[-1].get_x() == 5.15 ax = df.plot.bar(stacked=True) - tm.assert_numpy_array_equal(ticks, np.array([0, 1, 2, 3, 4, 5])) + tm.assert_numpy_array_equal( + ticks, np.array([0, 1, 2, 3, 4, 5], dtype=np.float) + ) assert ax.get_xlim() == (-0.5, 5.5) assert ax.patches[0].get_x() == -0.25 assert ax.patches[-1].get_x() == 4.75 From a1d9a538c889589f9054d8723e91c7f6897da3ab Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Tue, 1 Oct 2019 22:35:59 +0200 Subject: [PATCH 04/18] TST: Fix test for windows OS --- pandas/tests/plotting/test_frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index 94600a9180cde..7e1223ebb22ae 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -3472,7 +3472,7 @@ def test_bar_numeric(self): # Bar plot with numeric index have tick location values equal to index # values # GH: 11465 - index = np.arange(10, 20) + index = np.arange(10, 20, dtype=np.int64) df = pd.DataFrame(np.random.rand(10), index=np.arange(10, 20)) ax = df.plot.bar() ticklocs = ax.xaxis.get_ticklocs() From 383e7a426fc5afdd8ebfd3fc04f529b8a96323f2 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Tue, 1 Oct 2019 23:14:12 +0200 Subject: [PATCH 05/18] TST: Add test for plotting MultiIndex bar plot A fix to issue #26186 revealed no tests existed about plotting a bar plot for a MultiIndex, but a section of the user guide visualization did. This section of the user guide is now in the test suite. --- pandas/tests/plotting/test_frame.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index 7e1223ebb22ae..c4bf1229d6ba9 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -3478,6 +3478,33 @@ def test_bar_numeric(self): ticklocs = ax.xaxis.get_ticklocs() tm.assert_numpy_array_equal(ticklocs, index) + 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 = pd.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 + means.plot.bar(yerr=errors, capsize=4) + def _generate_4_axes_via_gridspec(): import matplotlib as mpl From 85881d173f5369db844432a16bc02698c135f434 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Thu, 3 Oct 2019 20:56:59 +0200 Subject: [PATCH 06/18] BUG: Special case for MultiIndex bar plot --- pandas/plotting/_matplotlib/core.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 1a5b09f718f35..2089a2b2b413e 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -1355,7 +1355,11 @@ def __init__(self, data, **kwargs): self.tickoffset = self.bar_width * pos self.lim_offset = 0 - self.ax_index = self.data.index + if isinstance(self.data.index, ABCMultiIndex): + # No real handling for MultiIndex yet + self.ax_index = np.arange(len(data)) + else: + self.ax_index = self.data.index def _args_adjust(self): if is_list_like(self.bottom): @@ -1384,6 +1388,8 @@ def _make_plot(self): ax = self._get_ax(i) if self.orientation == "vertical": + # import pdb + # pdb.set_trace() ax.xaxis.update_units(self.ax_index) self.tick_pos = ax.convert_xunits(self.ax_index) self.ax_pos = self.tick_pos - self.tickoffset From 86611034cf21bb36b33cc15a495831237af6f7d6 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Thu, 3 Oct 2019 21:28:27 +0200 Subject: [PATCH 07/18] DOC: Add whatsnew entry for PR #28733 --- doc/source/whatsnew/v1.0.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index 4f0ca97310d85..229306e9d42bc 100755 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -1202,6 +1202,7 @@ Plotting - Bug in color validation incorrectly raising for non-color styles (:issue:`29122`). - Allow :meth:`DataFrame.plot.scatter` to plot ``objects`` and ``datetime`` type data (:issue:`18755`, :issue:`30391`) - Bug in :meth:`DataFrame.hist`, ``xrot=0`` does not work with ``by`` and subplots (:issue:`30288`). +- Bug in BarPlot. Tick position where assigned by value order instead of using the value for numeric, or a smart order for sting. (:issue:`26186` and issue:`11465`) Groupby/resample/rolling ^^^^^^^^^^^^^^^^^^^^^^^^ From 0ef2dd57007e8b259f93a450306b0001973b62f8 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Sun, 6 Oct 2019 12:46:33 +0200 Subject: [PATCH 08/18] CLN: Clean up in code and doc --- pandas/plotting/_matplotlib/core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 2089a2b2b413e..9067d9d6d7bd5 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -1388,8 +1388,6 @@ def _make_plot(self): ax = self._get_ax(i) if self.orientation == "vertical": - # import pdb - # pdb.set_trace() ax.xaxis.update_units(self.ax_index) self.tick_pos = ax.convert_xunits(self.ax_index) self.ax_pos = self.tick_pos - self.tickoffset From e22710a2528dd3d797fe89f4da8bd19bfb65ff43 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Thu, 14 Nov 2019 10:50:28 +0100 Subject: [PATCH 09/18] CLN: Clean up test_bar_numeric --- pandas/tests/plotting/test_frame.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index c4bf1229d6ba9..cf4f13e74a3fd 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -3472,11 +3472,11 @@ def test_bar_numeric(self): # Bar plot with numeric index have tick location values equal to index # values # GH: 11465 - index = np.arange(10, 20, dtype=np.int64) df = pd.DataFrame(np.random.rand(10), index=np.arange(10, 20)) ax = df.plot.bar() ticklocs = ax.xaxis.get_ticklocs() - tm.assert_numpy_array_equal(ticklocs, index) + 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 From 52cfacbbbc0806b12a81d41ce3f5d0c7422809e2 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Wed, 12 Feb 2020 20:11:02 +0100 Subject: [PATCH 10/18] DOC Move to whatsnew v1.1 --- doc/source/whatsnew/v1.0.0.rst | 1 - doc/source/whatsnew/v1.1.0.rst | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index 229306e9d42bc..4f0ca97310d85 100755 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -1202,7 +1202,6 @@ Plotting - Bug in color validation incorrectly raising for non-color styles (:issue:`29122`). - Allow :meth:`DataFrame.plot.scatter` to plot ``objects`` and ``datetime`` type data (:issue:`18755`, :issue:`30391`) - Bug in :meth:`DataFrame.hist`, ``xrot=0`` does not work with ``by`` and subplots (:issue:`30288`). -- Bug in BarPlot. Tick position where assigned by value order instead of using the value for numeric, or a smart order for sting. (:issue:`26186` and issue:`11465`) Groupby/resample/rolling ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index a49b29d691692..dcda2340b84f4 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -1107,6 +1107,7 @@ Plotting - Bug in :meth:`DataFrame.plot.scatter` was adding a colorbar to the plot even if the argument ``c`` was assigned to a column containing color names (:issue:`34316`) - Bug in :meth:`pandas.plotting.bootstrap_plot` was causing cluttered axes and overlapping labels (:issue:`34905`) - Bug in :meth:`DataFrame.plot.scatter` caused an error when plotting variable marker sizes (:issue:`32904`) +- Bug in BarPlot. Tick position where assigned by value order instead of using the value for numeric, or a smart order for sting. (:issue:`26186` and issue:`11465`) Groupby/resample/rolling ^^^^^^^^^^^^^^^^^^^^^^^^ From 0ac15a00d9fcca51393be3b4d2eb4c0cc88550aa Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Sat, 15 Feb 2020 14:24:23 +0100 Subject: [PATCH 11/18] FIX: Make tick dtype int for backwards compatibility --- pandas/plotting/_matplotlib/core.py | 4 ++-- pandas/tests/plotting/test_frame.py | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 9067d9d6d7bd5..84fce28be7c05 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -1389,11 +1389,11 @@ def _make_plot(self): if self.orientation == "vertical": ax.xaxis.update_units(self.ax_index) - self.tick_pos = ax.convert_xunits(self.ax_index) + self.tick_pos = ax.convert_xunits(self.ax_index).astype(np.int) self.ax_pos = self.tick_pos - self.tickoffset elif self.orientation == "horizontal": ax.yaxis.update_units(self.ax_index) - self.tick_pos = ax.convert_yunits(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() diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index cf4f13e74a3fd..5701eee466405 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -1148,18 +1148,14 @@ def test_bar_categorical(self): for df in [df1, df2]: ax = df.plot.bar() ticks = ax.xaxis.get_ticklocs() - tm.assert_numpy_array_equal( - ticks, np.array([0, 1, 2, 3, 4, 5], dtype=np.float) - ) + tm.assert_numpy_array_equal(ticks, np.array([0, 1, 2, 3, 4, 5])) assert ax.get_xlim() == (-0.5, 5.5) # check left-edge of bars assert ax.patches[0].get_x() == -0.25 assert ax.patches[-1].get_x() == 5.15 ax = df.plot.bar(stacked=True) - tm.assert_numpy_array_equal( - ticks, np.array([0, 1, 2, 3, 4, 5], dtype=np.float) - ) + tm.assert_numpy_array_equal(ticks, np.array([0, 1, 2, 3, 4, 5])) assert ax.get_xlim() == (-0.5, 5.5) assert ax.patches[0].get_x() == -0.25 assert ax.patches[-1].get_x() == 4.75 From 70683b8ce623c84638585ee9eb19b3d78ff6eb9c Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Sun, 15 Mar 2020 14:31:15 +0100 Subject: [PATCH 12/18] DOC: Improve whatsnew message --- doc/source/whatsnew/v1.1.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index dcda2340b84f4..fcaf0bd674589 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -1107,7 +1107,7 @@ Plotting - Bug in :meth:`DataFrame.plot.scatter` was adding a colorbar to the plot even if the argument ``c`` was assigned to a column containing color names (:issue:`34316`) - Bug in :meth:`pandas.plotting.bootstrap_plot` was causing cluttered axes and overlapping labels (:issue:`34905`) - Bug in :meth:`DataFrame.plot.scatter` caused an error when plotting variable marker sizes (:issue:`32904`) -- Bug in BarPlot. Tick position where assigned by value order instead of using the value for numeric, or a smart order for sting. (:issue:`26186` and issue:`11465`) +- Bug in :func:`plot.bar`. Ticks position were assigned by value order instead of using the actual value for numeric, or a smart ordering for string. (:issue:`26186` and :issue:`11465`) Groupby/resample/rolling ^^^^^^^^^^^^^^^^^^^^^^^^ From 3c8b54fa3d7700a9e2534615fd4cded4b71326ee Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Sun, 15 Mar 2020 15:11:16 +0100 Subject: [PATCH 13/18] ENH: Add UserWarning when plotting bar plot with MultiIndex --- doc/source/user_guide/visualization.rst | 1 + pandas/plotting/_matplotlib/core.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/doc/source/user_guide/visualization.rst b/doc/source/user_guide/visualization.rst index 8ce4b30c717a4..098b6c09cb34d 100644 --- a/doc/source/user_guide/visualization.rst +++ b/doc/source/user_guide/visualization.rst @@ -1432,6 +1432,7 @@ Asymmetrical error bars are also supported, however raw error values must be pro Here is an example of one way to easily plot group means with standard deviations from the raw data. .. ipython:: python + :okwarning: # Generate the data ix3 = pd.MultiIndex.from_arrays([ diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 84fce28be7c05..84da4b70bc9ba 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -1357,6 +1357,9 @@ def __init__(self, data, **kwargs): if isinstance(self.data.index, ABCMultiIndex): # No real handling for MultiIndex yet + warnings.warn( + "Bar plot with a MultiIndex is not supported.", UserWarning, + ) self.ax_index = np.arange(len(data)) else: self.ax_index = self.data.index From 28f06fc683531943f850d467350a9c821a3433d7 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Sat, 13 Jun 2020 18:46:33 +0200 Subject: [PATCH 14/18] CLN: Remove duplicate code line --- pandas/plotting/_matplotlib/core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 84da4b70bc9ba..d674759389a4d 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -1393,11 +1393,10 @@ def _make_plot(self): if self.orientation == "vertical": ax.xaxis.update_units(self.ax_index) self.tick_pos = ax.convert_xunits(self.ax_index).astype(np.int) - self.ax_pos = self.tick_pos - self.tickoffset 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 + self.ax_pos = self.tick_pos - self.tickoffset kwds = self.kwds.copy() if self._is_series: From c19ef4b456273da5052f9dcce3775b07be1797e9 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Sat, 13 Jun 2020 20:02:51 +0200 Subject: [PATCH 15/18] TST: Capture UserWarning for Bar plot with MultiIndex --- pandas/tests/plotting/test_frame.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index 5701eee466405..7084ac65c0149 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -3499,7 +3499,9 @@ def test_bar_multiindex(self): errors = gp3.std() # No assertion we just ensure that we can plot a MultiIndex bar plot - means.plot.bar(yerr=errors, capsize=4) + # and are getting a UserWarning + with tm.assert_produces_warning(UserWarning): + means.plot.bar(yerr=errors, capsize=4) def _generate_4_axes_via_gridspec(): From a28db9cce16a9a0e68e4683cd745ba72103dddf2 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Sun, 14 Jun 2020 17:12:27 +0200 Subject: [PATCH 16/18] TST: Improve test explanation --- pandas/tests/plotting/test_frame.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index 7084ac65c0149..f22b54125bae5 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -3444,6 +3444,7 @@ def test_xlabel_ylabel_dataframe_subplots( @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": @@ -3451,17 +3452,26 @@ def get_main_axis(ax): elif method == "bar": return ax.xaxis + # Plot the first bar plot data = {"A": 0, "B": 3, "C": -4} df = pd.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): From 1e39ad9d7fb5c8396c831739089308cee335f1a6 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Sun, 14 Jun 2020 17:45:44 +0200 Subject: [PATCH 17/18] ENH: Raise UserWarning only if redrawing on existing axis with data --- doc/source/user_guide/visualization.rst | 1 - pandas/plotting/_matplotlib/core.py | 10 ++++++---- pandas/tests/plotting/test_frame.py | 6 ++++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/source/user_guide/visualization.rst b/doc/source/user_guide/visualization.rst index 098b6c09cb34d..8ce4b30c717a4 100644 --- a/doc/source/user_guide/visualization.rst +++ b/doc/source/user_guide/visualization.rst @@ -1432,7 +1432,6 @@ Asymmetrical error bars are also supported, however raw error values must be pro Here is an example of one way to easily plot group means with standard deviations from the raw data. .. ipython:: python - :okwarning: # Generate the data ix3 = pd.MultiIndex.from_arrays([ diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index d674759389a4d..9237a2217dfbd 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -1356,10 +1356,12 @@ def __init__(self, data, **kwargs): self.lim_offset = 0 if isinstance(self.data.index, ABCMultiIndex): - # No real handling for MultiIndex yet - warnings.warn( - "Bar plot with a MultiIndex is not supported.", UserWarning, - ) + 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 diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index f22b54125bae5..fe1ee7dcb3bed 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -3509,9 +3509,11 @@ def test_bar_multiindex(self): errors = gp3.std() # No assertion we just ensure that we can plot a MultiIndex bar plot - # and are getting a UserWarning + # 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) + means.plot.bar(yerr=errors, capsize=4, ax=ax) def _generate_4_axes_via_gridspec(): From 23635d49a393b573db73dc34e90ca440cc96cdd7 Mon Sep 17 00:00:00 2001 From: nrebena <49879400+nrebena@users.noreply.github.com> Date: Sat, 12 Sep 2020 18:24:05 +0200 Subject: [PATCH 18/18] =?UTF-8?q?DOC:=C2=A0Move=20to=20whatsnew=20v1.2.9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/source/whatsnew/v1.1.0.rst | 1 - doc/source/whatsnew/v1.2.0.rst | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index fcaf0bd674589..a49b29d691692 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -1107,7 +1107,6 @@ Plotting - Bug in :meth:`DataFrame.plot.scatter` was adding a colorbar to the plot even if the argument ``c`` was assigned to a column containing color names (:issue:`34316`) - Bug in :meth:`pandas.plotting.bootstrap_plot` was causing cluttered axes and overlapping labels (:issue:`34905`) - Bug in :meth:`DataFrame.plot.scatter` caused an error when plotting variable marker sizes (:issue:`32904`) -- Bug in :func:`plot.bar`. Ticks position were assigned by value order instead of using the actual value for numeric, or a smart ordering for string. (:issue:`26186` and :issue:`11465`) Groupby/resample/rolling ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index bce6a735b7b07..cbafef3b858df 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -302,6 +302,7 @@ Plotting ^^^^^^^^ - Bug in :meth:`DataFrame.plot` where a marker letter in the ``style`` keyword sometimes causes a ``ValueError`` (:issue:`21003`) +- Bug in :func:`DataFrame.plot.bar` and :func:`Series.plot.bar`. Ticks position were assigned by value order instead of using the actual value for numeric, or a smart ordering for string. (:issue:`26186` and :issue:`11465`) Groupby/resample/rolling ^^^^^^^^^^^^^^^^^^^^^^^^