""" Test cases for .hist method """ import re import numpy as np import pytest import pandas.util._test_decorators as td from pandas import ( DataFrame, Index, Series, to_datetime, ) import pandas._testing as tm from pandas.tests.plotting.common import ( TestPlotBase, _check_plot_works, ) @pytest.fixture def ts(): return tm.makeTimeSeries(name="ts") @td.skip_if_no_mpl class TestSeriesPlots(TestPlotBase): def test_hist_legacy(self, ts): _check_plot_works(ts.hist) _check_plot_works(ts.hist, grid=False) _check_plot_works(ts.hist, figsize=(8, 10)) # _check_plot_works adds an ax so catch warning. see GH #13188 with tm.assert_produces_warning(UserWarning, check_stacklevel=False): _check_plot_works(ts.hist, by=ts.index.month) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): _check_plot_works(ts.hist, by=ts.index.month, bins=5) fig, ax = self.plt.subplots(1, 1) _check_plot_works(ts.hist, ax=ax, default_axes=True) _check_plot_works(ts.hist, ax=ax, figure=fig, default_axes=True) _check_plot_works(ts.hist, figure=fig, default_axes=True) tm.close() fig, (ax1, ax2) = self.plt.subplots(1, 2) _check_plot_works(ts.hist, figure=fig, ax=ax1, default_axes=True) _check_plot_works(ts.hist, figure=fig, ax=ax2, default_axes=True) msg = ( "Cannot pass 'figure' when using the 'by' argument, since a new 'Figure' " "instance will be created" ) with pytest.raises(ValueError, match=msg): ts.hist(by=ts.index, figure=fig) def test_hist_bins_legacy(self): df = DataFrame(np.random.randn(10, 2)) ax = df.hist(bins=2)[0][0] assert len(ax.patches) == 2 def test_hist_layout(self, hist_df): df = hist_df msg = "The 'layout' keyword is not supported when 'by' is None" with pytest.raises(ValueError, match=msg): df.height.hist(layout=(1, 1)) with pytest.raises(ValueError, match=msg): df.height.hist(layout=[1, 1]) @pytest.mark.slow def test_hist_layout_with_by(self, hist_df): df = hist_df # _check_plot_works adds an `ax` kwarg to the method call # so we get a warning about an axis being cleared, even # though we don't explicing pass one, see GH #13188 with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(df.height.hist, by=df.gender, layout=(2, 1)) self._check_axes_shape(axes, axes_num=2, layout=(2, 1)) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(df.height.hist, by=df.gender, layout=(3, -1)) self._check_axes_shape(axes, axes_num=2, layout=(3, 1)) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(df.height.hist, by=df.category, layout=(4, 1)) self._check_axes_shape(axes, axes_num=4, layout=(4, 1)) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(df.height.hist, by=df.category, layout=(2, -1)) self._check_axes_shape(axes, axes_num=4, layout=(2, 2)) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(df.height.hist, by=df.category, layout=(3, -1)) self._check_axes_shape(axes, axes_num=4, layout=(3, 2)) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(df.height.hist, by=df.category, layout=(-1, 4)) self._check_axes_shape(axes, axes_num=4, layout=(1, 4)) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(df.height.hist, by=df.classroom, layout=(2, 2)) self._check_axes_shape(axes, axes_num=3, layout=(2, 2)) axes = df.height.hist(by=df.category, layout=(4, 2), figsize=(12, 7)) self._check_axes_shape(axes, axes_num=4, layout=(4, 2), figsize=(12, 7)) def test_hist_no_overlap(self): from matplotlib.pyplot import ( gcf, subplot, ) x = Series(np.random.randn(2)) y = Series(np.random.randn(2)) subplot(121) x.hist() subplot(122) y.hist() fig = gcf() axes = fig.axes assert len(axes) == 2 def test_hist_by_no_extra_plots(self, hist_df): df = hist_df axes = df.height.hist(by=df.gender) # noqa assert len(self.plt.get_fignums()) == 1 def test_plot_fails_when_ax_differs_from_figure(self, ts): from pylab import figure fig1 = figure() fig2 = figure() ax1 = fig1.add_subplot(111) msg = "passed axis not bound to passed figure" with pytest.raises(AssertionError, match=msg): ts.hist(ax=ax1, figure=fig2) @pytest.mark.parametrize( "histtype, expected", [ ("bar", True), ("barstacked", True), ("step", False), ("stepfilled", True), ], ) def test_histtype_argument(self, histtype, expected): # GH23992 Verify functioning of histtype argument ser = Series(np.random.randint(1, 10)) ax = ser.hist(histtype=histtype) self._check_patches_all_filled(ax, filled=expected) @pytest.mark.parametrize( "by, expected_axes_num, expected_layout", [(None, 1, (1, 1)), ("b", 2, (1, 2))] ) def test_hist_with_legend(self, by, expected_axes_num, expected_layout): # GH 6279 - Series histogram can have a legend index = 15 * ["1"] + 15 * ["2"] s = Series(np.random.randn(30), index=index, name="a") s.index.name = "b" # Use default_axes=True when plotting method generate subplots itself axes = _check_plot_works(s.hist, default_axes=True, legend=True, by=by) self._check_axes_shape(axes, axes_num=expected_axes_num, layout=expected_layout) self._check_legend_labels(axes, "a") @pytest.mark.parametrize("by", [None, "b"]) def test_hist_with_legend_raises(self, by): # GH 6279 - Series histogram with legend and label raises index = 15 * ["1"] + 15 * ["2"] s = Series(np.random.randn(30), index=index, name="a") s.index.name = "b" with pytest.raises(ValueError, match="Cannot use both legend and label"): s.hist(legend=True, by=by, label="c") def test_hist_kwargs(self, ts): _, ax = self.plt.subplots() ax = ts.plot.hist(bins=5, ax=ax) assert len(ax.patches) == 5 self._check_text_labels(ax.yaxis.get_label(), "Frequency") tm.close() _, ax = self.plt.subplots() ax = ts.plot.hist(orientation="horizontal", ax=ax) self._check_text_labels(ax.xaxis.get_label(), "Frequency") tm.close() _, ax = self.plt.subplots() ax = ts.plot.hist(align="left", stacked=True, ax=ax) tm.close() @pytest.mark.xfail(reason="Api changed in 3.6.0") @td.skip_if_no_scipy def test_hist_kde(self, ts): _, ax = self.plt.subplots() ax = ts.plot.hist(logy=True, ax=ax) self._check_ax_scales(ax, yaxis="log") xlabels = ax.get_xticklabels() # ticks are values, thus ticklabels are blank self._check_text_labels(xlabels, [""] * len(xlabels)) ylabels = ax.get_yticklabels() self._check_text_labels(ylabels, [""] * len(ylabels)) _check_plot_works(ts.plot.kde) _check_plot_works(ts.plot.density) _, ax = self.plt.subplots() ax = ts.plot.kde(logy=True, ax=ax) self._check_ax_scales(ax, yaxis="log") xlabels = ax.get_xticklabels() self._check_text_labels(xlabels, [""] * len(xlabels)) ylabels = ax.get_yticklabels() self._check_text_labels(ylabels, [""] * len(ylabels)) @td.skip_if_no_scipy def test_hist_kde_color(self, ts): _, ax = self.plt.subplots() ax = ts.plot.hist(logy=True, bins=10, color="b", ax=ax) self._check_ax_scales(ax, yaxis="log") assert len(ax.patches) == 10 self._check_colors(ax.patches, facecolors=["b"] * 10) _, ax = self.plt.subplots() ax = ts.plot.kde(logy=True, color="r", ax=ax) self._check_ax_scales(ax, yaxis="log") lines = ax.get_lines() assert len(lines) == 1 self._check_colors(lines, ["r"]) @td.skip_if_no_mpl class TestDataFramePlots(TestPlotBase): @pytest.mark.slow def test_hist_df_legacy(self, hist_df): from matplotlib.patches import Rectangle with tm.assert_produces_warning(UserWarning, check_stacklevel=False): _check_plot_works(hist_df.hist) # make sure layout is handled df = DataFrame(np.random.randn(100, 2)) df[2] = to_datetime( np.random.randint( 812419200000000000, 819331200000000000, size=100, dtype=np.int64, ) ) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(df.hist, grid=False) self._check_axes_shape(axes, axes_num=3, layout=(2, 2)) assert not axes[1, 1].get_visible() _check_plot_works(df[[2]].hist) df = DataFrame(np.random.randn(100, 1)) _check_plot_works(df.hist) # make sure layout is handled df = DataFrame(np.random.randn(100, 5)) df[5] = to_datetime( np.random.randint( 812419200000000000, 819331200000000000, size=100, dtype=np.int64, ) ) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(df.hist, layout=(4, 2)) self._check_axes_shape(axes, axes_num=6, layout=(4, 2)) # make sure sharex, sharey is handled with tm.assert_produces_warning(UserWarning, check_stacklevel=False): _check_plot_works(df.hist, sharex=True, sharey=True) # handle figsize arg with tm.assert_produces_warning(UserWarning, check_stacklevel=False): _check_plot_works(df.hist, figsize=(8, 10)) # check bins argument with tm.assert_produces_warning(UserWarning, check_stacklevel=False): _check_plot_works(df.hist, bins=5) # make sure xlabelsize and xrot are handled ser = df[0] xf, yf = 20, 18 xrot, yrot = 30, 40 axes = ser.hist(xlabelsize=xf, xrot=xrot, ylabelsize=yf, yrot=yrot) self._check_ticks_props( axes, xlabelsize=xf, xrot=xrot, ylabelsize=yf, yrot=yrot ) xf, yf = 20, 18 xrot, yrot = 30, 40 axes = df.hist(xlabelsize=xf, xrot=xrot, ylabelsize=yf, yrot=yrot) self._check_ticks_props( axes, xlabelsize=xf, xrot=xrot, ylabelsize=yf, yrot=yrot ) tm.close() ax = ser.hist(cumulative=True, bins=4, density=True) # height of last bin (index 5) must be 1.0 rects = [x for x in ax.get_children() if isinstance(x, Rectangle)] tm.assert_almost_equal(rects[-1].get_height(), 1.0) tm.close() ax = ser.hist(log=True) # scale of y must be 'log' self._check_ax_scales(ax, yaxis="log") tm.close() # propagate attr exception from matplotlib.Axes.hist with tm.external_error_raised(AttributeError): ser.hist(foo="bar") def test_hist_non_numerical_or_datetime_raises(self): # gh-10444, GH32590 df = DataFrame( { "a": np.random.rand(10), "b": np.random.randint(0, 10, 10), "c": to_datetime( np.random.randint( 1582800000000000000, 1583500000000000000, 10, dtype=np.int64 ) ), "d": to_datetime( np.random.randint( 1582800000000000000, 1583500000000000000, 10, dtype=np.int64 ), utc=True, ), } ) df_o = df.astype(object) msg = "hist method requires numerical or datetime columns, nothing to plot." with pytest.raises(ValueError, match=msg): df_o.hist() def test_hist_layout(self): df = DataFrame(np.random.randn(100, 2)) df[2] = to_datetime( np.random.randint( 812419200000000000, 819331200000000000, size=100, dtype=np.int64, ) ) layout_to_expected_size = ( {"layout": None, "expected_size": (2, 2)}, # default is 2x2 {"layout": (2, 2), "expected_size": (2, 2)}, {"layout": (4, 1), "expected_size": (4, 1)}, {"layout": (1, 4), "expected_size": (1, 4)}, {"layout": (3, 3), "expected_size": (3, 3)}, {"layout": (-1, 4), "expected_size": (1, 4)}, {"layout": (4, -1), "expected_size": (4, 1)}, {"layout": (-1, 2), "expected_size": (2, 2)}, {"layout": (2, -1), "expected_size": (2, 2)}, ) for layout_test in layout_to_expected_size: axes = df.hist(layout=layout_test["layout"]) expected = layout_test["expected_size"] self._check_axes_shape(axes, axes_num=3, layout=expected) # layout too small for all 4 plots msg = "Layout of 1x1 must be larger than required size 3" with pytest.raises(ValueError, match=msg): df.hist(layout=(1, 1)) # invalid format for layout msg = re.escape("Layout must be a tuple of (rows, columns)") with pytest.raises(ValueError, match=msg): df.hist(layout=(1,)) msg = "At least one dimension of layout must be positive" with pytest.raises(ValueError, match=msg): df.hist(layout=(-1, -1)) # GH 9351 def test_tight_layout(self): df = DataFrame(np.random.randn(100, 2)) df[2] = to_datetime( np.random.randint( 812419200000000000, 819331200000000000, size=100, dtype=np.int64, ) ) # Use default_axes=True when plotting method generate subplots itself _check_plot_works(df.hist, default_axes=True) self.plt.tight_layout() tm.close() def test_hist_subplot_xrot(self): # GH 30288 df = DataFrame( { "length": [1.5, 0.5, 1.2, 0.9, 3], "animal": ["pig", "rabbit", "pig", "pig", "rabbit"], } ) # Use default_axes=True when plotting method generate subplots itself axes = _check_plot_works( df.hist, default_axes=True, column="length", by="animal", bins=5, xrot=0, ) self._check_ticks_props(axes, xrot=0) @pytest.mark.parametrize( "column, expected", [ (None, ["width", "length", "height"]), (["length", "width", "height"], ["length", "width", "height"]), ], ) def test_hist_column_order_unchanged(self, column, expected): # GH29235 df = DataFrame( { "width": [0.7, 0.2, 0.15, 0.2, 1.1], "length": [1.5, 0.5, 1.2, 0.9, 3], "height": [3, 0.5, 3.4, 2, 1], }, index=["pig", "rabbit", "duck", "chicken", "horse"], ) # Use default_axes=True when plotting method generate subplots itself axes = _check_plot_works( df.hist, default_axes=True, column=column, layout=(1, 3), ) result = [axes[0, i].get_title() for i in range(3)] assert result == expected @pytest.mark.parametrize( "histtype, expected", [ ("bar", True), ("barstacked", True), ("step", False), ("stepfilled", True), ], ) def test_histtype_argument(self, histtype, expected): # GH23992 Verify functioning of histtype argument df = DataFrame(np.random.randint(1, 10, size=(100, 2)), columns=["a", "b"]) ax = df.hist(histtype=histtype) self._check_patches_all_filled(ax, filled=expected) @pytest.mark.parametrize("by", [None, "c"]) @pytest.mark.parametrize("column", [None, "b"]) def test_hist_with_legend(self, by, column): # GH 6279 - DataFrame histogram can have a legend expected_axes_num = 1 if by is None and column is not None else 2 expected_layout = (1, expected_axes_num) expected_labels = column or ["a", "b"] if by is not None: expected_labels = [expected_labels] * 2 index = Index(15 * ["1"] + 15 * ["2"], name="c") df = DataFrame(np.random.randn(30, 2), index=index, columns=["a", "b"]) # Use default_axes=True when plotting method generate subplots itself axes = _check_plot_works( df.hist, default_axes=True, legend=True, by=by, column=column, ) self._check_axes_shape(axes, axes_num=expected_axes_num, layout=expected_layout) if by is None and column is None: axes = axes[0] for expected_label, ax in zip(expected_labels, axes): self._check_legend_labels(ax, expected_label) @pytest.mark.parametrize("by", [None, "c"]) @pytest.mark.parametrize("column", [None, "b"]) def test_hist_with_legend_raises(self, by, column): # GH 6279 - DataFrame histogram with legend and label raises index = Index(15 * ["1"] + 15 * ["2"], name="c") df = DataFrame(np.random.randn(30, 2), index=index, columns=["a", "b"]) with pytest.raises(ValueError, match="Cannot use both legend and label"): df.hist(legend=True, by=by, column=column, label="d") def test_hist_df_kwargs(self): df = DataFrame(np.random.randn(10, 2)) _, ax = self.plt.subplots() ax = df.plot.hist(bins=5, ax=ax) assert len(ax.patches) == 10 def test_hist_df_with_nonnumerics(self): # GH 9853 df = DataFrame( np.random.RandomState(42).randn(10, 4), columns=["A", "B", "C", "D"] ) df["E"] = ["x", "y"] * 5 _, ax = self.plt.subplots() ax = df.plot.hist(bins=5, ax=ax) assert len(ax.patches) == 20 _, ax = self.plt.subplots() ax = df.plot.hist(ax=ax) # bins=10 assert len(ax.patches) == 40 def test_hist_secondary_legend(self): # GH 9610 df = DataFrame(np.random.randn(30, 4), columns=list("abcd")) # primary -> secondary _, ax = self.plt.subplots() ax = df["a"].plot.hist(legend=True, ax=ax) df["b"].plot.hist(ax=ax, legend=True, secondary_y=True) # both legends are drawn on left ax # left and right axis must be visible self._check_legend_labels(ax, labels=["a", "b (right)"]) assert ax.get_yaxis().get_visible() assert ax.right_ax.get_yaxis().get_visible() tm.close() # secondary -> secondary _, ax = self.plt.subplots() ax = df["a"].plot.hist(legend=True, secondary_y=True, ax=ax) df["b"].plot.hist(ax=ax, legend=True, secondary_y=True) # both legends are draw on left ax # left axis must be invisible, right axis must be visible self._check_legend_labels(ax.left_ax, labels=["a (right)", "b (right)"]) assert not ax.left_ax.get_yaxis().get_visible() assert ax.get_yaxis().get_visible() tm.close() # secondary -> primary _, ax = self.plt.subplots() ax = df["a"].plot.hist(legend=True, secondary_y=True, ax=ax) # right axes is returned df["b"].plot.hist(ax=ax, legend=True) # both legends are draw on left ax # left and right axis must be visible self._check_legend_labels(ax.left_ax, labels=["a (right)", "b"]) assert ax.left_ax.get_yaxis().get_visible() assert ax.get_yaxis().get_visible() tm.close() @td.skip_if_no_mpl def test_hist_with_nans_and_weights(self): # GH 48884 df = DataFrame( [[np.nan, 0.2, 0.3], [0.4, np.nan, np.nan], [0.7, 0.8, 0.9]], columns=list("abc"), ) weights = np.array([0.25, 0.3, 0.45]) no_nan_df = DataFrame([[0.4, 0.2, 0.3], [0.7, 0.8, 0.9]], columns=list("abc")) no_nan_weights = np.array([[0.3, 0.25, 0.25], [0.45, 0.45, 0.45]]) from matplotlib.patches import Rectangle _, ax0 = self.plt.subplots() df.plot.hist(ax=ax0, weights=weights) rects = [x for x in ax0.get_children() if isinstance(x, Rectangle)] heights = [rect.get_height() for rect in rects] _, ax1 = self.plt.subplots() no_nan_df.plot.hist(ax=ax1, weights=no_nan_weights) no_nan_rects = [x for x in ax1.get_children() if isinstance(x, Rectangle)] no_nan_heights = [rect.get_height() for rect in no_nan_rects] assert all(h0 == h1 for h0, h1 in zip(heights, no_nan_heights)) idxerror_weights = np.array([[0.3, 0.25], [0.45, 0.45]]) msg = "weights must have the same shape as data, or be a single column" with pytest.raises(ValueError, match=msg): _, ax2 = self.plt.subplots() no_nan_df.plot.hist(ax=ax2, weights=idxerror_weights) @td.skip_if_no_mpl class TestDataFrameGroupByPlots(TestPlotBase): def test_grouped_hist_legacy(self): from matplotlib.patches import Rectangle from pandas.plotting._matplotlib.hist import _grouped_hist df = DataFrame(np.random.randn(500, 1), columns=["A"]) df["B"] = to_datetime( np.random.randint( 812419200000000000, 819331200000000000, size=500, dtype=np.int64, ) ) df["C"] = np.random.randint(0, 4, 500) df["D"] = ["X"] * 500 axes = _grouped_hist(df.A, by=df.C) self._check_axes_shape(axes, axes_num=4, layout=(2, 2)) tm.close() axes = df.hist(by=df.C) self._check_axes_shape(axes, axes_num=4, layout=(2, 2)) tm.close() # group by a key with single value axes = df.hist(by="D", rot=30) self._check_axes_shape(axes, axes_num=1, layout=(1, 1)) self._check_ticks_props(axes, xrot=30) tm.close() # make sure kwargs to hist are handled xf, yf = 20, 18 xrot, yrot = 30, 40 axes = _grouped_hist( df.A, by=df.C, cumulative=True, bins=4, xlabelsize=xf, xrot=xrot, ylabelsize=yf, yrot=yrot, density=True, ) # height of last bin (index 5) must be 1.0 for ax in axes.ravel(): rects = [x for x in ax.get_children() if isinstance(x, Rectangle)] height = rects[-1].get_height() tm.assert_almost_equal(height, 1.0) self._check_ticks_props( axes, xlabelsize=xf, xrot=xrot, ylabelsize=yf, yrot=yrot ) tm.close() axes = _grouped_hist(df.A, by=df.C, log=True) # scale of y must be 'log' self._check_ax_scales(axes, yaxis="log") tm.close() # propagate attr exception from matplotlib.Axes.hist with tm.external_error_raised(AttributeError): _grouped_hist(df.A, by=df.C, foo="bar") msg = "Specify figure size by tuple instead" with pytest.raises(ValueError, match=msg): df.hist(by="C", figsize="default") def test_grouped_hist_legacy2(self): n = 10 weight = Series(np.random.normal(166, 20, size=n)) height = Series(np.random.normal(60, 10, size=n)) gender_int = np.random.RandomState(42).choice([0, 1], size=n) df_int = DataFrame({"height": height, "weight": weight, "gender": gender_int}) gb = df_int.groupby("gender") axes = gb.hist() assert len(axes) == 2 assert len(self.plt.get_fignums()) == 2 tm.close() @pytest.mark.slow def test_grouped_hist_layout(self, hist_df): df = hist_df msg = "Layout of 1x1 must be larger than required size 2" with pytest.raises(ValueError, match=msg): df.hist(column="weight", by=df.gender, layout=(1, 1)) msg = "Layout of 1x3 must be larger than required size 4" with pytest.raises(ValueError, match=msg): df.hist(column="height", by=df.category, layout=(1, 3)) msg = "At least one dimension of layout must be positive" with pytest.raises(ValueError, match=msg): df.hist(column="height", by=df.category, layout=(-1, -1)) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works( df.hist, column="height", by=df.gender, layout=(2, 1) ) self._check_axes_shape(axes, axes_num=2, layout=(2, 1)) with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works( df.hist, column="height", by=df.gender, layout=(2, -1) ) self._check_axes_shape(axes, axes_num=2, layout=(2, 1)) axes = df.hist(column="height", by=df.category, layout=(4, 1)) self._check_axes_shape(axes, axes_num=4, layout=(4, 1)) axes = df.hist(column="height", by=df.category, layout=(-1, 1)) self._check_axes_shape(axes, axes_num=4, layout=(4, 1)) axes = df.hist(column="height", by=df.category, layout=(4, 2), figsize=(12, 8)) self._check_axes_shape(axes, axes_num=4, layout=(4, 2), figsize=(12, 8)) tm.close() # GH 6769 with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works( df.hist, column="height", by="classroom", layout=(2, 2) ) self._check_axes_shape(axes, axes_num=3, layout=(2, 2)) # without column with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(df.hist, by="classroom") self._check_axes_shape(axes, axes_num=3, layout=(2, 2)) axes = df.hist(by="gender", layout=(3, 5)) self._check_axes_shape(axes, axes_num=2, layout=(3, 5)) axes = df.hist(column=["height", "weight", "category"]) self._check_axes_shape(axes, axes_num=3, layout=(2, 2)) def test_grouped_hist_multiple_axes(self, hist_df): # GH 6970, GH 7069 df = hist_df fig, axes = self.plt.subplots(2, 3) returned = df.hist(column=["height", "weight", "category"], ax=axes[0]) self._check_axes_shape(returned, axes_num=3, layout=(1, 3)) tm.assert_numpy_array_equal(returned, axes[0]) assert returned[0].figure is fig returned = df.hist(by="classroom", ax=axes[1]) self._check_axes_shape(returned, axes_num=3, layout=(1, 3)) tm.assert_numpy_array_equal(returned, axes[1]) assert returned[0].figure is fig fig, axes = self.plt.subplots(2, 3) # pass different number of axes from required msg = "The number of passed axes must be 1, the same as the output plot" with pytest.raises(ValueError, match=msg): axes = df.hist(column="height", ax=axes) def test_axis_share_x(self, hist_df): df = hist_df # GH4089 ax1, ax2 = df.hist(column="height", by=df.gender, sharex=True) # share x assert self.get_x_axis(ax1).joined(ax1, ax2) assert self.get_x_axis(ax2).joined(ax1, ax2) # don't share y assert not self.get_y_axis(ax1).joined(ax1, ax2) assert not self.get_y_axis(ax2).joined(ax1, ax2) def test_axis_share_y(self, hist_df): df = hist_df ax1, ax2 = df.hist(column="height", by=df.gender, sharey=True) # share y assert self.get_y_axis(ax1).joined(ax1, ax2) assert self.get_y_axis(ax2).joined(ax1, ax2) # don't share x assert not self.get_x_axis(ax1).joined(ax1, ax2) assert not self.get_x_axis(ax2).joined(ax1, ax2) def test_axis_share_xy(self, hist_df): df = hist_df ax1, ax2 = df.hist(column="height", by=df.gender, sharex=True, sharey=True) # share both x and y assert self.get_x_axis(ax1).joined(ax1, ax2) assert self.get_x_axis(ax2).joined(ax1, ax2) assert self.get_y_axis(ax1).joined(ax1, ax2) assert self.get_y_axis(ax2).joined(ax1, ax2) @pytest.mark.parametrize( "histtype, expected", [ ("bar", True), ("barstacked", True), ("step", False), ("stepfilled", True), ], ) def test_histtype_argument(self, histtype, expected): # GH23992 Verify functioning of histtype argument df = DataFrame(np.random.randint(1, 10, size=(100, 2)), columns=["a", "b"]) ax = df.hist(by="a", histtype=histtype) self._check_patches_all_filled(ax, filled=expected)