diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index 0ca5b9cdf1d57..68f634bd5f85f 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -304,7 +304,7 @@ I/O Plotting ^^^^^^^^ -- +- Bug in :func:'DataFrame.plot.scatter' and :func:'DataFrame.plot.hexbin' caused x-axis label and ticklabels to disappear when colorbar was on in IPython inline backend (:issue:`10611` and :issue:`10678`) - - diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index 8c713548d1ede..8c2ee90014302 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -833,6 +833,32 @@ def _post_plot_logic(self, ax, data): ax.set_ylabel(pprint_thing(y)) ax.set_xlabel(pprint_thing(x)) + def _plot_colorbar(self, ax, **kwds): + # Addresses issues #10611 and #10678: + # When plotting scatterplots and hexbinplots in IPython + # inline backend the colorbar axis height tends not to + # exactly match the parent axis height. + # The difference is due to small fractional differences + # in floating points with similar representation. + # To deal with this, this method forces the colorbar + # height to take the height of the parent axes. + # For a more detailed description of the issue + # see the following link: + # https://github.com/ipython/ipython/issues/11215 + + img = ax.collections[0] + cbar = self.fig.colorbar(img, **kwds) + points = ax.get_position().get_points() + cbar_points = cbar.ax.get_position().get_points() + cbar.ax.set_position([cbar_points[0, 0], + points[0, 1], + cbar_points[1, 0] - cbar_points[0, 0], + points[1, 1] - points[0, 1]]) + # To see the discrepancy in axis heights uncomment + # the following two lines: + # print(points[1, 1] - points[0, 1]) + # print(cbar_points[1, 1] - cbar_points[0, 1]) + class ScatterPlot(PlanePlot): _kind = 'scatter' @@ -878,11 +904,9 @@ def _make_plot(self): scatter = ax.scatter(data[x].values, data[y].values, c=c_values, label=label, cmap=cmap, **self.kwds) if cb: - img = ax.collections[0] - kws = dict(ax=ax) if self.mpl_ge_1_3_1(): - kws['label'] = c if c_is_column else '' - self.fig.colorbar(img, **kws) + cbar_label = c if c_is_column else '' + self._plot_colorbar(ax, label=cbar_label) if label is not None: self._add_legend_handle(scatter, label) @@ -923,8 +947,7 @@ def _make_plot(self): ax.hexbin(data[x].values, data[y].values, C=c_values, cmap=cmap, **self.kwds) if cb: - img = ax.collections[0] - self.fig.colorbar(img, ax=ax) + self._plot_colorbar(ax) def _make_legend(self): pass diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index 101713b06df8c..8ef0cf7154b88 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -1089,6 +1089,49 @@ def test_plot_scatter(self): axes = df.plot(x='x', y='y', kind='scatter', subplots=True) self._check_axes_shape(axes, axes_num=1, layout=(1, 1)) + @pytest.mark.slow + def test_if_scatterplot_colorbar_affects_xaxis_visibility(self): + # addressing issue #10611, to ensure colobar does not + # interfere with x-axis label and ticklabels with + # ipython inline backend. + random_array = np.random.random((1000, 3)) + df = pd.DataFrame(random_array, + columns=['A label', 'B label', 'C label']) + + ax1 = df.plot.scatter(x='A label', y='B label') + ax2 = df.plot.scatter(x='A label', y='B label', c='C label') + + vis1 = [vis.get_visible() for vis in + ax1.xaxis.get_minorticklabels()] + vis2 = [vis.get_visible() for vis in + ax2.xaxis.get_minorticklabels()] + assert vis1 == vis2 + + vis1 = [vis.get_visible() for vis in + ax1.xaxis.get_majorticklabels()] + vis2 = [vis.get_visible() for vis in + ax2.xaxis.get_majorticklabels()] + assert vis1 == vis2 + + assert (ax1.xaxis.get_label().get_visible() == + ax2.xaxis.get_label().get_visible()) + + @pytest.mark.slow + def test_if_hexbin_xaxis_label_is_visible(self): + # addressing issue #10678, to ensure colobar does not + # interfere with x-axis label and ticklabels with + # ipython inline backend. + random_array = np.random.random((1000, 3)) + df = pd.DataFrame(random_array, + columns=['A label', 'B label', 'C label']) + + ax = df.plot.hexbin('A label', 'B label', gridsize=12) + assert all([vis.get_visible() for vis in + ax.xaxis.get_minorticklabels()]) + assert all([vis.get_visible() for vis in + ax.xaxis.get_majorticklabels()]) + assert ax.xaxis.get_label().get_visible() + @pytest.mark.slow def test_plot_scatter_with_categorical_data(self): # GH 16199