diff --git a/pandas/tests/plotting/test_boxplot_method.py b/pandas/tests/plotting/test_boxplot_method.py index 075243603e9cd..9a73a394d0466 100644 --- a/pandas/tests/plotting/test_boxplot_method.py +++ b/pandas/tests/plotting/test_boxplot_method.py @@ -28,6 +28,12 @@ mpl = pytest.importorskip("matplotlib") +def _check_ax_limits(col, ax): + y_min, y_max = ax.get_ylim() + assert y_min <= col.min() + assert y_max >= col.max() + + class TestDataFramePlots: def test_stacked_boxplot_set_axis(self): # GH2980 @@ -88,18 +94,30 @@ def test_boxplot_legacy2(self): with tm.assert_produces_warning(UserWarning, check_stacklevel=False): _check_plot_works(df.boxplot, by="X") + def test_boxplot_legacy2_with_ax(self): + df = DataFrame(np.random.rand(10, 2), columns=["Col1", "Col2"]) + df["X"] = Series(["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"]) + df["Y"] = Series(["A"] * 10) # When ax is supplied and required number of axes is 1, # passed ax should be used: - fig, ax = mpl.pyplot.subplots() + _, ax = mpl.pyplot.subplots() axes = df.boxplot("Col1", by="X", ax=ax) ax_axes = ax.axes assert ax_axes is axes + def test_boxplot_legacy2_with_ax_return_type(self): + df = DataFrame(np.random.rand(10, 2), columns=["Col1", "Col2"]) + df["X"] = Series(["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"]) + df["Y"] = Series(["A"] * 10) fig, ax = mpl.pyplot.subplots() axes = df.groupby("Y").boxplot(ax=ax, return_type="axes") ax_axes = ax.axes assert ax_axes is axes["A"] + def test_boxplot_legacy2_with_multi_col(self): + df = DataFrame(np.random.rand(10, 2), columns=["Col1", "Col2"]) + df["X"] = Series(["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"]) + df["Y"] = Series(["A"] * 10) # Multiple columns with an ax argument should use same figure fig, ax = mpl.pyplot.subplots() with tm.assert_produces_warning(UserWarning): @@ -108,9 +126,13 @@ def test_boxplot_legacy2(self): ) assert axes["Col1"].get_figure() is fig + def test_boxplot_legacy2_by_none(self): + df = DataFrame(np.random.rand(10, 2), columns=["Col1", "Col2"]) + df["X"] = Series(["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"]) + df["Y"] = Series(["A"] * 10) # When by is None, check that all relevant lines are present in the # dict - fig, ax = mpl.pyplot.subplots() + _, ax = mpl.pyplot.subplots() d = df.boxplot(ax=ax, return_type="dict") lines = list(itertools.chain.from_iterable(d.values())) assert len(ax.get_lines()) == len(lines) @@ -135,24 +157,20 @@ def test_boxplot_return_type_legacy(self): result = df.boxplot() _check_box_return_type(result, "axes") - with tm.assert_produces_warning(False): - result = df.boxplot(return_type="dict") - _check_box_return_type(result, "dict") - - with tm.assert_produces_warning(False): - result = df.boxplot(return_type="axes") - _check_box_return_type(result, "axes") + @pytest.mark.parametrize("return_type", ["dict", "axes", "both"]) + def test_boxplot_return_type_legacy_return_type(self, return_type): + # API change in https://github.com/pandas-dev/pandas/pull/7096 + df = DataFrame( + np.random.randn(6, 4), + index=list(string.ascii_letters[:6]), + columns=["one", "two", "three", "four"], + ) with tm.assert_produces_warning(False): - result = df.boxplot(return_type="both") - _check_box_return_type(result, "both") + result = df.boxplot(return_type=return_type) + _check_box_return_type(result, return_type) def test_boxplot_axis_limits(self, hist_df): - def _check_ax_limits(col, ax): - y_min, y_max = ax.get_ylim() - assert y_min <= col.min() - assert y_max >= col.max() - df = hist_df.copy() df["age"] = np.random.randint(1, 20, df.shape[0]) # One full row @@ -161,6 +179,9 @@ def _check_ax_limits(col, ax): _check_ax_limits(df["weight"], weight_ax) assert weight_ax._sharey == height_ax + def test_boxplot_axis_limits_two_rows(self, hist_df): + df = hist_df.copy() + df["age"] = np.random.randint(1, 20, df.shape[0]) # Two rows, one partial p = df.boxplot(["height", "weight", "age"], by="category") height_ax, weight_ax, age_ax = p[0, 0], p[0, 1], p[1, 0] @@ -275,7 +296,7 @@ def test_color_kwd_errors(self, dict_colors, msg): ) def test_specified_props_kwd(self, props, expected): # GH 30346 - df = DataFrame({k: np.random.random(100) for k in "ABC"}) + df = DataFrame({k: np.random.random(10) for k in "ABC"}) kwd = {props: {"color": "C1"}} result = df.boxplot(return_type="dict", **kwd) @@ -285,9 +306,9 @@ def test_specified_props_kwd(self, props, expected): def test_plot_xlabel_ylabel(self, vert): df = DataFrame( { - "a": np.random.randn(100), - "b": np.random.randn(100), - "group": np.random.choice(["group1", "group2"], 100), + "a": np.random.randn(10), + "b": np.random.randn(10), + "group": np.random.choice(["group1", "group2"], 10), } ) xlabel, ylabel = "x", "y" @@ -299,9 +320,9 @@ def test_plot_xlabel_ylabel(self, vert): def test_boxplot_xlabel_ylabel(self, vert): df = DataFrame( { - "a": np.random.randn(100), - "b": np.random.randn(100), - "group": np.random.choice(["group1", "group2"], 100), + "a": np.random.randn(10), + "b": np.random.randn(10), + "group": np.random.choice(["group1", "group2"], 10), } ) xlabel, ylabel = "x", "y" @@ -313,9 +334,9 @@ def test_boxplot_xlabel_ylabel(self, vert): def test_boxplot_group_xlabel_ylabel(self, vert): df = DataFrame( { - "a": np.random.randn(100), - "b": np.random.randn(100), - "group": np.random.choice(["group1", "group2"], 100), + "a": np.random.randn(10), + "b": np.random.randn(10), + "group": np.random.choice(["group1", "group2"], 10), } ) xlabel, ylabel = "x", "y" @@ -325,6 +346,15 @@ def test_boxplot_group_xlabel_ylabel(self, vert): assert subplot.get_ylabel() == ylabel mpl.pyplot.close() + @pytest.mark.parametrize("vert", [True, False]) + def test_boxplot_group_no_xlabel_ylabel(self, vert): + df = DataFrame( + { + "a": np.random.randn(10), + "b": np.random.randn(10), + "group": np.random.choice(["group1", "group2"], 10), + } + ) ax = df.boxplot(by="group", vert=vert) for subplot in ax: target_label = subplot.get_xlabel() if vert else subplot.get_ylabel() @@ -338,6 +368,9 @@ def test_boxplot_legacy1(self, hist_df): with tm.assert_produces_warning(UserWarning, check_stacklevel=False): axes = _check_plot_works(grouped.boxplot, return_type="axes") _check_axes_shape(list(axes.values), axes_num=2, layout=(1, 2)) + + def test_boxplot_legacy1_return_type(self, hist_df): + grouped = hist_df.groupby(by="gender") axes = _check_plot_works(grouped.boxplot, subplots=False, return_type="axes") _check_axes_shape(axes, axes_num=1, layout=(1, 1)) @@ -350,6 +383,11 @@ def test_boxplot_legacy2(self): axes = _check_plot_works(grouped.boxplot, return_type="axes") _check_axes_shape(list(axes.values), axes_num=10, layout=(4, 3)) + @pytest.mark.slow + def test_boxplot_legacy2_return_type(self): + tuples = zip(string.ascii_letters[:10], range(10)) + df = DataFrame(np.random.rand(10, 3), index=MultiIndex.from_tuples(tuples)) + grouped = df.groupby(level=1) axes = _check_plot_works(grouped.boxplot, subplots=False, return_type="axes") _check_axes_shape(axes, axes_num=1, layout=(1, 1)) @@ -387,8 +425,14 @@ def test_grouped_plot_fignums(self): assert len(res) == 2 tm.close() + def test_grouped_plot_fignums_excluded_col(self): + n = 10 + weight = Series(np.random.normal(166, 20, size=n)) + height = Series(np.random.normal(60, 10, size=n)) + gender = np.random.RandomState(42).choice(["male", "female"], size=n) + df = DataFrame({"height": height, "weight": weight, "gender": gender}) # now works with GH 5610 as gender is excluded - res = df.groupby("gender").hist() + df.groupby("gender").hist() tm.close() @pytest.mark.slow @@ -545,10 +589,14 @@ def test_grouped_box_multiple_axes(self, hist_df): # location should be changed if other test is added # which has earlier alphabetical order with tm.assert_produces_warning(UserWarning): - fig, axes = mpl.pyplot.subplots(2, 2) + _, axes = mpl.pyplot.subplots(2, 2) df.groupby("category").boxplot(column="height", return_type="axes", ax=axes) _check_axes_shape(mpl.pyplot.gcf().axes, axes_num=4, layout=(2, 2)) + @pytest.mark.slow + def test_grouped_box_multiple_axes_on_fig(self, hist_df): + # GH 6970, GH 7069 + df = hist_df fig, axes = mpl.pyplot.subplots(2, 3) with tm.assert_produces_warning(UserWarning): returned = df.boxplot( @@ -572,6 +620,10 @@ def test_grouped_box_multiple_axes(self, hist_df): tm.assert_numpy_array_equal(returned, axes[1]) assert returned[0].figure is fig + @pytest.mark.slow + def test_grouped_box_multiple_axes_ax_error(self, hist_df): + # GH 6970, GH 7069 + df = hist_df msg = "The number of passed axes must be 3, the same as the output plot" with pytest.raises(ValueError, match=msg): fig, axes = mpl.pyplot.subplots(2, 3) diff --git a/pandas/tests/plotting/test_converter.py b/pandas/tests/plotting/test_converter.py index 834e6fac11283..6caeb3a5d7445 100644 --- a/pandas/tests/plotting/test_converter.py +++ b/pandas/tests/plotting/test_converter.py @@ -222,17 +222,15 @@ def test_conversion_float(self, dtc): rs = dtc.convert(datetime(2012, 1, 1, 1, 2, 3), None, None) tm.assert_almost_equal(rs, xp, rtol=rtol) - def test_conversion_outofbounds_datetime(self, dtc): + @pytest.mark.parametrize( + "values", + [ + [date(1677, 1, 1), date(1677, 1, 2)], + [datetime(1677, 1, 1, 12), datetime(1677, 1, 2, 12)], + ], + ) + def test_conversion_outofbounds_datetime(self, dtc, values): # 2579 - values = [date(1677, 1, 1), date(1677, 1, 2)] - rs = dtc.convert(values, None, None) - xp = converter.mdates.date2num(values) - tm.assert_numpy_array_equal(rs, xp) - rs = dtc.convert(values[0], None, None) - xp = converter.mdates.date2num(values[0]) - assert rs == xp - - values = [datetime(1677, 1, 1, 12), datetime(1677, 1, 2, 12)] rs = dtc.convert(values, None, None) xp = converter.mdates.date2num(values) tm.assert_numpy_array_equal(rs, xp) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index dda71328d4e6c..9a2fa7fb61796 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -61,7 +61,7 @@ def test_ts_plot_with_tz(self, tz_aware_fixture): def test_fontsize_set_correctly(self): # For issue #8765 df = DataFrame(np.random.randn(10, 9), index=range(10)) - fig, ax = mpl.pyplot.subplots() + _, ax = mpl.pyplot.subplots() df.plot(fontsize=2, ax=ax) for label in ax.get_xticklabels() + ax.get_yticklabels(): assert label.get_fontsize() == 2 @@ -79,6 +79,7 @@ def test_frame_inferred(self): df2 = DataFrame(np.random.randn(len(idx), 3), index=idx) _check_plot_works(df2.plot) + def test_frame_inferred_n_gt_1(self): # N > 1 idx = date_range("2008-1-1 00:15:00", freq="15T", periods=10) idx = DatetimeIndex(idx.values, freq=None) @@ -101,6 +102,9 @@ def test_nonnumeric_exclude(self): assert len(ax.get_lines()) == 1 # B was plotted mpl.pyplot.close(fig) + def test_nonnumeric_exclude_error(self): + idx = date_range("1/1/1987", freq="A", periods=3) + df = DataFrame({"A": ["x", "y", "z"], "B": [1, 2, 3]}, idx) msg = "no numeric data to plot" with pytest.raises(TypeError, match=msg): df["A"].plot() @@ -251,7 +255,8 @@ def test_plot_offset_freq(self): ser = tm.makeTimeSeries() _check_plot_works(ser.plot) - dr = date_range(ser.index[0], freq="BQS", periods=10) + def test_plot_offset_freq_business(self): + dr = date_range("2023-01-01", freq="BQS", periods=10) ser = Series(np.random.randn(len(dr)), index=dr) _check_plot_works(ser.plot) @@ -280,7 +285,7 @@ def test_uhf(self): assert xp == rs def test_irreg_hf(self): - idx = date_range("2012-6-22 21:59:51", freq="S", periods=100) + idx = date_range("2012-6-22 21:59:51", freq="S", periods=10) df = DataFrame(np.random.randn(len(idx), 2), index=idx) irreg = df.iloc[[0, 1, 3, 4]] @@ -291,11 +296,14 @@ def test_irreg_hf(self): sec = 1.0 / 24 / 60 / 60 assert (np.fabs(diffs[1:] - [sec, sec * 2, sec]) < 1e-8).all() + def test_irreg_hf_object(self): + idx = date_range("2012-6-22 21:59:51", freq="S", periods=10) + df2 = DataFrame(np.random.randn(len(idx), 2), index=idx) _, ax = mpl.pyplot.subplots() - df2 = df.copy() - df2.index = df.index.astype(object) + df2.index = df2.index.astype(object) df2.plot(ax=ax) diffs = Series(ax.get_lines()[0].get_xydata()[:, 0]).diff() + sec = 1.0 / 24 / 60 / 60 assert (np.fabs(diffs[1:] - sec) < 1e-8).all() def test_irregular_datetime64_repr_bug(self): @@ -355,45 +363,37 @@ def test_dataframe(self): idx = ax.get_lines()[0].get_xdata() tm.assert_index_equal(bts.index.to_period(), PeriodIndex(idx)) - def test_axis_limits(self): - def _test(ax): - xlim = ax.get_xlim() - ax.set_xlim(xlim[0] - 5, xlim[1] + 10) - result = ax.get_xlim() - assert result[0] == xlim[0] - 5 - assert result[1] == xlim[1] + 10 - - # string - expected = (Period("1/1/2000", ax.freq), Period("4/1/2000", ax.freq)) - ax.set_xlim("1/1/2000", "4/1/2000") - result = ax.get_xlim() - assert int(result[0]) == expected[0].ordinal - assert int(result[1]) == expected[1].ordinal - - # datetime - expected = (Period("1/1/2000", ax.freq), Period("4/1/2000", ax.freq)) - ax.set_xlim(datetime(2000, 1, 1), datetime(2000, 4, 1)) - result = ax.get_xlim() - assert int(result[0]) == expected[0].ordinal - assert int(result[1]) == expected[1].ordinal - fig = ax.get_figure() - mpl.pyplot.close(fig) - - ser = tm.makeTimeSeries() - _, ax = mpl.pyplot.subplots() - ser.plot(ax=ax) - _test(ax) - + @pytest.mark.parametrize( + "obj", + [ + tm.makeTimeSeries(), + DataFrame({"a": tm.makeTimeSeries(), "b": tm.makeTimeSeries() + 1}), + ], + ) + def test_axis_limits(self, obj): _, ax = mpl.pyplot.subplots() - df = DataFrame({"a": ser, "b": ser + 1}) - df.plot(ax=ax) - _test(ax) - - df = DataFrame({"a": ser, "b": ser + 1}) - axes = df.plot(subplots=True) - - for ax in axes: - _test(ax) + obj.plot(ax=ax) + xlim = ax.get_xlim() + ax.set_xlim(xlim[0] - 5, xlim[1] + 10) + result = ax.get_xlim() + assert result[0] == xlim[0] - 5 + assert result[1] == xlim[1] + 10 + + # string + expected = (Period("1/1/2000", ax.freq), Period("4/1/2000", ax.freq)) + ax.set_xlim("1/1/2000", "4/1/2000") + result = ax.get_xlim() + assert int(result[0]) == expected[0].ordinal + assert int(result[1]) == expected[1].ordinal + + # datetime + expected = (Period("1/1/2000", ax.freq), Period("4/1/2000", ax.freq)) + ax.set_xlim(datetime(2000, 1, 1), datetime(2000, 4, 1)) + result = ax.get_xlim() + assert int(result[0]) == expected[0].ordinal + assert int(result[1]) == expected[1].ordinal + fig = ax.get_figure() + mpl.pyplot.close(fig) def test_get_finder(self): import pandas.plotting._matplotlib.converter as conv @@ -538,6 +538,7 @@ def test_gaps(self): assert mask[5:25, 1].all() mpl.pyplot.close(ax.get_figure()) + def test_gaps_irregular(self): # irregular ts = tm.makeTimeSeries() ts = ts.iloc[[0, 1, 2, 5, 7, 9, 12, 15, 20]] @@ -556,6 +557,7 @@ def test_gaps(self): assert mask[2:5, 1].all() mpl.pyplot.close(ax.get_figure()) + def test_gaps_non_ts(self): # non-ts idx = [0, 1, 2, 5, 7, 9, 12, 15, 20] ser = Series(np.random.randn(len(idx)), idx) @@ -595,7 +597,6 @@ def test_gap_upsample(self): def test_secondary_y(self): ser = Series(np.random.randn(10)) - ser2 = Series(np.random.randn(10)) fig, _ = mpl.pyplot.subplots() ax = ser.plot(secondary_y=True) assert hasattr(ax, "left_ax") @@ -608,11 +609,17 @@ def test_secondary_y(self): assert not axes[0].get_yaxis().get_visible() mpl.pyplot.close(fig) + def test_secondary_y_yaxis(self): + Series(np.random.randn(10)) + ser2 = Series(np.random.randn(10)) _, ax2 = mpl.pyplot.subplots() ser2.plot(ax=ax2) assert ax2.get_yaxis().get_ticks_position() == "left" mpl.pyplot.close(ax2.get_figure()) + def test_secondary_both(self): + ser = Series(np.random.randn(10)) + ser2 = Series(np.random.randn(10)) ax = ser2.plot() ax2 = ser.plot(secondary_y=True) assert ax.get_yaxis().get_visible() @@ -624,7 +631,6 @@ def test_secondary_y(self): def test_secondary_y_ts(self): idx = date_range("1/1/2000", periods=10) ser = Series(np.random.randn(10), idx) - ser2 = Series(np.random.randn(10), idx) fig, _ = mpl.pyplot.subplots() ax = ser.plot(secondary_y=True) assert hasattr(ax, "left_ax") @@ -637,13 +643,18 @@ def test_secondary_y_ts(self): assert not axes[0].get_yaxis().get_visible() mpl.pyplot.close(fig) + def test_secondary_y_ts_yaxis(self): + idx = date_range("1/1/2000", periods=10) + ser2 = Series(np.random.randn(10), idx) _, ax2 = mpl.pyplot.subplots() ser2.plot(ax=ax2) assert ax2.get_yaxis().get_ticks_position() == "left" mpl.pyplot.close(ax2.get_figure()) + def test_secondary_y_ts_visible(self): + idx = date_range("1/1/2000", periods=10) + ser2 = Series(np.random.randn(10), idx) ax = ser2.plot() - ax2 = ser.plot(secondary_y=True) assert ax.get_yaxis().get_visible() @td.skip_if_no_scipy @@ -781,6 +792,7 @@ def test_mixed_freq_lf_first(self): assert len(leg.texts) == 2 mpl.pyplot.close(ax.get_figure()) + def test_mixed_freq_lf_first_hourly(self): idxh = date_range("1/1/1999", periods=240, freq="T") idxl = date_range("1/1/1999", periods=4, freq="H") high = Series(np.random.randn(len(idxh)), idxh) @@ -807,7 +819,7 @@ def test_mixed_freq_shared_ax(self): s1 = Series(range(len(idx1)), idx1) s2 = Series(range(len(idx2)), idx2) - fig, (ax1, ax2) = mpl.pyplot.subplots(nrows=2, sharex=True) + _, (ax1, ax2) = mpl.pyplot.subplots(nrows=2, sharex=True) s1.plot(ax=ax1) s2.plot(ax=ax2) @@ -815,6 +827,12 @@ def test_mixed_freq_shared_ax(self): assert ax2.freq == "M" assert ax1.lines[0].get_xydata()[0, 0] == ax2.lines[0].get_xydata()[0, 0] + def test_mixed_freq_shared_ax_twin_x(self): + # GH13341, using sharex=True + idx1 = date_range("2015-01-01", periods=3, freq="M") + idx2 = idx1[:1].union(idx1[2:]) + s1 = Series(range(len(idx1)), idx1) + s2 = Series(range(len(idx2)), idx2) # using twinx fig, ax1 = mpl.pyplot.subplots() ax2 = ax1.twinx() @@ -877,96 +895,99 @@ def test_from_weekly_resampling(self): tm.assert_numpy_array_equal(xdata, expected_h) tm.close() - def test_from_resampling_area_line_mixed(self): + @pytest.mark.parametrize("kind1, kind2", [("line", "area"), ("area", "line")]) + def test_from_resampling_area_line_mixed(self, kind1, kind2): idxh = date_range("1/1/1999", periods=52, freq="W") idxl = date_range("1/1/1999", periods=12, freq="M") high = DataFrame(np.random.rand(len(idxh), 3), index=idxh, columns=[0, 1, 2]) low = DataFrame(np.random.rand(len(idxl), 3), index=idxl, columns=[0, 1, 2]) - # low to high - for kind1, kind2 in [("line", "area"), ("area", "line")]: - _, ax = mpl.pyplot.subplots() - low.plot(kind=kind1, stacked=True, ax=ax) - high.plot(kind=kind2, stacked=True, ax=ax) - - # check low dataframe result - expected_x = np.array( - [ - 1514, - 1519, - 1523, - 1527, - 1531, - 1536, - 1540, - 1544, - 1549, - 1553, - 1558, - 1562, - ], - dtype=np.float64, - ) - expected_y = np.zeros(len(expected_x), dtype=np.float64) - for i in range(3): - line = ax.lines[i] - assert PeriodIndex(line.get_xdata()).freq == idxh.freq - tm.assert_numpy_array_equal(line.get_xdata(orig=False), expected_x) - # check stacked values are correct - expected_y += low[i].values - tm.assert_numpy_array_equal(line.get_ydata(orig=False), expected_y) - - # check high dataframe result - expected_x = idxh.to_period().asi8.astype(np.float64) - expected_y = np.zeros(len(expected_x), dtype=np.float64) - for i in range(3): - line = ax.lines[3 + i] - assert PeriodIndex(data=line.get_xdata()).freq == idxh.freq - tm.assert_numpy_array_equal(line.get_xdata(orig=False), expected_x) - expected_y += high[i].values - tm.assert_numpy_array_equal(line.get_ydata(orig=False), expected_y) + _, ax = mpl.pyplot.subplots() + low.plot(kind=kind1, stacked=True, ax=ax) + high.plot(kind=kind2, stacked=True, ax=ax) + + # check low dataframe result + expected_x = np.array( + [ + 1514, + 1519, + 1523, + 1527, + 1531, + 1536, + 1540, + 1544, + 1549, + 1553, + 1558, + 1562, + ], + dtype=np.float64, + ) + expected_y = np.zeros(len(expected_x), dtype=np.float64) + for i in range(3): + line = ax.lines[i] + assert PeriodIndex(line.get_xdata()).freq == idxh.freq + tm.assert_numpy_array_equal(line.get_xdata(orig=False), expected_x) + # check stacked values are correct + expected_y += low[i].values + tm.assert_numpy_array_equal(line.get_ydata(orig=False), expected_y) + + # check high dataframe result + expected_x = idxh.to_period().asi8.astype(np.float64) + expected_y = np.zeros(len(expected_x), dtype=np.float64) + for i in range(3): + line = ax.lines[3 + i] + assert PeriodIndex(data=line.get_xdata()).freq == idxh.freq + tm.assert_numpy_array_equal(line.get_xdata(orig=False), expected_x) + expected_y += high[i].values + tm.assert_numpy_array_equal(line.get_ydata(orig=False), expected_y) - # high to low - for kind1, kind2 in [("line", "area"), ("area", "line")]: - _, ax = mpl.pyplot.subplots() - high.plot(kind=kind1, stacked=True, ax=ax) - low.plot(kind=kind2, stacked=True, ax=ax) - - # check high dataframe result - expected_x = idxh.to_period().asi8.astype(np.float64) - expected_y = np.zeros(len(expected_x), dtype=np.float64) - for i in range(3): - line = ax.lines[i] - assert PeriodIndex(data=line.get_xdata()).freq == idxh.freq - tm.assert_numpy_array_equal(line.get_xdata(orig=False), expected_x) - expected_y += high[i].values - tm.assert_numpy_array_equal(line.get_ydata(orig=False), expected_y) - - # check low dataframe result - expected_x = np.array( - [ - 1514, - 1519, - 1523, - 1527, - 1531, - 1536, - 1540, - 1544, - 1549, - 1553, - 1558, - 1562, - ], - dtype=np.float64, - ) - expected_y = np.zeros(len(expected_x), dtype=np.float64) - for i in range(3): - lines = ax.lines[3 + i] - assert PeriodIndex(data=lines.get_xdata()).freq == idxh.freq - tm.assert_numpy_array_equal(lines.get_xdata(orig=False), expected_x) - expected_y += low[i].values - tm.assert_numpy_array_equal(lines.get_ydata(orig=False), expected_y) + @pytest.mark.parametrize("kind1, kind2", [("line", "area"), ("area", "line")]) + def test_from_resampling_area_line_mixed_high_to_low(self, kind1, kind2): + idxh = date_range("1/1/1999", periods=52, freq="W") + idxl = date_range("1/1/1999", periods=12, freq="M") + high = DataFrame(np.random.rand(len(idxh), 3), index=idxh, columns=[0, 1, 2]) + low = DataFrame(np.random.rand(len(idxl), 3), index=idxl, columns=[0, 1, 2]) + _, ax = mpl.pyplot.subplots() + high.plot(kind=kind1, stacked=True, ax=ax) + low.plot(kind=kind2, stacked=True, ax=ax) + + # check high dataframe result + expected_x = idxh.to_period().asi8.astype(np.float64) + expected_y = np.zeros(len(expected_x), dtype=np.float64) + for i in range(3): + line = ax.lines[i] + assert PeriodIndex(data=line.get_xdata()).freq == idxh.freq + tm.assert_numpy_array_equal(line.get_xdata(orig=False), expected_x) + expected_y += high[i].values + tm.assert_numpy_array_equal(line.get_ydata(orig=False), expected_y) + + # check low dataframe result + expected_x = np.array( + [ + 1514, + 1519, + 1523, + 1527, + 1531, + 1536, + 1540, + 1544, + 1549, + 1553, + 1558, + 1562, + ], + dtype=np.float64, + ) + expected_y = np.zeros(len(expected_x), dtype=np.float64) + for i in range(3): + lines = ax.lines[3 + i] + assert PeriodIndex(data=lines.get_xdata()).freq == idxh.freq + tm.assert_numpy_array_equal(lines.get_xdata(orig=False), expected_x) + expected_y += low[i].values + tm.assert_numpy_array_equal(lines.get_ydata(orig=False), expected_y) def test_mixed_freq_second_millisecond(self): # GH 7772, GH 7760 @@ -983,6 +1004,12 @@ def test_mixed_freq_second_millisecond(self): assert PeriodIndex(data=line.get_xdata()).freq == "L" tm.close() + def test_mixed_freq_second_millisecond_low_to_high(self): + # GH 7772, GH 7760 + idxh = date_range("2014-07-01 09:00", freq="S", periods=50) + idxl = date_range("2014-07-01 09:00", freq="100L", periods=500) + high = Series(np.random.randn(len(idxh)), idxh) + low = Series(np.random.randn(len(idxl)), idxl) # low to high _, ax = mpl.pyplot.subplots() low.plot(ax=ax) @@ -997,6 +1024,7 @@ def test_irreg_dtypes(self): df = DataFrame(np.random.randn(len(idx), 3), Index(idx, dtype=object)) _check_plot_works(df.plot) + def test_irreg_dtypes_dt64(self): # np.datetime64 idx = date_range("1/1/2000", periods=10) idx = idx[[0, 2, 5, 9]].astype(object) @@ -1011,7 +1039,7 @@ def test_time(self): df = DataFrame( {"a": np.random.randn(len(ts)), "b": np.random.randn(len(ts))}, index=ts ) - fig, ax = mpl.pyplot.subplots() + _, ax = mpl.pyplot.subplots() df.plot(ax=ax) # verify tick labels @@ -1035,7 +1063,7 @@ def test_time_change_xlim(self): df = DataFrame( {"a": np.random.randn(len(ts)), "b": np.random.randn(len(ts))}, index=ts ) - fig, ax = mpl.pyplot.subplots() + _, ax = mpl.pyplot.subplots() df.plot(ax=ax) # verify tick labels @@ -1076,7 +1104,7 @@ def test_time_musec(self): df = DataFrame( {"a": np.random.randn(len(ts)), "b": np.random.randn(len(ts))}, index=ts ) - fig, ax = mpl.pyplot.subplots() + _, ax = mpl.pyplot.subplots() ax = df.plot(ax=ax) # verify tick labels @@ -1137,6 +1165,8 @@ def test_secondary_legend(self): assert len(colors) == 4 mpl.pyplot.close(fig) + def test_secondary_legend_right(self): + df = tm.makeTimeDataFrame() fig = mpl.pyplot.figure() ax = fig.add_subplot(211) df.plot(secondary_y=["A", "C"], mark_right=False, ax=ax) @@ -1148,6 +1178,8 @@ def test_secondary_legend(self): assert leg.get_texts()[3].get_text() == "D" mpl.pyplot.close(fig) + def test_secondary_legend_bar(self): + df = tm.makeTimeDataFrame() fig, ax = mpl.pyplot.subplots() df.plot(kind="bar", secondary_y=["A"], ax=ax) leg = ax.get_legend() @@ -1155,6 +1187,8 @@ def test_secondary_legend(self): assert leg.get_texts()[1].get_text() == "B" mpl.pyplot.close(fig) + def test_secondary_legend_bar_right(self): + df = tm.makeTimeDataFrame() fig, ax = mpl.pyplot.subplots() df.plot(kind="bar", secondary_y=["A"], mark_right=False, ax=ax) leg = ax.get_legend() @@ -1162,6 +1196,8 @@ def test_secondary_legend(self): assert leg.get_texts()[1].get_text() == "B" mpl.pyplot.close(fig) + def test_secondary_legend_multi_col(self): + df = tm.makeTimeDataFrame() fig = mpl.pyplot.figure() ax = fig.add_subplot(211) df = tm.makeTimeDataFrame() @@ -1177,6 +1213,7 @@ def test_secondary_legend(self): assert len(colors) == 4 mpl.pyplot.close(fig) + def test_secondary_legend_nonts(self): # non-ts df = tm.makeDataFrame() fig = mpl.pyplot.figure() @@ -1193,6 +1230,9 @@ def test_secondary_legend(self): assert len(colors) == 4 mpl.pyplot.close() + def test_secondary_legend_nonts_multi_col(self): + # non-ts + df = tm.makeDataFrame() fig = mpl.pyplot.figure() ax = fig.add_subplot(211) ax = df.plot(secondary_y=["C", "D"], ax=ax) @@ -1338,7 +1378,7 @@ def test_format_timedelta_ticks_narrow(self): rng = timedelta_range("0", periods=10, freq="ns") df = DataFrame(np.random.randn(len(rng), 3), rng) - fig, ax = mpl.pyplot.subplots() + _, ax = mpl.pyplot.subplots() df.plot(fontsize=2, ax=ax) mpl.pyplot.draw() labels = ax.get_xticklabels() @@ -1362,7 +1402,7 @@ def test_format_timedelta_ticks_wide(self): rng = timedelta_range("0", periods=10, freq="1 d") df = DataFrame(np.random.randn(len(rng), 3), rng) - fig, ax = mpl.pyplot.subplots() + _, ax = mpl.pyplot.subplots() ax = df.plot(fontsize=2, ax=ax) mpl.pyplot.draw() labels = ax.get_xticklabels() @@ -1377,12 +1417,14 @@ def test_timedelta_plot(self): _, ax = mpl.pyplot.subplots() _check_plot_works(s.plot, ax=ax) + def test_timedelta_long_period(self): # test long period index = timedelta_range("1 day 2 hr 30 min 10 s", periods=10, freq="1 d") s = Series(np.random.randn(len(index)), index) _, ax = mpl.pyplot.subplots() _check_plot_works(s.plot, ax=ax) + def test_timedelta_short_period(self): # test short period index = timedelta_range("1 day 2 hr 30 min 10 s", periods=10, freq="1 ns") s = Series(np.random.randn(len(index)), index) @@ -1441,7 +1483,7 @@ def test_matplotlib_scatter_datetime64(self): # https://github.com/matplotlib/matplotlib/issues/11391 df = DataFrame(np.random.RandomState(0).rand(10, 2), columns=["x", "y"]) df["time"] = date_range("2018-01-01", periods=10, freq="D") - fig, ax = mpl.pyplot.subplots() + _, ax = mpl.pyplot.subplots() ax.scatter(x="time", y="y", data=df) mpl.pyplot.draw() label = ax.get_xticklabels()[0] @@ -1456,18 +1498,27 @@ def test_check_xticks_rot(self): axes = df.plot(x="x", y="y") _check_ticks_props(axes, xrot=0) + def test_check_xticks_rot_irregular(self): # irregular time series x = to_datetime(["2020-05-01", "2020-05-02", "2020-05-04"]) df = DataFrame({"x": x, "y": [1, 2, 3]}) axes = df.plot(x="x", y="y") _check_ticks_props(axes, xrot=30) + def test_check_xticks_rot_use_idx(self): + # irregular time series + x = to_datetime(["2020-05-01", "2020-05-02", "2020-05-04"]) + df = DataFrame({"x": x, "y": [1, 2, 3]}) # use timeseries index or not axes = df.set_index("x").plot(y="y", use_index=True) _check_ticks_props(axes, xrot=30) axes = df.set_index("x").plot(y="y", use_index=False) _check_ticks_props(axes, xrot=0) + def test_check_xticks_rot_sharex(self): + # irregular time series + x = to_datetime(["2020-05-01", "2020-05-02", "2020-05-04"]) + df = DataFrame({"x": x, "y": [1, 2, 3]}) # separate subplots axes = df.plot(x="x", y="y", subplots=True, sharex=True) _check_ticks_props(axes, xrot=30) diff --git a/pandas/tests/plotting/test_groupby.py b/pandas/tests/plotting/test_groupby.py index 43e35e94164c3..64c413a054ad0 100644 --- a/pandas/tests/plotting/test_groupby.py +++ b/pandas/tests/plotting/test_groupby.py @@ -22,13 +22,22 @@ class TestDataFrameGroupByPlots: def test_series_groupby_plotting_nominally_works(self): n = 10 weight = Series(np.random.normal(166, 20, size=n)) - height = Series(np.random.normal(60, 10, size=n)) gender = np.random.RandomState(42).choice(["male", "female"], size=n) weight.groupby(gender).plot() tm.close() + + def test_series_groupby_plotting_nominally_works_hist(self): + n = 10 + height = Series(np.random.normal(60, 10, size=n)) + gender = np.random.RandomState(42).choice(["male", "female"], size=n) height.groupby(gender).hist() tm.close() + + def test_series_groupby_plotting_nominally_works_alpha(self): + n = 10 + height = Series(np.random.normal(60, 10, size=n)) + gender = np.random.RandomState(42).choice(["male", "female"], size=n) # Regression test for GH8733 height.groupby(gender).plot(alpha=0.5) tm.close() @@ -42,6 +51,13 @@ def test_plotting_with_float_index_works(self): df.groupby("def")["val"].plot() tm.close() + + def test_plotting_with_float_index_works_apply(self): + # GH 7025 + df = DataFrame( + {"def": [1, 1, 1, 2, 2, 2, 3, 3, 3], "val": np.random.randn(9)}, + index=[1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0], + ) df.groupby("def")["val"].apply(lambda x: x.plot()) tm.close() @@ -50,6 +66,10 @@ def test_hist_single_row(self): bins = np.arange(80, 100 + 2, 1) df = DataFrame({"Name": ["AAA", "BBB"], "ByCol": [1, 2], "Mark": [85, 89]}) df["Mark"].hist(by=df["ByCol"], bins=bins) + + def test_hist_single_row_single_bycol(self): + # GH10214 + bins = np.arange(80, 100 + 2, 1) df = DataFrame({"Name": ["AAA"], "ByCol": [1], "Mark": [85]}) df["Mark"].hist(by=df["ByCol"], bins=bins) @@ -57,6 +77,9 @@ def test_plot_submethod_works(self): df = DataFrame({"x": [1, 2, 3, 4, 5], "y": [1, 2, 3, 2, 1], "z": list("ababa")}) df.groupby("z").plot.scatter("x", "y") tm.close() + + def test_plot_submethod_works_line(self): + df = DataFrame({"x": [1, 2, 3, 4, 5], "y": [1, 2, 3, 2, 1], "z": list("ababa")}) df.groupby("z")["x"].plot.line() tm.close() @@ -68,6 +91,8 @@ def test_plot_kwargs(self): # contain a PathCollection from the scatter plot (GH11805) assert len(res["a"].collections) == 1 + def test_plot_kwargs_scatter(self): + df = DataFrame({"x": [1, 2, 3, 4, 5], "y": [1, 2, 3, 2, 1], "z": list("ababa")}) res = df.groupby("z").plot.scatter(x="x", y="y") assert len(res["a"].collections) == 1 diff --git a/pandas/tests/plotting/test_hist_method.py b/pandas/tests/plotting/test_hist_method.py index edcb1ab07d7eb..a153e5dc8f932 100644 --- a/pandas/tests/plotting/test_hist_method.py +++ b/pandas/tests/plotting/test_hist_method.py @@ -35,26 +35,35 @@ def ts(): class TestSeriesPlots: - 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)) + @pytest.mark.parametrize("kwargs", [{}, {"grid": False}, {"figsize": (8, 10)}]) + def test_hist_legacy_kwargs(self, ts, kwargs): + _check_plot_works(ts.hist, **kwargs) + + @pytest.mark.parametrize("kwargs", [{}, {"bins": 5}]) + def test_hist_legacy_kwargs_warning(self, ts, kwargs): # _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) + _check_plot_works(ts.hist, by=ts.index.month, **kwargs) + def test_hist_legacy_ax(self, ts): fig, ax = mpl.pyplot.subplots(1, 1) _check_plot_works(ts.hist, ax=ax, default_axes=True) + + def test_hist_legacy_ax_and_fig(self, ts): + fig, ax = mpl.pyplot.subplots(1, 1) _check_plot_works(ts.hist, ax=ax, figure=fig, default_axes=True) + + def test_hist_legacy_fig(self, ts): + fig, _ = mpl.pyplot.subplots(1, 1) _check_plot_works(ts.hist, figure=fig, default_axes=True) - tm.close() + def test_hist_legacy_multi_ax(self, ts): fig, (ax1, ax2) = mpl.pyplot.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) + def test_hist_legacy_by_fig_error(self, ts): + fig, _ = mpl.pyplot.subplots(1, 1) msg = ( "Cannot pass 'figure' when using the 'by' argument, since a new 'Figure' " "instance will be created" @@ -180,16 +189,17 @@ def test_hist_kwargs(self, ts): ax = ts.plot.hist(bins=5, ax=ax) assert len(ax.patches) == 5 _check_text_labels(ax.yaxis.get_label(), "Frequency") - tm.close() + def test_hist_kwargs_horizontal(self, ts): _, ax = mpl.pyplot.subplots() + ax = ts.plot.hist(bins=5, ax=ax) ax = ts.plot.hist(orientation="horizontal", ax=ax) _check_text_labels(ax.xaxis.get_label(), "Frequency") - tm.close() + def test_hist_kwargs_align(self, ts): _, ax = mpl.pyplot.subplots() + ax = ts.plot.hist(bins=5, ax=ax) 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 @@ -203,8 +213,17 @@ def test_hist_kde(self, ts): ylabels = ax.get_yticklabels() _check_text_labels(ylabels, [""] * len(ylabels)) + @td.skip_if_no_scipy + def test_hist_kde_plot_works(self, ts): _check_plot_works(ts.plot.kde) + + @td.skip_if_no_scipy + def test_hist_kde_density_works(self, ts): _check_plot_works(ts.plot.density) + + @pytest.mark.xfail(reason="Api changed in 3.6.0") + @td.skip_if_no_scipy + def test_hist_kde_logy(self, ts): _, ax = mpl.pyplot.subplots() ax = ts.plot.kde(logy=True, ax=ax) _check_ax_scales(ax, yaxis="log") @@ -214,13 +233,15 @@ def test_hist_kde(self, ts): _check_text_labels(ylabels, [""] * len(ylabels)) @td.skip_if_no_scipy - def test_hist_kde_color(self, ts): + def test_hist_kde_color_bins(self, ts): _, ax = mpl.pyplot.subplots() ax = ts.plot.hist(logy=True, bins=10, color="b", ax=ax) _check_ax_scales(ax, yaxis="log") assert len(ax.patches) == 10 _check_colors(ax.patches, facecolors=["b"] * 10) + @td.skip_if_no_scipy + def test_hist_kde_color(self, ts): _, ax = mpl.pyplot.subplots() ax = ts.plot.kde(logy=True, color="r", ax=ax) _check_ax_scales(ax, yaxis="log") @@ -353,18 +374,9 @@ def test_hist_non_numerical_or_datetime_raises(self): 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 = ( + @pytest.mark.parametrize( + "layout_test", + ( {"layout": None, "expected_size": (2, 2)}, # default is 2x2 {"layout": (2, 2), "expected_size": (2, 2)}, {"layout": (4, 1), "expected_size": (4, 1)}, @@ -374,13 +386,32 @@ def test_hist_layout(self): {"layout": (4, -1), "expected_size": (4, 1)}, {"layout": (-1, 2), "expected_size": (2, 2)}, {"layout": (2, -1), "expected_size": (2, 2)}, + ), + ) + def test_hist_layout(self, layout_test): + df = DataFrame(np.random.randn(10, 2)) + df[2] = to_datetime( + np.random.randint( + 812419200000000000, + 819331200000000000, + size=10, + dtype=np.int64, + ) ) + axes = df.hist(layout=layout_test["layout"]) + expected = layout_test["expected_size"] + _check_axes_shape(axes, axes_num=3, layout=expected) - for layout_test in layout_to_expected_size: - axes = df.hist(layout=layout_test["layout"]) - expected = layout_test["expected_size"] - _check_axes_shape(axes, axes_num=3, layout=expected) - + def test_hist_layout_error(self): + df = DataFrame(np.random.randn(10, 2)) + df[2] = to_datetime( + np.random.randint( + 812419200000000000, + 819331200000000000, + size=10, + dtype=np.int64, + ) + ) # layout too small for all 4 plots msg = "Layout of 1x1 must be larger than required size 3" with pytest.raises(ValueError, match=msg): @@ -528,6 +559,12 @@ def test_hist_df_with_nonnumerics(self): ax = df.plot.hist(bins=5, ax=ax) assert len(ax.patches) == 20 + def test_hist_df_with_nonnumerics_no_bins(self): + # GH 9853 + df = DataFrame( + np.random.RandomState(42).randn(10, 4), columns=["A", "B", "C", "D"] + ) + df["E"] = ["x", "y"] * 5 _, ax = mpl.pyplot.subplots() ax = df.plot.hist(ax=ax) # bins=10 assert len(ax.patches) == 40 @@ -547,6 +584,9 @@ def test_hist_secondary_legend(self): assert ax.right_ax.get_yaxis().get_visible() tm.close() + def test_hist_secondary_secondary(self): + # GH 9610 + df = DataFrame(np.random.randn(30, 4), columns=list("abcd")) # secondary -> secondary _, ax = mpl.pyplot.subplots() ax = df["a"].plot.hist(legend=True, secondary_y=True, ax=ax) @@ -558,6 +598,9 @@ def test_hist_secondary_legend(self): assert ax.get_yaxis().get_visible() tm.close() + def test_hist_secondary_primary(self): + # GH 9610 + df = DataFrame(np.random.randn(30, 4), columns=list("abcd")) # secondary -> primary _, ax = mpl.pyplot.subplots() ax = df["a"].plot.hist(legend=True, secondary_y=True, ax=ax) @@ -603,36 +646,74 @@ def test_hist_with_nans_and_weights(self): class TestDataFrameGroupByPlots: 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"]) + rs = np.random.RandomState(42) + df = DataFrame(rs.randn(10, 1), columns=["A"]) df["B"] = to_datetime( - np.random.randint( + rs.randint( 812419200000000000, 819331200000000000, - size=500, + size=10, dtype=np.int64, ) ) - df["C"] = np.random.randint(0, 4, 500) - df["D"] = ["X"] * 500 + df["C"] = rs.randint(0, 4, 10) + df["D"] = ["X"] * 10 axes = _grouped_hist(df.A, by=df.C) _check_axes_shape(axes, axes_num=4, layout=(2, 2)) - tm.close() + def test_grouped_hist_legacy_axes_shape_no_col(self): + rs = np.random.RandomState(42) + df = DataFrame(rs.randn(10, 1), columns=["A"]) + df["B"] = to_datetime( + rs.randint( + 812419200000000000, + 819331200000000000, + size=10, + dtype=np.int64, + ) + ) + df["C"] = rs.randint(0, 4, 10) + df["D"] = ["X"] * 10 axes = df.hist(by=df.C) _check_axes_shape(axes, axes_num=4, layout=(2, 2)) - tm.close() + def test_grouped_hist_legacy_single_key(self): + rs = np.random.RandomState(42) + df = DataFrame(rs.randn(10, 1), columns=["A"]) + df["B"] = to_datetime( + rs.randint( + 812419200000000000, + 819331200000000000, + size=10, + dtype=np.int64, + ) + ) + df["C"] = rs.randint(0, 4, 10) + df["D"] = ["X"] * 10 # group by a key with single value axes = df.hist(by="D", rot=30) _check_axes_shape(axes, axes_num=1, layout=(1, 1)) _check_ticks_props(axes, xrot=30) - tm.close() + def test_grouped_hist_legacy_grouped_hist_kwargs(self): + from matplotlib.patches import Rectangle + + from pandas.plotting._matplotlib.hist import _grouped_hist + + rs = np.random.RandomState(42) + df = DataFrame(rs.randn(10, 1), columns=["A"]) + df["B"] = to_datetime( + rs.randint( + 812419200000000000, + 819331200000000000, + size=10, + dtype=np.int64, + ) + ) + df["C"] = rs.randint(0, 4, 10) # make sure kwargs to hist are handled xf, yf = 20, 18 xrot, yrot = 30, 40 @@ -655,16 +736,57 @@ def test_grouped_hist_legacy(self): tm.assert_almost_equal(height, 1.0) _check_ticks_props(axes, xlabelsize=xf, xrot=xrot, ylabelsize=yf, yrot=yrot) - tm.close() + def test_grouped_hist_legacy_grouped_hist(self): + from pandas.plotting._matplotlib.hist import _grouped_hist + + rs = np.random.RandomState(42) + df = DataFrame(rs.randn(10, 1), columns=["A"]) + df["B"] = to_datetime( + rs.randint( + 812419200000000000, + 819331200000000000, + size=10, + dtype=np.int64, + ) + ) + df["C"] = rs.randint(0, 4, 10) + df["D"] = ["X"] * 10 axes = _grouped_hist(df.A, by=df.C, log=True) # scale of y must be 'log' _check_ax_scales(axes, yaxis="log") - tm.close() + def test_grouped_hist_legacy_external_err(self): + from pandas.plotting._matplotlib.hist import _grouped_hist + + rs = np.random.RandomState(42) + df = DataFrame(rs.randn(10, 1), columns=["A"]) + df["B"] = to_datetime( + rs.randint( + 812419200000000000, + 819331200000000000, + size=10, + dtype=np.int64, + ) + ) + df["C"] = rs.randint(0, 4, 10) + df["D"] = ["X"] * 10 # propagate attr exception from matplotlib.Axes.hist with tm.external_error_raised(AttributeError): _grouped_hist(df.A, by=df.C, foo="bar") + def test_grouped_hist_legacy_figsize_err(self): + rs = np.random.RandomState(42) + df = DataFrame(rs.randn(10, 1), columns=["A"]) + df["B"] = to_datetime( + rs.randint( + 812419200000000000, + 819331200000000000, + size=10, + dtype=np.int64, + ) + ) + df["C"] = rs.randint(0, 4, 10) + df["D"] = ["X"] * 10 msg = "Specify figure size by tuple instead" with pytest.raises(ValueError, match=msg): df.hist(by="C", figsize="default") @@ -679,7 +801,6 @@ def test_grouped_hist_legacy2(self): axes = gb.hist() assert len(axes) == 2 assert len(mpl.pyplot.get_fignums()) == 2 - tm.close() @pytest.mark.slow @pytest.mark.parametrize( @@ -760,11 +881,20 @@ def test_grouped_hist_multiple_axes(self, hist_df): _check_axes_shape(returned, axes_num=3, layout=(1, 3)) tm.assert_numpy_array_equal(returned, axes[0]) assert returned[0].figure is fig + + def test_grouped_hist_multiple_axes_no_cols(self, hist_df): + # GH 6970, GH 7069 + df = hist_df + + fig, axes = mpl.pyplot.subplots(2, 3) returned = df.hist(by="classroom", ax=axes[1]) _check_axes_shape(returned, axes_num=3, layout=(1, 3)) tm.assert_numpy_array_equal(returned, axes[1]) assert returned[0].figure is fig + def test_grouped_hist_multiple_axes_error(self, hist_df): + # GH 6970, GH 7069 + df = hist_df fig, axes = mpl.pyplot.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" @@ -818,6 +948,6 @@ def test_axis_share_xy(self, hist_df): ) 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"]) + df = DataFrame(np.random.randint(1, 10, size=(10, 2)), columns=["a", "b"]) ax = df.hist(by="a", histtype=histtype) _check_patches_all_filled(ax, filled=expected)