diff --git a/pandas/tests/plotting/frame/test_frame.py b/pandas/tests/plotting/frame/test_frame.py index 52fbfc23ef66c..f79d651384c56 100644 --- a/pandas/tests/plotting/frame/test_frame.py +++ b/pandas/tests/plotting/frame/test_frame.py @@ -50,16 +50,21 @@ class TestDataFramePlots: - @pytest.mark.xfail(reason="Api changed in 3.6.0") @pytest.mark.slow def test_plot(self): df = tm.makeTimeDataFrame() _check_plot_works(df.plot, grid=False) + @pytest.mark.slow + def test_plot_subplots(self): + df = tm.makeTimeDataFrame() # _check_plot_works adds an ax so use default_axes=True to avoid warning axes = _check_plot_works(df.plot, default_axes=True, subplots=True) _check_axes_shape(axes, axes_num=4, layout=(4, 1)) + @pytest.mark.slow + def test_plot_subplots_negative_layout(self): + df = tm.makeTimeDataFrame() axes = _check_plot_works( df.plot, default_axes=True, @@ -68,6 +73,9 @@ def test_plot(self): ) _check_axes_shape(axes, axes_num=4, layout=(2, 2)) + @pytest.mark.slow + def test_plot_subplots_use_index(self): + df = tm.makeTimeDataFrame() axes = _check_plot_works( df.plot, default_axes=True, @@ -77,21 +85,38 @@ def test_plot(self): _check_ticks_props(axes, xrot=0) _check_axes_shape(axes, axes_num=4, layout=(4, 1)) + @pytest.mark.xfail(reason="Api changed in 3.6.0") + @pytest.mark.slow + def test_plot_invalid_arg(self): df = DataFrame({"x": [1, 2], "y": [3, 4]}) msg = "'Line2D' object has no property 'blarg'" with pytest.raises(AttributeError, match=msg): df.plot.line(blarg=True) + @pytest.mark.slow + def test_plot_tick_props(self): df = DataFrame(np.random.rand(10, 3), index=list(string.ascii_letters[:10])) ax = _check_plot_works(df.plot, use_index=True) _check_ticks_props(ax, xrot=0) - _check_plot_works(df.plot, yticks=[1, 5, 10]) - _check_plot_works(df.plot, xticks=[1, 5, 10]) - _check_plot_works(df.plot, ylim=(-100, 100), xlim=(-100, 100)) - _check_plot_works(df.plot, default_axes=True, subplots=True, title="blah") + @pytest.mark.slow + @pytest.mark.parametrize( + "kwargs", + [ + {"yticks": [1, 5, 10]}, + {"xticks": [1, 5, 10]}, + {"ylim": (-100, 100), "xlim": (-100, 100)}, + {"default_axes": True, "subplots": True, "title": "blah"}, + ], + ) + def test_plot_other_args(self, kwargs): + df = DataFrame(np.random.rand(10, 3), index=list(string.ascii_letters[:10])) + _check_plot_works(df.plot, **kwargs) + @pytest.mark.slow + def test_plot_visible_ax(self): + df = DataFrame(np.random.rand(10, 3), index=list(string.ascii_letters[:10])) # We have to redo it here because _check_plot_works does two plots, # once without an ax kwarg and once with an ax kwarg and the new sharex # behaviour does not remove the visibility of the latter axis (as ax is @@ -99,7 +124,6 @@ def test_plot(self): axes = df.plot(subplots=True, title="blah") _check_axes_shape(axes, axes_num=3, layout=(3, 1)) - # axes[0].figure.savefig("test.png") for ax in axes[:2]: _check_visible(ax.xaxis) # xaxis must be visible for grid _check_visible(ax.get_xticklabels(), visible=False) @@ -111,13 +135,20 @@ def test_plot(self): _check_visible([ax.xaxis.get_label()]) _check_ticks_props(ax, xrot=0) + @pytest.mark.slow + def test_plot_title(self): + df = DataFrame(np.random.rand(10, 3), index=list(string.ascii_letters[:10])) _check_plot_works(df.plot, title="blah") + @pytest.mark.slow + def test_plot_multiindex(self): tuples = zip(string.ascii_letters[:10], range(10)) df = DataFrame(np.random.rand(10, 3), index=MultiIndex.from_tuples(tuples)) ax = _check_plot_works(df.plot, use_index=True) _check_ticks_props(ax, xrot=0) + @pytest.mark.slow + def test_plot_multiindex_unicode(self): # unicode index = MultiIndex.from_tuples( [ @@ -138,23 +169,39 @@ def test_plot(self): df = DataFrame(np.random.randint(0, 10, (8, 2)), columns=columns, index=index) _check_plot_works(df.plot, title="\u03A3") + @pytest.mark.slow + @pytest.mark.parametrize("layout", [None, (-1, 1)]) + def test_plot_single_column_bar(self, layout): # GH 6951 # Test with single column df = DataFrame({"x": np.random.rand(10)}) - axes = _check_plot_works(df.plot.bar, subplots=True) + axes = _check_plot_works(df.plot.bar, subplots=True, layout=layout) _check_axes_shape(axes, axes_num=1, layout=(1, 1)) - axes = _check_plot_works(df.plot.bar, subplots=True, layout=(-1, 1)) - _check_axes_shape(axes, axes_num=1, layout=(1, 1)) + @pytest.mark.slow + def test_plot_passed_ax(self): # When ax is supplied and required number of axes is 1, # passed ax should be used: - fig, ax = mpl.pyplot.subplots() + df = DataFrame({"x": np.random.rand(10)}) + _, ax = mpl.pyplot.subplots() axes = df.plot.bar(subplots=True, ax=ax) assert len(axes) == 1 result = ax.axes assert result is axes[0] - def test_nullable_int_plot(self): + @pytest.mark.parametrize( + "cols, x, y", + [ + [list("ABCDE"), "A", "B"], + [["A", "B"], "A", "B"], + [["C", "A"], "C", "A"], + [["A", "C"], "A", "C"], + [["B", "C"], "B", "C"], + [["A", "D"], "A", "D"], + [["A", "E"], "A", "E"], + ], + ) + def test_nullable_int_plot(self, cols, x, y): # GH 32073 dates = ["2008", "2009", None, "2011", "2012"] df = DataFrame( @@ -167,32 +214,34 @@ def test_nullable_int_plot(self): } ) - _check_plot_works(df.plot, x="A", y="B") - _check_plot_works(df[["A", "B"]].plot, x="A", y="B") - _check_plot_works(df[["C", "A"]].plot, x="C", y="A") # nullable value on x-axis - _check_plot_works(df[["A", "C"]].plot, x="A", y="C") - _check_plot_works(df[["B", "C"]].plot, x="B", y="C") - _check_plot_works(df[["A", "D"]].plot, x="A", y="D") - _check_plot_works(df[["A", "E"]].plot, x="A", y="E") + _check_plot_works(df[cols].plot, x=x, y=y) @pytest.mark.slow - def test_integer_array_plot(self): + @pytest.mark.parametrize("plot", ["line", "bar", "hist", "pie"]) + def test_integer_array_plot_series(self, plot): # GH 25587 arr = pd.array([1, 2, 3, 4], dtype="UInt32") s = Series(arr) - _check_plot_works(s.plot.line) - _check_plot_works(s.plot.bar) - _check_plot_works(s.plot.hist) - _check_plot_works(s.plot.pie) + _check_plot_works(getattr(s.plot, plot)) + @pytest.mark.slow + @pytest.mark.parametrize( + "plot, kwargs", + [ + ["line", {}], + ["bar", {}], + ["hist", {}], + ["pie", {"y": "y"}], + ["scatter", {"x": "x", "y": "y"}], + ["hexbin", {"x": "x", "y": "y"}], + ], + ) + def test_integer_array_plot_df(self, plot, kwargs): + # GH 25587 + arr = pd.array([1, 2, 3, 4], dtype="UInt32") df = DataFrame({"x": arr, "y": arr}) - _check_plot_works(df.plot.line) - _check_plot_works(df.plot.bar) - _check_plot_works(df.plot.hist) - _check_plot_works(df.plot.pie, y="y") - _check_plot_works(df.plot.scatter, x="x", y="y") - _check_plot_works(df.plot.hexbin, x="x", y="y") + _check_plot_works(getattr(df.plot, plot), **kwargs) def test_nonnumeric_exclude(self): df = DataFrame({"A": ["x", "y", "z"], "B": [1, 2, 3]}) @@ -213,7 +262,7 @@ def test_donot_overwrite_index_name(self): def test_plot_xy(self): # columns.inferred_type == 'string' - df = tm.makeTimeDataFrame() + df = tm.makeTimeDataFrame(5) _check_data(df.plot(x=0, y=1), df.set_index("A")["B"].plot()) _check_data(df.plot(x=0), df.set_index("A").plot()) _check_data(df.plot(y=0), df.B.plot()) @@ -221,12 +270,16 @@ def test_plot_xy(self): _check_data(df.plot(x="A"), df.set_index("A").plot()) _check_data(df.plot(y="B"), df.B.plot()) + def test_plot_xy_int_cols(self): + df = tm.makeTimeDataFrame(5) # columns.inferred_type == 'integer' df.columns = np.arange(1, len(df.columns) + 1) _check_data(df.plot(x=1, y=2), df.set_index(1)[2].plot()) _check_data(df.plot(x=1), df.set_index(1).plot()) _check_data(df.plot(y=1), df[1].plot()) + def test_plot_xy_figsize_and_title(self): + df = tm.makeTimeDataFrame(5) # figsize and title ax = df.plot(x=1, y=2, title="Test", figsize=(16, 8)) _check_text_labels(ax.title, "Test") @@ -271,6 +324,9 @@ def test_xcompat(self): _check_ticks_props(ax, xrot=30) tm.close() + + def test_xcompat_plot_params(self): + df = tm.makeTimeDataFrame() plotting.plot_params["xaxis.compat"] = True ax = df.plot() lines = ax.get_lines() @@ -278,6 +334,9 @@ def test_xcompat(self): _check_ticks_props(ax, xrot=30) tm.close() + + def test_xcompat_plot_params_x_compat(self): + df = tm.makeTimeDataFrame() plotting.plot_params["x_compat"] = False ax = df.plot() @@ -286,6 +345,9 @@ def test_xcompat(self): assert isinstance(PeriodIndex(lines[0].get_xdata()), PeriodIndex) tm.close() + + def test_xcompat_plot_params_context_manager(self): + df = tm.makeTimeDataFrame() # useful if you're plotting a bunch together with plotting.plot_params.use("x_compat", True): ax = df.plot() @@ -294,6 +356,9 @@ def test_xcompat(self): _check_ticks_props(ax, xrot=30) tm.close() + + def test_xcompat_plot_period(self): + df = tm.makeTimeDataFrame() ax = df.plot() lines = ax.get_lines() assert not isinstance(lines[0].get_xdata(), PeriodIndex) @@ -313,9 +378,12 @@ def test_period_compat(self): mpl.pyplot.axhline(y=0) tm.close() - def test_unsorted_index(self): + @pytest.mark.parametrize("index_dtype", [np.int64, np.float64]) + def test_unsorted_index(self, index_dtype): df = DataFrame( - {"y": np.arange(100)}, index=np.arange(99, -1, -1), dtype=np.int64 + {"y": np.arange(100)}, + index=pd.Index(np.arange(99, -1, -1), dtype=index_dtype), + dtype=np.int64, ) ax = df.plot() lines = ax.get_lines()[0] @@ -324,31 +392,24 @@ def test_unsorted_index(self): tm.assert_series_equal(rs, df.y, check_index_type=False) tm.close() - df.index = pd.Index(np.arange(99, -1, -1), dtype=np.float64) - ax = df.plot() - lines = ax.get_lines()[0] - rs = lines.get_xydata() - rs = Series(rs[:, 1], rs[:, 0], dtype=np.int64, name="y") - tm.assert_series_equal(rs, df.y) - - def test_unsorted_index_lims(self): - df = DataFrame({"y": [0.0, 1.0, 2.0, 3.0]}, index=[1.0, 0.0, 3.0, 2.0]) - ax = df.plot() - xmin, xmax = ax.get_xlim() - lines = ax.get_lines() - assert xmin <= np.nanmin(lines[0].get_data()[0]) - assert xmax >= np.nanmax(lines[0].get_data()[0]) - - df = DataFrame( - {"y": [0.0, 1.0, np.nan, 3.0, 4.0, 5.0, 6.0]}, - index=[1.0, 0.0, 3.0, 2.0, np.nan, 3.0, 2.0], - ) + @pytest.mark.parametrize( + "df", + [ + DataFrame({"y": [0.0, 1.0, 2.0, 3.0]}, index=[1.0, 0.0, 3.0, 2.0]), + DataFrame( + {"y": [0.0, 1.0, np.nan, 3.0, 4.0, 5.0, 6.0]}, + index=[1.0, 0.0, 3.0, 2.0, np.nan, 3.0, 2.0], + ), + ], + ) + def test_unsorted_index_lims(self, df): ax = df.plot() xmin, xmax = ax.get_xlim() lines = ax.get_lines() assert xmin <= np.nanmin(lines[0].get_data()[0]) assert xmax >= np.nanmax(lines[0].get_data()[0]) + def test_unsorted_index_lims_x_y(self): df = DataFrame({"y": [0.0, 1.0, 2.0, 3.0], "z": [91.0, 90.0, 93.0, 92.0]}) ax = df.plot(x="z", y="y") xmin, xmax = ax.get_xlim() @@ -376,10 +437,18 @@ def _compare_stacked_y_cood(self, normal_lines, stacked_lines): tm.assert_numpy_array_equal(base, sy) @pytest.mark.parametrize("kind", ["line", "area"]) - def test_line_area_stacked(self, kind): + @pytest.mark.parametrize("mult", [1, -1]) + def test_line_area_stacked(self, kind, mult): + np_random = np.random.RandomState(42) + df = mult * DataFrame(np_random.rand(6, 4), columns=["w", "x", "y", "z"]) + + ax1 = _check_plot_works(df.plot, kind=kind, stacked=False) + ax2 = _check_plot_works(df.plot, kind=kind, stacked=True) + self._compare_stacked_y_cood(ax1.lines, ax2.lines) + + @pytest.mark.parametrize("kind", ["line", "area"]) + def test_line_area_stacked_sep_df(self, kind): np_random = np.random.RandomState(42) - df = DataFrame(np_random.rand(6, 4), columns=["w", "x", "y", "z"]) - neg_df = -df # each column has either positive or negative value sep_df = DataFrame( { @@ -389,27 +458,20 @@ def test_line_area_stacked(self, kind): "z": -np_random.rand(6), } ) - # each column has positive-negative mixed value - mixed_df = DataFrame( - np_random.randn(6, 4), - index=list(string.ascii_letters[:6]), - columns=["w", "x", "y", "z"], - ) - - ax1 = _check_plot_works(df.plot, kind=kind, stacked=False) - ax2 = _check_plot_works(df.plot, kind=kind, stacked=True) - self._compare_stacked_y_cood(ax1.lines, ax2.lines) - - ax1 = _check_plot_works(neg_df.plot, kind=kind, stacked=False) - ax2 = _check_plot_works(neg_df.plot, kind=kind, stacked=True) - self._compare_stacked_y_cood(ax1.lines, ax2.lines) - ax1 = _check_plot_works(sep_df.plot, kind=kind, stacked=False) ax2 = _check_plot_works(sep_df.plot, kind=kind, stacked=True) self._compare_stacked_y_cood(ax1.lines[:2], ax2.lines[:2]) self._compare_stacked_y_cood(ax1.lines[2:], ax2.lines[2:]) + def test_line_area_stacked_mixed(self): + np_random = np.random.RandomState(42) + mixed_df = DataFrame( + np_random.randn(6, 4), + index=list(string.ascii_letters[:6]), + columns=["w", "x", "y", "z"], + ) _check_plot_works(mixed_df.plot, stacked=False) + msg = ( "When stacked is True, each column must be either all positive or " "all negative. Column 'w' contains both positive and negative " @@ -418,6 +480,10 @@ def test_line_area_stacked(self, kind): with pytest.raises(ValueError, match=msg): mixed_df.plot(stacked=True) + @pytest.mark.parametrize("kind", ["line", "area"]) + def test_line_area_stacked_positive_idx(self, kind): + np_random = np.random.RandomState(42) + df = DataFrame(np_random.rand(6, 4), columns=["w", "x", "y", "z"]) # Use an index with strictly positive values, preventing # matplotlib from warning about ignoring xlim df2 = df.set_index(df.index + 1) @@ -478,20 +544,21 @@ def test_line_area_nan_df_stacked_area(self, idx, kwargs): else: tm.assert_numpy_array_equal(ax.lines[1].get_ydata(), expected1 + expected2) - def test_line_lim(self): - df = DataFrame(np.random.rand(6, 3), columns=["x", "y", "z"]) - ax = df.plot() - xmin, xmax = ax.get_xlim() - lines = ax.get_lines() - assert xmin <= lines[0].get_data()[0][0] - assert xmax >= lines[0].get_data()[0][-1] + ax = _check_plot_works(df.plot.area, stacked=False) + tm.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected1) + tm.assert_numpy_array_equal(ax.lines[1].get_ydata(), expected2) - ax = df.plot(secondary_y=True) + @pytest.mark.parametrize("kwargs", [{}, {"secondary_y": True}]) + def test_line_lim(self, kwargs): + df = DataFrame(np.random.rand(6, 3), columns=["x", "y", "z"]) + ax = df.plot(**kwargs) xmin, xmax = ax.get_xlim() lines = ax.get_lines() assert xmin <= lines[0].get_data()[0][0] assert xmax >= lines[0].get_data()[0][-1] + def test_line_lim_subplots(self): + df = DataFrame(np.random.rand(6, 3), columns=["x", "y", "z"]) axes = df.plot(secondary_y=True, subplots=True) _check_axes_shape(axes, axes_num=3, layout=(3, 1)) for ax in axes: @@ -536,19 +603,16 @@ def test_area_sharey_dont_overwrite(self): assert get_y_axis(ax1).joined(ax1, ax2) assert get_y_axis(ax2).joined(ax1, ax2) - def test_bar_linewidth(self): + @pytest.mark.parametrize("stacked", [True, False]) + def test_bar_linewidth(self, stacked): df = DataFrame(np.random.randn(5, 5)) - # regular - ax = df.plot.bar(linewidth=2) - for r in ax.patches: - assert r.get_linewidth() == 2 - - # stacked - ax = df.plot.bar(stacked=True, linewidth=2) + ax = df.plot.bar(stacked=stacked, linewidth=2) for r in ax.patches: assert r.get_linewidth() == 2 + def test_bar_linewidth_subplots(self): + df = DataFrame(np.random.randn(5, 5)) # subplots axes = df.plot.bar(linewidth=2, subplots=True) _check_axes_shape(axes, axes_num=5, layout=(5, 1)) @@ -556,44 +620,36 @@ def test_bar_linewidth(self): for r in ax.patches: assert r.get_linewidth() == 2 - def test_bar_barwidth(self): + @pytest.mark.parametrize( + "meth, dim", [("bar", "get_width"), ("barh", "get_height")] + ) + @pytest.mark.parametrize("stacked", [True, False]) + def test_bar_barwidth(self, meth, dim, stacked): df = DataFrame(np.random.randn(5, 5)) width = 0.9 - # regular - ax = df.plot.bar(width=width) + ax = getattr(df.plot, meth)(stacked=stacked, width=width) for r in ax.patches: - assert r.get_width() == width / len(df.columns) + if not stacked: + assert getattr(r, dim)() == width / len(df.columns) + else: + assert getattr(r, dim)() == width - # stacked - ax = df.plot.bar(stacked=True, width=width) - for r in ax.patches: - assert r.get_width() == width - - # horizontal regular - ax = df.plot.barh(width=width) - for r in ax.patches: - assert r.get_height() == width / len(df.columns) - - # horizontal stacked - ax = df.plot.barh(stacked=True, width=width) - for r in ax.patches: - assert r.get_height() == width + @pytest.mark.parametrize( + "meth, dim", [("bar", "get_width"), ("barh", "get_height")] + ) + def test_barh_barwidth_subplots(self, meth, dim): + df = DataFrame(np.random.randn(5, 5)) - # subplots - axes = df.plot.bar(width=width, subplots=True) - for ax in axes: - for r in ax.patches: - assert r.get_width() == width + width = 0.9 - # horizontal subplots - axes = df.plot.barh(width=width, subplots=True) + axes = getattr(df.plot, meth)(width=width, subplots=True) for ax in axes: for r in ax.patches: - assert r.get_height() == width + assert getattr(r, dim)() == width - def test_bar_bottom_left(self): + def test_bar_bottom_left_bottom(self): df = DataFrame(np.random.rand(5, 5)) ax = df.plot.bar(stacked=False, bottom=1) result = [p.get_y() for p in ax.patches] @@ -603,6 +659,8 @@ def test_bar_bottom_left(self): result = [p.get_y() for p in ax.patches[:5]] assert result == [-1, -2, -3, -4, -5] + def test_bar_bottom_left_left(self): + df = DataFrame(np.random.rand(5, 5)) ax = df.plot.barh(stacked=False, left=np.array([1, 1, 1, 1, 1])) result = [p.get_x() for p in ax.patches] assert result == [1] * 25 @@ -611,6 +669,8 @@ def test_bar_bottom_left(self): result = [p.get_x() for p in ax.patches[:5]] assert result == [1, 2, 3, 4, 5] + def test_bar_bottom_left_subplots(self): + df = DataFrame(np.random.rand(5, 5)) axes = df.plot.bar(subplots=True, bottom=-1) for ax in axes: result = [p.get_y() for p in ax.patches] @@ -628,7 +688,10 @@ def test_bar_nan(self): result = [p.get_height() for p in ax.patches] assert result == expected + def test_bar_nan_stacked(self): + df = DataFrame({"A": [10, np.nan, 20], "B": [5, 10, 20], "C": [1, 2, 3]}) ax = df.plot.bar(stacked=True) + expected = [10, 0, 20, 5, 10, 20, 1, 2, 3] result = [p.get_height() for p in ax.patches] assert result == expected @@ -636,45 +699,45 @@ def test_bar_nan(self): expected = [0.0, 0.0, 0.0, 10.0, 0.0, 20.0, 15.0, 10.0, 40.0] assert result == expected - def test_bar_categorical(self): + @pytest.mark.parametrize("idx", [pd.Index, pd.CategoricalIndex]) + def test_bar_categorical(self, idx): # GH 13019 - df1 = DataFrame( + df = DataFrame( np.random.randn(6, 5), - index=pd.Index(list("ABCDEF")), - columns=pd.Index(list("abcde")), + index=idx(list("ABCDEF")), + columns=idx(list("abcde")), ) - # categorical index must behave the same - df2 = DataFrame( - np.random.randn(6, 5), - index=pd.CategoricalIndex(list("ABCDEF")), - columns=pd.CategoricalIndex(list("abcde")), - ) - - 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])) - 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])) - assert ax.get_xlim() == (-0.5, 5.5) - assert ax.patches[0].get_x() == -0.25 - assert ax.patches[-1].get_x() == 4.75 - - def test_plot_scatter(self): + + ax = df.plot.bar() + ticks = ax.xaxis.get_ticklocs() + 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])) + assert ax.get_xlim() == (-0.5, 5.5) + assert ax.patches[0].get_x() == -0.25 + assert ax.patches[-1].get_x() == 4.75 + + @pytest.mark.parametrize("x, y", [("x", "y"), (1, 2)]) + def test_plot_scatter(self, x, y): df = DataFrame( np.random.randn(6, 4), index=list(string.ascii_letters[:6]), columns=["x", "y", "z", "four"], ) - _check_plot_works(df.plot.scatter, x="x", y="y") - _check_plot_works(df.plot.scatter, x=1, y=2) + _check_plot_works(df.plot.scatter, x=x, y=y) + def test_plot_scatter_error(self): + df = DataFrame( + np.random.randn(6, 4), + index=list(string.ascii_letters[:6]), + columns=["x", "y", "z", "four"], + ) msg = re.escape("scatter() missing 1 required positional argument: 'y'") with pytest.raises(TypeError, match=msg): df.plot.scatter(x="x") @@ -682,6 +745,12 @@ def test_plot_scatter(self): with pytest.raises(TypeError, match=msg): df.plot.scatter(y="y") + def test_plot_scatter_shape(self): + df = DataFrame( + np.random.randn(6, 4), + index=list(string.ascii_letters[:6]), + columns=["x", "y", "z", "four"], + ) # GH 6951 axes = df.plot(x="x", y="y", kind="scatter", subplots=True) _check_axes_shape(axes, axes_num=1, layout=(1, 1)) @@ -695,26 +764,22 @@ def test_raise_error_on_datetime_time_data(self): with pytest.raises(TypeError, match=msg): df.plot(kind="scatter", x="dtime", y="a") - def test_scatterplot_datetime_data(self): + @pytest.mark.parametrize("x, y", [("dates", "vals"), (0, 1)]) + def test_scatterplot_datetime_data(self, x, y): # GH 30391 dates = date_range(start=date(2019, 1, 1), periods=12, freq="W") vals = np.random.normal(0, 1, len(dates)) df = DataFrame({"dates": dates, "vals": vals}) - _check_plot_works(df.plot.scatter, x="dates", y="vals") - _check_plot_works(df.plot.scatter, x=0, y=1) + _check_plot_works(df.plot.scatter, x=x, y=y) - def test_scatterplot_object_data(self): + @pytest.mark.parametrize("x, y", [("a", "b"), (0, 1)]) + @pytest.mark.parametrize("b_col", [[2, 3, 4], ["a", "b", "c"]]) + def test_scatterplot_object_data(self, b_col, x, y): # GH 18755 - df = DataFrame({"a": ["A", "B", "C"], "b": [2, 3, 4]}) + df = DataFrame({"a": ["A", "B", "C"], "b": b_col}) - _check_plot_works(df.plot.scatter, x="a", y="b") - _check_plot_works(df.plot.scatter, x=0, y=1) - - df = DataFrame({"a": ["A", "B", "C"], "b": ["a", "b", "c"]}) - - _check_plot_works(df.plot.scatter, x="a", y="b") - _check_plot_works(df.plot.scatter, x=0, y=1) + _check_plot_works(df.plot.scatter, x=x, y=y) @pytest.mark.parametrize("ordered", [True, False]) @pytest.mark.parametrize( @@ -754,20 +819,26 @@ def test_plot_scatter_with_categorical_data(self, x, y): _check_plot_works(df.plot.scatter, x=x, y=y) - def test_plot_scatter_with_c(self): + @pytest.mark.parametrize("x, y, c", [("x", "y", "z"), (0, 1, 2)]) + def test_plot_scatter_with_c(self, x, y, c): df = DataFrame( np.random.randint(low=0, high=100, size=(6, 4)), index=list(string.ascii_letters[:6]), columns=["x", "y", "z", "four"], ) - axes = [df.plot.scatter(x="x", y="y", c="z"), df.plot.scatter(x=0, y=1, c=2)] - for ax in axes: - # default to Greys - assert ax.collections[0].cmap.name == "Greys" + ax = df.plot.scatter(x=x, y=y, c=c) + # default to Greys + assert ax.collections[0].cmap.name == "Greys" - assert ax.collections[0].colorbar.ax.get_ylabel() == "z" + assert ax.collections[0].colorbar.ax.get_ylabel() == "z" + def test_plot_scatter_with_c_props(self): + df = DataFrame( + np.random.randint(low=0, high=100, size=(6, 4)), + index=list(string.ascii_letters[:6]), + columns=["x", "y", "z", "four"], + ) cm = "cubehelix" ax = df.plot.scatter(x="x", y="y", c="z", colormap=cm) assert ax.collections[0].cmap.name == cm @@ -781,6 +852,7 @@ def test_plot_scatter_with_c(self): assert ax.collections[0].colorbar is None _check_colors(ax.collections, facecolors=["r"]) + def test_plot_scatter_with_c_array(self): # Ensure that we can pass an np.array straight through to matplotlib, # this functionality was accidentally removed previously. # See https://github.com/pandas-dev/pandas/issues/8852 for bug report @@ -812,8 +884,6 @@ def test_plot_scatter_with_s(self): def test_plot_scatter_with_norm(self): # added while fixing GH 45809 - import matplotlib as mpl - df = DataFrame(np.random.random((10, 3)) * 100, columns=["a", "b", "c"]) norm = mpl.colors.LogNorm() ax = df.plot.scatter(x="a", y="b", c="c", norm=norm) @@ -821,8 +891,6 @@ def test_plot_scatter_with_norm(self): def test_plot_scatter_without_norm(self): # added while fixing GH 45809 - import matplotlib as mpl - df = DataFrame(np.random.random((10, 3)) * 100, columns=["a", "b", "c"]) ax = df.plot.scatter(x="a", y="b", c="c") plot_norm = ax.collections[0].norm @@ -832,18 +900,26 @@ def test_plot_scatter_without_norm(self): assert plot_norm(value) == default_norm(value) @pytest.mark.slow - def test_plot_bar(self): + @pytest.mark.parametrize( + "kwargs", + [ + {}, + {"legend": False}, + {"default_axes": True, "subplots": True}, + {"stacked": True}, + ], + ) + def test_plot_bar(self, kwargs): df = DataFrame( np.random.randn(6, 4), index=list(string.ascii_letters[:6]), columns=["one", "two", "three", "four"], ) - _check_plot_works(df.plot.bar) - _check_plot_works(df.plot.bar, legend=False) - _check_plot_works(df.plot.bar, default_axes=True, subplots=True) - _check_plot_works(df.plot.bar, stacked=True) + _check_plot_works(df.plot.bar, **kwargs) + @pytest.mark.slow + def test_plot_bar_int_col(self): df = DataFrame( np.random.randn(10, 15), index=list(string.ascii_letters[:10]), @@ -851,6 +927,8 @@ def test_plot_bar(self): ) _check_plot_works(df.plot.bar) + @pytest.mark.slow + def test_plot_bar_ticks(self): df = DataFrame({"a": [0, 1], "b": [1, 0]}) ax = _check_plot_works(df.plot.bar) _check_ticks_props(ax, xrot=90) @@ -858,6 +936,9 @@ def test_plot_bar(self): ax = df.plot.bar(rot=35, fontsize=10) _check_ticks_props(ax, xrot=35, xlabelsize=10, ylabelsize=10) + @pytest.mark.slow + def test_plot_barh_ticks(self): + df = DataFrame({"a": [0, 1], "b": [1, 0]}) ax = _check_plot_works(df.plot.barh) _check_ticks_props(ax, yrot=0) @@ -866,7 +947,6 @@ def test_plot_bar(self): def test_boxplot(self, hist_df): df = hist_df - series = df["height"] numeric_cols = df._get_numeric_data().columns labels = [pprint_thing(c) for c in numeric_cols] @@ -878,12 +958,17 @@ def test_boxplot(self, hist_df): assert len(ax.lines) == 7 * len(numeric_cols) tm.close() + def test_boxplot_series(self, hist_df): + df = hist_df + series = df["height"] axes = series.plot.box(rot=40) _check_ticks_props(axes, xrot=40, yrot=0) tm.close() - ax = _check_plot_works(series.plot.box) + _check_plot_works(series.plot.box) + def test_boxplot_series_positions(self, hist_df): + df = hist_df positions = np.array([1, 6, 7]) ax = df.plot.box(positions=positions) numeric_cols = df._get_numeric_data().columns @@ -903,6 +988,11 @@ def test_boxplot_vertical(self, hist_df): _check_text_labels(ax.get_yticklabels(), labels) assert len(ax.lines) == 7 * len(numeric_cols) + @pytest.mark.filterwarnings("ignore:Attempt:UserWarning") + def test_boxplot_vertical_subplots(self, hist_df): + df = hist_df + numeric_cols = df._get_numeric_data().columns + labels = [pprint_thing(c) for c in numeric_cols] axes = _check_plot_works( df.plot.box, default_axes=True, @@ -916,13 +1006,17 @@ def test_boxplot_vertical(self, hist_df): _check_text_labels(ax.get_yticklabels(), [label]) assert len(ax.lines) == 7 + def test_boxplot_vertical_positions(self, hist_df): + df = hist_df + numeric_cols = df._get_numeric_data().columns + labels = [pprint_thing(c) for c in numeric_cols] positions = np.array([3, 2, 8]) ax = df.plot.box(positions=positions, vert=False) _check_text_labels(ax.get_yticklabels(), labels) tm.assert_numpy_array_equal(ax.yaxis.get_ticklocs(), positions) assert len(ax.lines) == 7 * len(numeric_cols) - def test_boxplot_return_type(self): + def test_boxplot_return_type_invalid(self): df = DataFrame( np.random.randn(6, 4), index=list(string.ascii_letters[:6]), @@ -932,17 +1026,15 @@ def test_boxplot_return_type(self): with pytest.raises(ValueError, match=msg): df.plot.box(return_type="not_a_type") - result = df.plot.box(return_type="dict") - _check_box_return_type(result, "dict") - - result = df.plot.box(return_type="axes") - _check_box_return_type(result, "axes") - - result = df.plot.box() # default axes - _check_box_return_type(result, "axes") - - result = df.plot.box(return_type="both") - _check_box_return_type(result, "both") + @pytest.mark.parametrize("return_type", ["dict", "axes", "both"]) + def test_boxplot_return_type_invalid_type(self, return_type): + df = DataFrame( + np.random.randn(6, 4), + index=list(string.ascii_letters[:6]), + columns=["one", "two", "three", "four"], + ) + result = df.plot.box(return_type=return_type) + _check_box_return_type(result, return_type) @td.skip_if_no_scipy def test_kde_df(self): @@ -952,9 +1044,15 @@ def test_kde_df(self): _check_legend_labels(ax, labels=expected) _check_ticks_props(ax, xrot=0) + @td.skip_if_no_scipy + def test_kde_df_rot(self): + df = DataFrame(np.random.randn(10, 4)) ax = df.plot(kind="kde", rot=20, fontsize=5) _check_ticks_props(ax, xrot=20, xlabelsize=5, ylabelsize=5) + @td.skip_if_no_scipy + def test_kde_df_subplots(self): + df = DataFrame(np.random.randn(10, 4)) axes = _check_plot_works( df.plot, default_axes=True, @@ -963,6 +1061,9 @@ def test_kde_df(self): ) _check_axes_shape(axes, axes_num=4, layout=(4, 1)) + @td.skip_if_no_scipy + def test_kde_df_logy(self): + df = DataFrame(np.random.randn(10, 4)) axes = df.plot(kind="kde", logy=True, subplots=True) _check_ax_scales(axes, yaxis="log") @@ -973,10 +1074,7 @@ def test_kde_missing_vals(self): _check_plot_works(df.plot, kind="kde") def test_hist_df(self): - from matplotlib.patches import Rectangle - df = DataFrame(np.random.randn(100, 4)) - series = df[0] ax = _check_plot_works(df.plot.hist) expected = [pprint_thing(c) for c in df.columns] @@ -991,22 +1089,34 @@ def test_hist_df(self): _check_axes_shape(axes, axes_num=4, layout=(4, 1)) _check_ax_scales(axes, yaxis="log") + def test_hist_df_series(self): + series = Series(np.random.rand(10)) axes = series.plot.hist(rot=40) _check_ticks_props(axes, xrot=40, yrot=0) tm.close() + def test_hist_df_series_cumulative_density(self): + from matplotlib.patches import Rectangle + + series = Series(np.random.rand(10)) ax = series.plot.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() + def test_hist_df_series_cumulative(self): + from matplotlib.patches import Rectangle + + series = Series(np.random.rand(10)) ax = series.plot.hist(cumulative=True, bins=4) rects = [x for x in ax.get_children() if isinstance(x, Rectangle)] - tm.assert_almost_equal(rects[-2].get_height(), 100.0) + tm.assert_almost_equal(rects[-2].get_height(), 10.0) tm.close() + def test_hist_df_orientation(self): + df = DataFrame(np.random.randn(10, 4)) # if horizontal, yticklabels are rotated axes = df.plot.hist(rot=50, fontsize=8, orientation="horizontal") _check_ticks_props(axes, xrot=0, yrot=50, ylabelsize=8) @@ -1053,17 +1163,14 @@ def _check_box_coord( if expected_w is not None: tm.assert_numpy_array_equal(result_width, expected_w, check_dtype=False) - def test_hist_df_coord(self): - normal_df = DataFrame( + @pytest.mark.parametrize( + "data", + [ { "A": np.repeat(np.array([1, 2, 3, 4, 5]), np.array([10, 9, 8, 7, 6])), "B": np.repeat(np.array([1, 2, 3, 4, 5]), np.array([8, 8, 8, 8, 8])), "C": np.repeat(np.array([1, 2, 3, 4, 5]), np.array([6, 7, 8, 9, 10])), }, - columns=["A", "B", "C"], - ) - - nan_df = DataFrame( { "A": np.repeat( np.array([np.nan, 1, 2, 3, 4, 5]), np.array([3, 10, 9, 8, 7, 6]) @@ -1075,136 +1182,139 @@ def test_hist_df_coord(self): np.array([1, 2, 3, np.nan, 4, 5]), np.array([6, 7, 8, 3, 9, 10]) ), }, - columns=["A", "B", "C"], + ], + ) + def test_hist_df_coord(self, data): + df = DataFrame(data) + + ax = df.plot.hist(bins=5) + self._check_box_coord( + ax.patches[:5], + expected_y=np.array([0, 0, 0, 0, 0]), + expected_h=np.array([10, 9, 8, 7, 6]), + ) + self._check_box_coord( + ax.patches[5:10], + expected_y=np.array([0, 0, 0, 0, 0]), + expected_h=np.array([8, 8, 8, 8, 8]), + ) + self._check_box_coord( + ax.patches[10:], + expected_y=np.array([0, 0, 0, 0, 0]), + expected_h=np.array([6, 7, 8, 9, 10]), ) - for df in [normal_df, nan_df]: - ax = df.plot.hist(bins=5) - self._check_box_coord( - ax.patches[:5], - expected_y=np.array([0, 0, 0, 0, 0]), - expected_h=np.array([10, 9, 8, 7, 6]), - ) - self._check_box_coord( - ax.patches[5:10], - expected_y=np.array([0, 0, 0, 0, 0]), - expected_h=np.array([8, 8, 8, 8, 8]), - ) - self._check_box_coord( - ax.patches[10:], - expected_y=np.array([0, 0, 0, 0, 0]), - expected_h=np.array([6, 7, 8, 9, 10]), - ) - - ax = df.plot.hist(bins=5, stacked=True) - self._check_box_coord( - ax.patches[:5], - expected_y=np.array([0, 0, 0, 0, 0]), - expected_h=np.array([10, 9, 8, 7, 6]), - ) - self._check_box_coord( - ax.patches[5:10], - expected_y=np.array([10, 9, 8, 7, 6]), - expected_h=np.array([8, 8, 8, 8, 8]), - ) - self._check_box_coord( - ax.patches[10:], - expected_y=np.array([18, 17, 16, 15, 14]), - expected_h=np.array([6, 7, 8, 9, 10]), - ) + ax = df.plot.hist(bins=5, stacked=True) + self._check_box_coord( + ax.patches[:5], + expected_y=np.array([0, 0, 0, 0, 0]), + expected_h=np.array([10, 9, 8, 7, 6]), + ) + self._check_box_coord( + ax.patches[5:10], + expected_y=np.array([10, 9, 8, 7, 6]), + expected_h=np.array([8, 8, 8, 8, 8]), + ) + self._check_box_coord( + ax.patches[10:], + expected_y=np.array([18, 17, 16, 15, 14]), + expected_h=np.array([6, 7, 8, 9, 10]), + ) - axes = df.plot.hist(bins=5, stacked=True, subplots=True) - self._check_box_coord( - axes[0].patches, - expected_y=np.array([0, 0, 0, 0, 0]), - expected_h=np.array([10, 9, 8, 7, 6]), - ) - self._check_box_coord( - axes[1].patches, - expected_y=np.array([0, 0, 0, 0, 0]), - expected_h=np.array([8, 8, 8, 8, 8]), - ) - self._check_box_coord( - axes[2].patches, - expected_y=np.array([0, 0, 0, 0, 0]), - expected_h=np.array([6, 7, 8, 9, 10]), - ) + axes = df.plot.hist(bins=5, stacked=True, subplots=True) + self._check_box_coord( + axes[0].patches, + expected_y=np.array([0, 0, 0, 0, 0]), + expected_h=np.array([10, 9, 8, 7, 6]), + ) + self._check_box_coord( + axes[1].patches, + expected_y=np.array([0, 0, 0, 0, 0]), + expected_h=np.array([8, 8, 8, 8, 8]), + ) + self._check_box_coord( + axes[2].patches, + expected_y=np.array([0, 0, 0, 0, 0]), + expected_h=np.array([6, 7, 8, 9, 10]), + ) - # horizontal - ax = df.plot.hist(bins=5, orientation="horizontal") - self._check_box_coord( - ax.patches[:5], - expected_x=np.array([0, 0, 0, 0, 0]), - expected_w=np.array([10, 9, 8, 7, 6]), - ) - self._check_box_coord( - ax.patches[5:10], - expected_x=np.array([0, 0, 0, 0, 0]), - expected_w=np.array([8, 8, 8, 8, 8]), - ) - self._check_box_coord( - ax.patches[10:], - expected_x=np.array([0, 0, 0, 0, 0]), - expected_w=np.array([6, 7, 8, 9, 10]), - ) + # horizontal + ax = df.plot.hist(bins=5, orientation="horizontal") + self._check_box_coord( + ax.patches[:5], + expected_x=np.array([0, 0, 0, 0, 0]), + expected_w=np.array([10, 9, 8, 7, 6]), + ) + self._check_box_coord( + ax.patches[5:10], + expected_x=np.array([0, 0, 0, 0, 0]), + expected_w=np.array([8, 8, 8, 8, 8]), + ) + self._check_box_coord( + ax.patches[10:], + expected_x=np.array([0, 0, 0, 0, 0]), + expected_w=np.array([6, 7, 8, 9, 10]), + ) - ax = df.plot.hist(bins=5, stacked=True, orientation="horizontal") - self._check_box_coord( - ax.patches[:5], - expected_x=np.array([0, 0, 0, 0, 0]), - expected_w=np.array([10, 9, 8, 7, 6]), - ) - self._check_box_coord( - ax.patches[5:10], - expected_x=np.array([10, 9, 8, 7, 6]), - expected_w=np.array([8, 8, 8, 8, 8]), - ) - self._check_box_coord( - ax.patches[10:], - expected_x=np.array([18, 17, 16, 15, 14]), - expected_w=np.array([6, 7, 8, 9, 10]), - ) + ax = df.plot.hist(bins=5, stacked=True, orientation="horizontal") + self._check_box_coord( + ax.patches[:5], + expected_x=np.array([0, 0, 0, 0, 0]), + expected_w=np.array([10, 9, 8, 7, 6]), + ) + self._check_box_coord( + ax.patches[5:10], + expected_x=np.array([10, 9, 8, 7, 6]), + expected_w=np.array([8, 8, 8, 8, 8]), + ) + self._check_box_coord( + ax.patches[10:], + expected_x=np.array([18, 17, 16, 15, 14]), + expected_w=np.array([6, 7, 8, 9, 10]), + ) - axes = df.plot.hist( - bins=5, stacked=True, subplots=True, orientation="horizontal" - ) - self._check_box_coord( - axes[0].patches, - expected_x=np.array([0, 0, 0, 0, 0]), - expected_w=np.array([10, 9, 8, 7, 6]), - ) - self._check_box_coord( - axes[1].patches, - expected_x=np.array([0, 0, 0, 0, 0]), - expected_w=np.array([8, 8, 8, 8, 8]), - ) - self._check_box_coord( - axes[2].patches, - expected_x=np.array([0, 0, 0, 0, 0]), - expected_w=np.array([6, 7, 8, 9, 10]), - ) + axes = df.plot.hist( + bins=5, stacked=True, subplots=True, orientation="horizontal" + ) + self._check_box_coord( + axes[0].patches, + expected_x=np.array([0, 0, 0, 0, 0]), + expected_w=np.array([10, 9, 8, 7, 6]), + ) + self._check_box_coord( + axes[1].patches, + expected_x=np.array([0, 0, 0, 0, 0]), + expected_w=np.array([8, 8, 8, 8, 8]), + ) + self._check_box_coord( + axes[2].patches, + expected_x=np.array([0, 0, 0, 0, 0]), + expected_w=np.array([6, 7, 8, 9, 10]), + ) def test_plot_int_columns(self): df = DataFrame(np.random.randn(100, 4)).cumsum() _check_plot_works(df.plot, legend=True) - def test_style_by_column(self): - import matplotlib.pyplot as plt - - fig = plt.gcf() - - df = DataFrame(np.random.randn(100, 3)) - for markers in [ + @pytest.mark.parametrize( + "markers", + [ {0: "^", 1: "+", 2: "o"}, {0: "^", 1: "+"}, ["^", "+", "o"], ["^", "+"], - ]: - fig.clf() - fig.add_subplot(111) - ax = df.plot(style=markers) - for idx, line in enumerate(ax.get_lines()[: len(markers)]): - assert line.get_marker() == markers[idx] + ], + ) + def test_style_by_column(self, markers): + import matplotlib.pyplot as plt + + fig = plt.gcf() + fig.clf() + fig.add_subplot(111) + df = DataFrame(np.random.randn(10, 3)) + ax = df.plot(style=markers) + for idx, line in enumerate(ax.get_lines()[: len(markers)]): + assert line.get_marker() == markers[idx] def test_line_label_none(self): s = Series([1, 2]) @@ -1244,35 +1354,35 @@ def test_unordered_ts(self): tm.assert_numpy_array_equal(ydata, np.array([1.0, 2.0, 3.0])) @td.skip_if_no_scipy - def test_kind_both_ways(self): + @pytest.mark.parametrize("kind", plotting.PlotAccessor._common_kinds) + def test_kind_both_ways(self, kind): df = DataFrame({"x": [1, 2, 3]}) - for kind in plotting.PlotAccessor._common_kinds: - df.plot(kind=kind) - getattr(df.plot, kind)() - for kind in ["scatter", "hexbin"]: - df.plot("x", "x", kind=kind) - getattr(df.plot, kind)("x", "x") + df.plot(kind=kind) + getattr(df.plot, kind)() + + @td.skip_if_no_scipy + @pytest.mark.parametrize("kind", ["scatter", "hexbin"]) + def test_kind_both_ways_x_y(self, kind): + df = DataFrame({"x": [1, 2, 3]}) + df.plot("x", "x", kind=kind) + getattr(df.plot, kind)("x", "x") - def test_all_invalid_plot_data(self): + @pytest.mark.parametrize("kind", plotting.PlotAccessor._common_kinds) + def test_all_invalid_plot_data(self, kind): df = DataFrame(list("abcd")) - for kind in plotting.PlotAccessor._common_kinds: - msg = "no numeric data to plot" - with pytest.raises(TypeError, match=msg): - df.plot(kind=kind) + msg = "no numeric data to plot" + with pytest.raises(TypeError, match=msg): + df.plot(kind=kind) - def test_partially_invalid_plot_data(self): + @pytest.mark.parametrize( + "kind", list(plotting.PlotAccessor._common_kinds) + ["area"] + ) + def test_partially_invalid_plot_data_numeric(self, kind): df = DataFrame(np.random.RandomState(42).randn(10, 2), dtype=object) df[np.random.rand(df.shape[0]) > 0.5] = "a" - for kind in plotting.PlotAccessor._common_kinds: - msg = "no numeric data to plot" - with pytest.raises(TypeError, match=msg): - df.plot(kind=kind) - - # area plot doesn't support positive/negative mixed data - df = DataFrame(np.random.RandomState(42).rand(10, 2), dtype=object) - df[np.random.rand(df.shape[0]) > 0.5] = "a" - with pytest.raises(TypeError, match="no numeric data to plot"): - df.plot(kind="area") + msg = "no numeric data to plot" + with pytest.raises(TypeError, match=msg): + df.plot(kind=kind) def test_invalid_kind(self): df = DataFrame(np.random.randn(10, 2)) @@ -1343,6 +1453,14 @@ def test_hexbin_basic(self): # TODO: need better way to test. This just does existence. assert len(ax.collections) == 1 + def test_hexbin_basic_subplots(self): + df = DataFrame( + { + "A": np.random.uniform(size=20), + "B": np.random.uniform(size=20), + "C": np.arange(20) + np.random.uniform(size=20), + } + ) # GH 6951 axes = df.plot.hexbin(x="A", y="B", subplots=True) # hexbin should have 2 axes in the figure, 1 for plotting and another @@ -1351,7 +1469,8 @@ def test_hexbin_basic(self): # return value is single axes _check_axes_shape(axes, axes_num=1, layout=(1, 1)) - def test_hexbin_with_c(self): + @pytest.mark.parametrize("reduce_C", [None, np.std]) + def test_hexbin_with_c(self, reduce_C): df = DataFrame( { "A": np.random.uniform(size=20), @@ -1360,10 +1479,7 @@ def test_hexbin_with_c(self): } ) - ax = df.plot.hexbin(x="A", y="B", C="C") - assert len(ax.collections) == 1 - - ax = df.plot.hexbin(x="A", y="B", C="C", reduce_C_function=np.std) + ax = df.plot.hexbin(x="A", y="B", C="C", reduce_C_function=reduce_C) assert len(ax.collections) == 1 @pytest.mark.parametrize( @@ -1385,7 +1501,7 @@ def test_hexbin_cmap(self, kwargs, expected): ax = df.plot.hexbin(x="A", y="B", **kwargs) assert ax.collections[0].cmap.name == expected - def test_pie_df(self): + def test_pie_df_err(self): df = DataFrame( np.random.rand(5, 3), columns=["X", "Y", "Z"], @@ -1395,12 +1511,22 @@ def test_pie_df(self): with pytest.raises(ValueError, match=msg): df.plot.pie() - ax = _check_plot_works(df.plot.pie, y="Y") - _check_text_labels(ax.texts, df.index) - - ax = _check_plot_works(df.plot.pie, y=2) + @pytest.mark.parametrize("y", ["Y", 2]) + def test_pie_df(self, y): + df = DataFrame( + np.random.rand(5, 3), + columns=["X", "Y", "Z"], + index=["a", "b", "c", "d", "e"], + ) + ax = _check_plot_works(df.plot.pie, y=y) _check_text_labels(ax.texts, df.index) + def test_pie_df_subplots(self): + df = DataFrame( + np.random.rand(5, 3), + columns=["X", "Y", "Z"], + index=["a", "b", "c", "d", "e"], + ) axes = _check_plot_works( df.plot.pie, default_axes=True, @@ -1412,6 +1538,12 @@ def test_pie_df(self): for ax, ylabel in zip(axes, df.columns): assert ax.get_ylabel() == ylabel + def test_pie_df_labels_colors(self): + df = DataFrame( + np.random.rand(5, 3), + columns=["X", "Y", "Z"], + index=["a", "b", "c", "d", "e"], + ) labels = ["A", "B", "C", "D", "E"] color_args = ["r", "g", "b", "c", "m"] axes = _check_plot_works( @@ -1431,7 +1563,7 @@ def test_pie_df_nan(self): df = DataFrame(np.random.rand(4, 4)) for i in range(4): df.iloc[i, i] = np.nan - fig, axes = mpl.pyplot.subplots(ncols=4) + _, axes = mpl.pyplot.subplots(ncols=4) # GH 37668 kwargs = {"normalize": True} @@ -1454,27 +1586,39 @@ def test_pie_df_nan(self): assert result_labels == expected_labels @pytest.mark.slow - def test_errorbar_plot(self): + @pytest.mark.parametrize( + "kwargs", + [ + {"logy": True}, + {"logx": True, "logy": True}, + {"loglog": True}, + ], + ) + def test_errorbar_plot(self, kwargs): d = {"x": np.arange(12), "y": np.arange(12, 0, -1)} df = DataFrame(d) d_err = {"x": np.ones(12) * 0.2, "y": np.ones(12) * 0.4} df_err = DataFrame(d_err) # check line plots - ax = _check_plot_works(df.plot, yerr=df_err, logy=True) - _check_has_errorbars(ax, xerr=0, yerr=2) - - ax = _check_plot_works(df.plot, yerr=df_err, logx=True, logy=True) - _check_has_errorbars(ax, xerr=0, yerr=2) - - ax = _check_plot_works(df.plot, yerr=df_err, loglog=True) + ax = _check_plot_works(df.plot, yerr=df_err, **kwargs) _check_has_errorbars(ax, xerr=0, yerr=2) + @pytest.mark.slow + def test_errorbar_plot_bar(self): + d = {"x": np.arange(12), "y": np.arange(12, 0, -1)} + df = DataFrame(d) + d_err = {"x": np.ones(12) * 0.2, "y": np.ones(12) * 0.4} + df_err = DataFrame(d_err) ax = _check_plot_works( (df + 1).plot, yerr=df_err, xerr=df_err, kind="bar", log=True ) _check_has_errorbars(ax, xerr=2, yerr=2) + @pytest.mark.slow + def test_errorbar_plot_yerr_array(self): + d = {"x": np.arange(12), "y": np.arange(12, 0, -1)} + df = DataFrame(d) # yerr is raw error values ax = _check_plot_works(df["y"].plot, yerr=np.ones(12) * 0.4) _check_has_errorbars(ax, xerr=0, yerr=1) @@ -1482,47 +1626,72 @@ def test_errorbar_plot(self): ax = _check_plot_works(df.plot, yerr=np.ones((2, 12)) * 0.4) _check_has_errorbars(ax, xerr=0, yerr=2) - # yerr is column name - for yerr in ["yerr", "誤差"]: - s_df = df.copy() - s_df[yerr] = np.ones(12) * 0.2 + @pytest.mark.slow + @pytest.mark.parametrize("yerr", ["yerr", "誤差"]) + def test_errorbar_plot_column_name(self, yerr): + d = {"x": np.arange(12), "y": np.arange(12, 0, -1)} + df = DataFrame(d) + df[yerr] = np.ones(12) * 0.2 - ax = _check_plot_works(s_df.plot, yerr=yerr) - _check_has_errorbars(ax, xerr=0, yerr=2) + ax = _check_plot_works(df.plot, yerr=yerr) + _check_has_errorbars(ax, xerr=0, yerr=2) - ax = _check_plot_works(s_df.plot, y="y", x="x", yerr=yerr) - _check_has_errorbars(ax, xerr=0, yerr=1) + ax = _check_plot_works(df.plot, y="y", x="x", yerr=yerr) + _check_has_errorbars(ax, xerr=0, yerr=1) + @pytest.mark.slow + def test_errorbar_plot_external_valueerror(self): + d = {"x": np.arange(12), "y": np.arange(12, 0, -1)} + df = DataFrame(d) with tm.external_error_raised(ValueError): df.plot(yerr=np.random.randn(11)) + @pytest.mark.slow + def test_errorbar_plot_external_typeerror(self): + d = {"x": np.arange(12), "y": np.arange(12, 0, -1)} + df = DataFrame(d) df_err = DataFrame({"x": ["zzz"] * 12, "y": ["zzz"] * 12}) with tm.external_error_raised(TypeError): df.plot(yerr=df_err) @pytest.mark.slow @pytest.mark.parametrize("kind", ["line", "bar", "barh"]) - def test_errorbar_plot_different_kinds(self, kind): - d = {"x": np.arange(12), "y": np.arange(12, 0, -1)} - df = DataFrame(d) - d_err = {"x": np.ones(12) * 0.2, "y": np.ones(12) * 0.4} - df_err = DataFrame(d_err) - - ax = _check_plot_works(df.plot, yerr=df_err["x"], kind=kind) - _check_has_errorbars(ax, xerr=0, yerr=2) + @pytest.mark.parametrize( + "y_err", + [ + Series(np.ones(12) * 0.2, name="x"), + DataFrame({"x": np.ones(12) * 0.2, "y": np.ones(12) * 0.4}), + ], + ) + def test_errorbar_plot_different_yerr(self, kind, y_err): + df = DataFrame({"x": np.arange(12), "y": np.arange(12, 0, -1)}) - ax = _check_plot_works(df.plot, yerr=d_err, kind=kind) + ax = _check_plot_works(df.plot, yerr=y_err, kind=kind) _check_has_errorbars(ax, xerr=0, yerr=2) - ax = _check_plot_works(df.plot, yerr=df_err, xerr=df_err, kind=kind) - _check_has_errorbars(ax, xerr=2, yerr=2) - - ax = _check_plot_works(df.plot, yerr=df_err["x"], xerr=df_err["x"], kind=kind) - _check_has_errorbars(ax, xerr=2, yerr=2) - - ax = _check_plot_works(df.plot, xerr=0.2, yerr=0.2, kind=kind) + @pytest.mark.slow + @pytest.mark.parametrize("kind", ["line", "bar", "barh"]) + @pytest.mark.parametrize( + "y_err, x_err", + [ + ( + DataFrame({"x": np.ones(12) * 0.2, "y": np.ones(12) * 0.4}), + DataFrame({"x": np.ones(12) * 0.2, "y": np.ones(12) * 0.4}), + ), + (Series(np.ones(12) * 0.2, name="x"), Series(np.ones(12) * 0.2, name="x")), + (0.2, 0.2), + ], + ) + def test_errorbar_plot_different_yerr_xerr(self, kind, y_err, x_err): + df = DataFrame({"x": np.arange(12), "y": np.arange(12, 0, -1)}) + ax = _check_plot_works(df.plot, yerr=y_err, xerr=x_err, kind=kind) _check_has_errorbars(ax, xerr=2, yerr=2) + @pytest.mark.slow + @pytest.mark.parametrize("kind", ["line", "bar", "barh"]) + def test_errorbar_plot_different_yerr_xerr_subplots(self, kind): + df = DataFrame({"x": np.arange(12), "y": np.arange(12, 0, -1)}) + df_err = DataFrame({"x": np.ones(12) * 0.2, "y": np.ones(12) * 0.4}) axes = _check_plot_works( df.plot, default_axes=True, @@ -1664,6 +1833,7 @@ def test_errorbar_scatter(self): ax = _check_plot_works(df.plot.scatter, x="x", y="y", xerr=df_err, yerr=df_err) _check_has_errorbars(ax, xerr=1, yerr=1) + def test_errorbar_scatter_color(self): def _check_errorbar_color(containers, expected, has_err="has_xerr"): lines = [] errs = [c.lines for c in ax.containers if getattr(c, has_err, False)][0] @@ -1735,6 +1905,20 @@ def _check(axes): _check(axes) tm.close() + def test_sharex_false_and_ax(self): + # https://github.com/pandas-dev/pandas/issues/9737 using gridspec, + # the axis in fig.get_axis() are sorted differently than pandas + # expected them, so make sure that only the right ones are removed + import matplotlib.pyplot as plt + + df = DataFrame( + { + "a": [1, 2, 3, 4, 5, 6], + "b": [1, 2, 3, 4, 5, 6], + "c": [1, 2, 3, 4, 5, 6], + "d": [1, 2, 3, 4, 5, 6], + } + ) gs, axes = _generate_4_axes_via_gridspec() # without sharex, no labels should be touched! for ax in axes: @@ -1789,6 +1973,18 @@ def _check(axes): _check(axes) tm.close() + def test_sharey_and_ax_tight(self): + # https://github.com/pandas-dev/pandas/issues/9737 using gridspec, + import matplotlib.pyplot as plt + + df = DataFrame( + { + "a": [1, 2, 3, 4, 5, 6], + "b": [1, 2, 3, 4, 5, 6], + "c": [1, 2, 3, 4, 5, 6], + "d": [1, 2, 3, 4, 5, 6], + } + ) gs, axes = _generate_4_axes_via_gridspec() # without sharex, no labels should be touched! for ax in axes: @@ -1830,7 +2026,7 @@ def test_memory_leak(self, kind): gc.collect() assert ref() is None - def test_df_gridspec_patterns(self): + def test_df_gridspec_patterns_vert_horiz(self): # GH 10819 from matplotlib import gridspec import matplotlib.pyplot as plt @@ -1862,7 +2058,7 @@ def _get_horizontal_grid(): _check_visible(ax.get_yticklabels(), visible=True) _check_visible(ax.get_xticklabels(), visible=True) _check_visible(ax.get_xticklabels(minor=True), visible=True) - tm.close() + plt.close("all") # subplots=True for ax1, ax2 in [_get_vertical_grid(), _get_horizontal_grid()]: @@ -1873,7 +2069,7 @@ def _get_horizontal_grid(): _check_visible(ax.get_yticklabels(), visible=True) _check_visible(ax.get_xticklabels(), visible=True) _check_visible(ax.get_xticklabels(minor=True), visible=True) - tm.close() + plt.close("all") # vertical / subplots / sharex=True / sharey=True ax1, ax2 = _get_vertical_grid() @@ -1889,7 +2085,7 @@ def _get_horizontal_grid(): _check_visible(axes[0].get_xticklabels(minor=True), visible=False) _check_visible(axes[1].get_xticklabels(), visible=True) _check_visible(axes[1].get_xticklabels(minor=True), visible=True) - tm.close() + plt.close("all") # horizontal / subplots / sharex=True / sharey=True ax1, ax2 = _get_horizontal_grid() @@ -1904,7 +2100,14 @@ def _get_horizontal_grid(): # xaxis are visible because there is only one column _check_visible(ax.get_xticklabels(), visible=True) _check_visible(ax.get_xticklabels(minor=True), visible=True) - tm.close() + plt.close("all") + + def test_df_gridspec_patterns_boxed(self): + # GH 10819 + from matplotlib import gridspec + import matplotlib.pyplot as plt + + ts = Series(np.random.randn(10), index=date_range("1/1/2000", periods=10)) # boxed def _get_boxed_grid(): @@ -1925,7 +2128,7 @@ def _get_boxed_grid(): _check_visible(ax.get_yticklabels(), visible=True) _check_visible(ax.get_xticklabels(), visible=True) _check_visible(ax.get_xticklabels(minor=True), visible=True) - tm.close() + plt.close("all") # subplots / sharex=True / sharey=True axes = _get_boxed_grid() @@ -1943,7 +2146,7 @@ def _get_boxed_grid(): for ax in [axes[2], axes[3]]: # bottom row _check_visible(ax.get_xticklabels(), visible=True) _check_visible(ax.get_xticklabels(minor=True), visible=True) - tm.close() + plt.close("all") def test_df_grid_settings(self): # Make sure plot defaults to rcParams['axes.grid'] setting, GH 9792 @@ -1960,6 +2163,7 @@ def test_plain_axes(self): fig.add_axes([0.2, 0.2, 0.2, 0.2]) Series(np.random.rand(10)).plot(ax=ax) + def test_plain_axes_df(self): # supplied ax itself is a plain Axes, but because the cmap keyword # a new ax is created for the colorbar -> also multiples axes (GH11520) df = DataFrame({"a": np.random.randn(8), "b": np.random.randn(8)}) @@ -1967,6 +2171,7 @@ def test_plain_axes(self): ax = fig.add_axes((0, 0, 1, 1)) df.plot(kind="scatter", ax=ax, x="a", y="b", c="a", cmap="hsv") + def test_plain_axes_make_axes_locatable(self): # other examples fig, ax = mpl.pyplot.subplots() from mpl_toolkits.axes_grid1 import make_axes_locatable @@ -1976,6 +2181,7 @@ def test_plain_axes(self): Series(np.random.rand(10)).plot(ax=ax) Series(np.random.rand(10)).plot(ax=cax) + def test_plain_axes_make_inset_axes(self): fig, ax = mpl.pyplot.subplots() from mpl_toolkits.axes_grid1.inset_locator import inset_axes @@ -2230,8 +2436,6 @@ def test_secondary_y(self, secondary_y): def _generate_4_axes_via_gridspec(): - import matplotlib as mpl - import matplotlib.gridspec import matplotlib.pyplot as plt gs = mpl.gridspec.GridSpec(2, 2)