diff --git a/doc/_templates/autosummary/accessor_callable.rst b/doc/_templates/autosummary/accessor_callable.rst new file mode 100644 index 0000000000000..6f45e0fd01e16 --- /dev/null +++ b/doc/_templates/autosummary/accessor_callable.rst @@ -0,0 +1,6 @@ +{{ fullname }} +{{ underline }} + +.. currentmodule:: {{ module.split('.')[0] }} + +.. autoaccessorcallable:: {{ [module.split('.')[1], objname]|join('.') }}.__call__ diff --git a/doc/source/api.rst b/doc/source/api.rst index 38c2c1091469b..1fe1d8751bf12 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -672,12 +672,34 @@ the Categorical back to a numpy array, so levels and order information is not pr Plotting ~~~~~~~~ +``Series.plot`` is both a callable method and a namespace attribute for +specific plotting methods of the form ``Series.plot.``. + .. autosummary:: :toctree: generated/ + :template: autosummary/accessor_callable.rst - Series.hist Series.plot +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_method.rst + + Series.plot.area + Series.plot.bar + Series.plot.barh + Series.plot.box + Series.plot.density + Series.plot.hist + Series.plot.kde + Series.plot.line + Series.plot.pie + +.. autosummary:: + :toctree: generated/ + + Series.hist + Serialization / IO / Conversion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autosummary:: @@ -946,14 +968,41 @@ Time series-related DataFrame.tz_convert DataFrame.tz_localize +.. _api.dataframe.plotting: + Plotting ~~~~~~~~ + +``DataFrame.plot`` is both a callable method and a namespace attribute for +specific plotting methods of the form ``DataFrame.plot.``. + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_plot.rst + + DataFrame.plot + +.. autosummary:: + :toctree: generated/ + :template: autosummary/accessor_method.rst + + DataFrame.plot.area + DataFrame.plot.bar + DataFrame.plot.barh + DataFrame.plot.box + DataFrame.plot.density + DataFrame.plot.hexbin + DataFrame.plot.hist + DataFrame.plot.kde + DataFrame.plot.line + DataFrame.plot.pie + DataFrame.plot.scatter + .. autosummary:: :toctree: generated/ DataFrame.boxplot DataFrame.hist - DataFrame.plot Serialization / IO / Conversion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/source/conf.py b/doc/source/conf.py index 57c1667dca0c3..f2a033eb82d9c 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -214,7 +214,7 @@ # template names. # Add redirect for previously existing API pages (which are now included in -# the API pages as top-level functions) based on a template (GH9911) +# the API pages as top-level functions) based on a template (GH9911) moved_api_pages = [ 'pandas.core.common.isnull', 'pandas.core.common.notnull', 'pandas.core.reshape.get_dummies', 'pandas.tools.merge.concat', 'pandas.tools.merge.merge', 'pandas.tools.pivot.pivot_table', @@ -327,6 +327,7 @@ from sphinx.util import rpartition from sphinx.ext.autodoc import Documenter, MethodDocumenter, AttributeDocumenter +from sphinx.ext.autosummary import Autosummary class AccessorLevelDocumenter(Documenter): @@ -388,6 +389,44 @@ class AccessorMethodDocumenter(AccessorLevelDocumenter, MethodDocumenter): directivetype = 'method' +class AccessorCallableDocumenter(AccessorLevelDocumenter, MethodDocumenter): + """ + This documenter lets us removes .__call__ from the method signature for + callable accessors like Series.plot + """ + objtype = 'accessorcallable' + directivetype = 'method' + + # lower than MethodDocumenter; otherwise the doc build prints warnings + priority = 0.5 + + def format_name(self): + return MethodDocumenter.format_name(self).rstrip('.__call__') + + +class PandasAutosummary(Autosummary): + """ + This alternative autosummary class lets us override the table summary for + Series.plot and DataFrame.plot in the API docs. + """ + + def _replace_pandas_items(self, display_name, sig, summary, real_name): + # this a hack: ideally we should extract the signature from the + # .__call__ method instead of hard coding this + if display_name == 'DataFrame.plot': + sig = '([x, y, kind, ax, ....])' + summary = 'DataFrame plotting accessor and method' + elif display_name == 'Series.plot': + sig = '([kind, ax, figsize, ....])' + summary = 'Series plotting accessor and method' + return (display_name, sig, summary, real_name) + + def get_items(self, names): + items = Autosummary.get_items(self, names) + items = [self._replace_pandas_items(*item) for item in items] + return items + + # remove the docstring of the flags attribute (inherited from numpy ndarray) # because these give doc build errors (see GH issue 5331) def remove_flags_docstring(app, what, name, obj, options, lines): @@ -398,3 +437,5 @@ def setup(app): app.connect("autodoc-process-docstring", remove_flags_docstring) app.add_autodocumenter(AccessorAttributeDocumenter) app.add_autodocumenter(AccessorMethodDocumenter) + app.add_autodocumenter(AccessorCallableDocumenter) + app.add_directive('autosummary', PandasAutosummary) diff --git a/doc/source/visualization.rst b/doc/source/visualization.rst index 8785a8d092d48..b6ee2d83fd131 100644 --- a/doc/source/visualization.rst +++ b/doc/source/visualization.rst @@ -121,8 +121,9 @@ You can plot one column versus another using the `x` and `y` keywords in Other Plots ----------- -The ``kind`` keyword argument of :meth:`~DataFrame.plot` accepts -a handful of values for plots other than the default Line plot. +Plotting methods allow for a handful of plot styles other than the +default Line plot. These methods can be provided as the ``kind`` +keyword argument to :meth:`~DataFrame.plot`. These include: * :ref:`'bar' ` or :ref:`'barh' ` for bar plots @@ -134,6 +135,19 @@ These include: * :ref:`'hexbin' ` for hexagonal bin plots * :ref:`'pie' ` for pie plots +.. versionadded:: 0.17 + +You can also create these other plots using the methods ``DataFrame.plot.`` instead of providing the ``kind`` keyword argument. This makes it easier to discover plot methods and the specific arguments they use: + +.. ipython:: + :verbatim: + + In [14]: df = pd.DataFrame() + + In [15]: df.plot. + df.plot.area df.plot.barh df.plot.density df.plot.hist df.plot.line df.plot.scatter + df.plot.bar df.plot.box df.plot.hexbin df.plot.kde df.plot.pie + In addition to these ``kind`` s, there are the :ref:`DataFrame.hist() `, and :ref:`DataFrame.boxplot() ` methods, which use a separate interface. diff --git a/doc/source/whatsnew/v0.17.0.txt b/doc/source/whatsnew/v0.17.0.txt index 0b6f6522dfde0..a29e7127f5f88 100644 --- a/doc/source/whatsnew/v0.17.0.txt +++ b/doc/source/whatsnew/v0.17.0.txt @@ -29,6 +29,7 @@ users upgrade to this version. Highlights include: - Release the Global Interpreter Lock (GIL) on some cython operations, see :ref:`here ` +- Plotting methods are now available as attributes of the ``.plot`` accessor, see :ref:`here ` - The sorting API has been revamped to remove some long-time inconsistencies, see :ref:`here ` - Support for a ``datetime64[ns]`` with timezones as a first-class dtype, see :ref:`here ` - The default for ``to_datetime`` will now be to ``raise`` when presented with unparseable formats, @@ -116,6 +117,35 @@ Releasing of the GIL could benefit an application that uses threads for user int .. _dask: https://dask.readthedocs.org/en/latest/ .. _QT: https://wiki.python.org/moin/PyQt +.. _whatsnew_0170.plot: + +Plot submethods +^^^^^^^^^^^^^^^ + +The Series and DataFrame ``.plot()`` method allows for customizing :ref:`plot types` by supplying the ``kind`` keyword arguments. Unfortunately, many of these kinds of plots use different required and optional keyword arguments, which makes it difficult to discover what any given plot kind uses out of the dozens of possible arguments. + +To alleviate this issue, we have added a new, optional plotting interface, which exposes each kind of plot as a method of the ``.plot`` attribute. Instead of writing ``series.plot(kind=, ...)``, you can now also use ``series.plot.(...)``: + +.. ipython:: + :verbatim: + + In [13]: df = pd.DataFrame(np.random.rand(10, 2), columns=['a', 'b']) + + In [14]: df.plot.bar() + +.. image:: _static/whatsnew_plot_submethods.png + +As a result of this change, these methods are now all discoverable via tab-completion: + +.. ipython:: + :verbatim: + + In [15]: df.plot. + df.plot.area df.plot.barh df.plot.density df.plot.hist df.plot.line df.plot.scatter + df.plot.bar df.plot.box df.plot.hexbin df.plot.kde df.plot.pie + +Each method signature only includes relevant arguments. Currently, these are limited to required arguments, but in the future these will include optional arguments, as well. For an overview, see the new :ref:`api.dataframe.plotting` API documentation. + .. _whatsnew_0170.strftime: Support strftime for Datetimelikes @@ -251,7 +281,6 @@ has been changed to make this keyword unnecessary - the change is shown below. Excel files saved in version 0.16.2 or prior that had index names will still able to be read in, but the ``has_index_names`` argument must specified to ``True``. - .. _whatsnew_0170.enhancements.other: Other enhancements diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 5ab75f7d2658a..77b8c4cf35aad 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -47,13 +47,14 @@ OrderedDict, raise_with_traceback) from pandas import compat from pandas.sparse.array import SparseArray -from pandas.util.decorators import deprecate, Appender, Substitution, \ - deprecate_kwarg +from pandas.util.decorators import (cache_readonly, deprecate, Appender, + Substitution, deprecate_kwarg) from pandas.tseries.period import PeriodIndex from pandas.tseries.index import DatetimeIndex import pandas.core.algorithms as algos +import pandas.core.base as base import pandas.core.common as com import pandas.core.format as fmt import pandas.core.nanops as nanops @@ -5432,7 +5433,7 @@ def _put_str(s, space): import pandas.tools.plotting as gfx -DataFrame.plot = gfx.plot_frame +DataFrame.plot = base.AccessorProperty(gfx.FramePlotMethods, gfx.FramePlotMethods) DataFrame.hist = gfx.hist_frame diff --git a/pandas/core/groupby.py b/pandas/core/groupby.py index 1d5a92d43d680..6b4b8be1430fe 100644 --- a/pandas/core/groupby.py +++ b/pandas/core/groupby.py @@ -296,6 +296,28 @@ def _get_binner_for_grouping(self, obj): def groups(self): return self.grouper.groups + +class GroupByPlot(PandasObject): + """ + Class implementing the .plot attribute for groupby objects + """ + def __init__(self, groupby): + self._groupby = groupby + + def __call__(self, *args, **kwargs): + def f(self, *args, **kwargs): + return self.plot(*args, **kwargs) + f.__name__ = 'plot' + return self._groupby.apply(f) + + def __getattr__(self, name): + def attr(*args, **kwargs): + def f(self): + return getattr(self.plot, name)(*args, **kwargs) + return self._groupby.apply(f) + return attr + + class GroupBy(PandasObject): """ @@ -538,6 +560,8 @@ def __getattr__(self, attr): def __getitem__(self, key): raise NotImplementedError('Not implemented: %s' % key) + plot = property(GroupByPlot) + def _make_wrapper(self, name): if name not in self._apply_whitelist: is_callable = callable(getattr(self._selected_obj, name, None)) diff --git a/pandas/core/series.py b/pandas/core/series.py index 0c44f79febdda..067c514fa37cd 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2882,7 +2882,7 @@ def __init__(self, *args, **kwargs): import pandas.tools.plotting as _gfx -Series.plot = _gfx.plot_series +Series.plot = base.AccessorProperty(_gfx.SeriesPlotMethods, _gfx.SeriesPlotMethods) Series.hist = _gfx.hist_series # Add arithmetic! diff --git a/pandas/tests/test_graphics.py b/pandas/tests/test_graphics.py index d1f1f2196558a..ad9518116a066 100644 --- a/pandas/tests/test_graphics.py +++ b/pandas/tests/test_graphics.py @@ -505,8 +505,8 @@ def test_plot(self): ax = _check_plot_works(self.ts.plot, style='.', loglog=True) self._check_ax_scales(ax, xaxis='log', yaxis='log') - _check_plot_works(self.ts[:10].plot, kind='bar') - _check_plot_works(self.ts.plot, kind='area', stacked=False) + _check_plot_works(self.ts[:10].plot.bar) + _check_plot_works(self.ts.plot.area, stacked=False) _check_plot_works(self.iseries.plot) for kind in ['line', 'bar', 'barh', 'kde', 'hist', 'box']: @@ -514,8 +514,8 @@ def test_plot(self): continue _check_plot_works(self.series[:5].plot, kind=kind) - _check_plot_works(self.series[:10].plot, kind='barh') - ax = _check_plot_works(Series(randn(10)).plot, kind='bar', color='black') + _check_plot_works(self.series[:10].plot.barh) + ax = _check_plot_works(Series(randn(10)).plot.bar, color='black') self._check_colors([ax.patches[0]], facecolors=['black']) # GH 6951 @@ -555,7 +555,7 @@ def test_ts_line_lim(self): self.assertEqual(xmax, lines[0].get_data(orig=False)[0][-1]) def test_ts_area_lim(self): - ax = self.ts.plot(kind='area', stacked=False) + ax = self.ts.plot.area(stacked=False) xmin, xmax = ax.get_xlim() line = ax.get_lines()[0].get_data(orig=False)[0] self.assertEqual(xmin, line[0]) @@ -563,7 +563,7 @@ def test_ts_area_lim(self): tm.close() # GH 7471 - ax = self.ts.plot(kind='area', stacked=False, x_compat=True) + ax = self.ts.plot.area(stacked=False, x_compat=True) xmin, xmax = ax.get_xlim() line = ax.get_lines()[0].get_data(orig=False)[0] self.assertEqual(xmin, line[0]) @@ -572,14 +572,14 @@ def test_ts_area_lim(self): tz_ts = self.ts.copy() tz_ts.index = tz_ts.tz_localize('GMT').tz_convert('CET') - ax = tz_ts.plot(kind='area', stacked=False, x_compat=True) + ax = tz_ts.plot.area(stacked=False, x_compat=True) xmin, xmax = ax.get_xlim() line = ax.get_lines()[0].get_data(orig=False)[0] self.assertEqual(xmin, line[0]) self.assertEqual(xmax, line[-1]) tm.close() - ax = tz_ts.plot(kind='area', stacked=False, secondary_y=True) + ax = tz_ts.plot.area(stacked=False, secondary_y=True) xmin, xmax = ax.get_xlim() line = ax.get_lines()[0].get_data(orig=False)[0] self.assertEqual(xmin, line[0]) @@ -623,9 +623,9 @@ def test_line_area_nan_series(self): expected = np.array([1, 2, 0, 3]) ax = _check_plot_works(d.plot, stacked=True) self.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected) - ax = _check_plot_works(d.plot, kind='area') + ax = _check_plot_works(d.plot.area) self.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected) - ax = _check_plot_works(d.plot, kind='area', stacked=False) + ax = _check_plot_works(d.plot.area, stacked=False) self.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected) def test_line_use_index_false(self): @@ -634,7 +634,7 @@ def test_line_use_index_false(self): ax = s.plot(use_index=False) label = ax.get_xlabel() self.assertEqual(label, '') - ax2 = s.plot(kind='bar', use_index=False) + ax2 = s.plot.bar(use_index=False) label2 = ax2.get_xlabel() self.assertEqual(label2, '') @@ -645,11 +645,11 @@ def test_bar_log(self): if not self.mpl_le_1_2_1: expected = np.hstack((.1, expected, 1e4)) - ax = Series([200, 500]).plot(log=True, kind='bar') + ax = Series([200, 500]).plot.bar(log=True) tm.assert_numpy_array_equal(ax.yaxis.get_ticklocs(), expected) tm.close() - ax = Series([200, 500]).plot(log=True, kind='barh') + ax = Series([200, 500]).plot.barh(log=True) tm.assert_numpy_array_equal(ax.xaxis.get_ticklocs(), expected) tm.close() @@ -671,7 +671,7 @@ def test_bar_log(self): @slow def test_bar_ignore_index(self): df = Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd']) - ax = df.plot(kind='bar', use_index=False) + ax = df.plot.bar(use_index=False) self._check_text_labels(ax.get_xticklabels(), ['0', '1', '2', '3']) def test_rotation(self): @@ -697,17 +697,17 @@ def test_pie_series(self): # if sum of values is less than 1.0, pie handle them as rate and draw semicircle. series = Series(np.random.randint(1, 5), index=['a', 'b', 'c', 'd', 'e'], name='YLABEL') - ax = _check_plot_works(series.plot, kind='pie') + ax = _check_plot_works(series.plot.pie) self._check_text_labels(ax.texts, series.index) self.assertEqual(ax.get_ylabel(), 'YLABEL') # without wedge labels - ax = _check_plot_works(series.plot, kind='pie', labels=None) + ax = _check_plot_works(series.plot.pie, labels=None) self._check_text_labels(ax.texts, [''] * 5) # with less colors than elements color_args = ['r', 'g', 'b'] - ax = _check_plot_works(series.plot, kind='pie', colors=color_args) + ax = _check_plot_works(series.plot.pie, colors=color_args) color_expected = ['r', 'g', 'b', 'r', 'g'] self._check_colors(ax.patches, facecolors=color_expected) @@ -715,12 +715,12 @@ def test_pie_series(self): # with labels and colors labels = ['A', 'B', 'C', 'D', 'E'] color_args = ['r', 'g', 'b', 'c', 'm'] - ax = _check_plot_works(series.plot, kind='pie', labels=labels, colors=color_args) + ax = _check_plot_works(series.plot.pie, labels=labels, colors=color_args) self._check_text_labels(ax.texts, labels) self._check_colors(ax.patches, facecolors=color_args) # with autopct and fontsize - ax = _check_plot_works(series.plot, kind='pie', colors=color_args, + ax = _check_plot_works(series.plot.pie, colors=color_args, autopct='%.2f', fontsize=7) pcts = ['{0:.2f}'.format(s * 100) for s in series.values / float(series.sum())] iters = [iter(series.index), iter(pcts)] @@ -732,17 +732,17 @@ def test_pie_series(self): # includes negative value with tm.assertRaises(ValueError): series = Series([1, 2, 0, 4, -1], index=['a', 'b', 'c', 'd', 'e']) - series.plot(kind='pie') + series.plot.pie() # includes nan series = Series([1, 2, np.nan, 4], index=['a', 'b', 'c', 'd'], name='YLABEL') - ax = _check_plot_works(series.plot, kind='pie') + ax = _check_plot_works(series.plot.pie) self._check_text_labels(ax.texts, ['a', 'b', '', 'd']) def test_pie_nan(self): s = Series([1, np.nan, 1, 1]) - ax = s.plot(kind='pie', legend=True) + ax = s.plot.pie(legend=True) expected = ['0', '', '2', '3'] result = [x.get_text() for x in ax.texts] self.assertEqual(result, expected) @@ -750,7 +750,7 @@ def test_pie_nan(self): @slow def test_hist_df_kwargs(self): df = DataFrame(np.random.randn(10, 2)) - ax = df.plot(kind='hist', bins=5) + ax = df.plot.hist(bins=5) self.assertEqual(len(ax.patches), 10) @slow @@ -759,10 +759,10 @@ def test_hist_df_with_nonnumerics(self): with tm.RNGContext(1): df = DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D']) df['E'] = ['x', 'y'] * 5 - ax = df.plot(kind='hist', bins=5) + ax = df.plot.hist(bins=5) self.assertEqual(len(ax.patches), 20) - ax = df.plot(kind='hist') # bins=10 + ax = df.plot.hist() # bins=10 self.assertEqual(len(ax.patches), 40) @slow @@ -848,8 +848,8 @@ def test_hist_secondary_legend(self): df = DataFrame(np.random.randn(30, 4), columns=list('abcd')) # primary -> secondary - ax = df['a'].plot(kind='hist', legend=True) - df['b'].plot(kind='hist', ax=ax, legend=True, secondary_y=True) + ax = df['a'].plot.hist(legend=True) + df['b'].plot.hist(ax=ax, legend=True, secondary_y=True) # both legends are dran on left ax # left and right axis must be visible self._check_legend_labels(ax, labels=['a', 'b (right)']) @@ -858,8 +858,8 @@ def test_hist_secondary_legend(self): tm.close() # secondary -> secondary - ax = df['a'].plot(kind='hist', legend=True, secondary_y=True) - df['b'].plot(kind='hist', ax=ax, legend=True, secondary_y=True) + ax = df['a'].plot.hist(legend=True, secondary_y=True) + df['b'].plot.hist(ax=ax, legend=True, secondary_y=True) # both legends are draw on left ax # left axis must be invisible, right axis must be visible self._check_legend_labels(ax.left_ax, labels=['a (right)', 'b (right)']) @@ -868,9 +868,9 @@ def test_hist_secondary_legend(self): tm.close() # secondary -> primary - ax = df['a'].plot(kind='hist', legend=True, secondary_y=True) + ax = df['a'].plot.hist(legend=True, secondary_y=True) # right axes is returned - df['b'].plot(kind='hist', ax=ax, legend=True) + df['b'].plot.hist(ax=ax, legend=True) # both legends are draw on left ax # left and right axis must be visible self._check_legend_labels(ax.left_ax, labels=['a (right)', 'b']) @@ -945,7 +945,7 @@ def test_plot_fails_with_dupe_color_and_style(self): @slow def test_hist_kde(self): - ax = self.ts.plot(kind='hist', logy=True) + ax = self.ts.plot.hist(logy=True) self._check_ax_scales(ax, yaxis='log') xlabels = ax.get_xticklabels() # ticks are values, thus ticklabels are blank @@ -955,9 +955,9 @@ def test_hist_kde(self): tm._skip_if_no_scipy() _skip_if_no_scipy_gaussian_kde() - _check_plot_works(self.ts.plot, kind='kde') - _check_plot_works(self.ts.plot, kind='density') - ax = self.ts.plot(kind='kde', logy=True) + _check_plot_works(self.ts.plot.kde) + _check_plot_works(self.ts.plot.density) + ax = self.ts.plot.kde(logy=True) self._check_ax_scales(ax, yaxis='log') xlabels = ax.get_xticklabels() self._check_text_labels(xlabels, [''] * len(xlabels)) @@ -969,9 +969,9 @@ def test_kde_kwargs(self): tm._skip_if_no_scipy() _skip_if_no_scipy_gaussian_kde() from numpy import linspace - _check_plot_works(self.ts.plot, kind='kde', bw_method=.5, ind=linspace(-100,100,20)) - _check_plot_works(self.ts.plot, kind='density', bw_method=.5, ind=linspace(-100,100,20)) - ax = self.ts.plot(kind='kde', logy=True, bw_method=.5, ind=linspace(-100,100,20)) + _check_plot_works(self.ts.plot.kde, bw_method=.5, ind=linspace(-100,100,20)) + _check_plot_works(self.ts.plot.density, bw_method=.5, ind=linspace(-100,100,20)) + ax = self.ts.plot.kde(logy=True, bw_method=.5, ind=linspace(-100,100,20)) self._check_ax_scales(ax, yaxis='log') self._check_text_labels(ax.yaxis.get_label(), 'Density') @@ -981,33 +981,33 @@ def test_kde_missing_vals(self): _skip_if_no_scipy_gaussian_kde() s = Series(np.random.uniform(size=50)) s[0] = np.nan - ax = _check_plot_works(s.plot, kind='kde') + ax = _check_plot_works(s.plot.kde) @slow def test_hist_kwargs(self): - ax = self.ts.plot(kind='hist', bins=5) + ax = self.ts.plot.hist(bins=5) self.assertEqual(len(ax.patches), 5) self._check_text_labels(ax.yaxis.get_label(), 'Frequency') tm.close() if self.mpl_ge_1_3_1: - ax = self.ts.plot(kind='hist', orientation='horizontal') + ax = self.ts.plot.hist(orientation='horizontal') self._check_text_labels(ax.xaxis.get_label(), 'Frequency') tm.close() - ax = self.ts.plot(kind='hist', align='left', stacked=True) + ax = self.ts.plot.hist(align='left', stacked=True) tm.close() @slow def test_hist_kde_color(self): - ax = self.ts.plot(kind='hist', logy=True, bins=10, color='b') + ax = self.ts.plot.hist(logy=True, bins=10, color='b') self._check_ax_scales(ax, yaxis='log') self.assertEqual(len(ax.patches), 10) self._check_colors(ax.patches, facecolors=['b'] * 10) tm._skip_if_no_scipy() _skip_if_no_scipy_gaussian_kde() - ax = self.ts.plot(kind='kde', logy=True, color='r') + ax = self.ts.plot.kde(logy=True, color='r') self._check_ax_scales(ax, yaxis='log') lines = ax.get_lines() self.assertEqual(len(lines), 1) @@ -1015,13 +1015,22 @@ def test_hist_kde_color(self): @slow def test_boxplot_series(self): - ax = self.ts.plot(kind='box', logy=True) + ax = self.ts.plot.box(logy=True) self._check_ax_scales(ax, yaxis='log') xlabels = ax.get_xticklabels() self._check_text_labels(xlabels, [self.ts.name]) ylabels = ax.get_yticklabels() self._check_text_labels(ylabels, [''] * len(ylabels)) + @slow + def test_kind_both_ways(self): + s = Series(range(3)) + for kind in plotting._common_kinds + plotting._series_kinds: + if not _ok_for_gaussian_kde(kind): + continue + s.plot(kind=kind) + getattr(s.plot, kind)() + @slow def test_invalid_plot_data(self): s = Series(list('abcd')) @@ -1216,7 +1225,7 @@ def test_plot(self): df = DataFrame({'x': [1, 2], 'y': [3, 4]}) with tm.assertRaises(TypeError): - df.plot(kind='line', blarg=True) + df.plot.line(blarg=True) df = DataFrame(np.random.rand(10, 3), index=list(string.ascii_letters[:10])) @@ -1272,16 +1281,16 @@ def test_plot(self): # GH 6951 # Test with single column df = DataFrame({'x': np.random.rand(10)}) - axes = _check_plot_works(df.plot, kind='bar', subplots=True) + axes = _check_plot_works(df.plot.bar, subplots=True) self._check_axes_shape(axes, axes_num=1, layout=(1, 1)) - axes = _check_plot_works(df.plot, kind='bar', subplots=True, + axes = _check_plot_works(df.plot.bar, subplots=True, layout=(-1, 1)) self._check_axes_shape(axes, axes_num=1, layout=(1, 1)) # When ax is supplied and required number of axes is 1, # passed ax should be used: fig, ax = self.plt.subplots() - axes = df.plot(kind='bar', subplots=True, ax=ax) + axes = df.plot.bar(subplots=True, ax=ax) self.assertEqual(len(axes), 1) self.assertIs(ax.get_axes(), axes[0]) @@ -1674,9 +1683,9 @@ def test_negative_log(self): columns=['x', 'y', 'z', 'four']) with tm.assertRaises(ValueError): - df.plot(kind='area', logy=True) + df.plot.area(logy=True) with tm.assertRaises(ValueError): - df.plot(kind='area', loglog=True) + df.plot.area(loglog=True) def _compare_stacked_y_cood(self, normal_lines, stacked_lines): base = np.zeros(len(normal_lines[0].get_data()[1])) @@ -1740,11 +1749,11 @@ def test_line_area_nan_df(self): self.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected1) self.assert_numpy_array_equal(ax.lines[1].get_ydata(), expected1 + expected2) - ax = _check_plot_works(d.plot, kind='area') + ax = _check_plot_works(d.plot.area) self.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected1) self.assert_numpy_array_equal(ax.lines[1].get_ydata(), expected1 + expected2) - ax = _check_plot_works(d.plot, kind='area', stacked=False) + ax = _check_plot_works(d.plot.area, stacked=False) self.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected1) self.assert_numpy_array_equal(ax.lines[1].get_ydata(), expected2) @@ -1778,7 +1787,7 @@ def test_area_lim(self): neg_df = - df for stacked in [True, False]: - ax = _check_plot_works(df.plot, kind='area', stacked=stacked) + ax = _check_plot_works(df.plot.area, stacked=stacked) xmin, xmax = ax.get_xlim() ymin, ymax = ax.get_ylim() lines = ax.get_lines() @@ -1786,7 +1795,7 @@ def test_area_lim(self): self.assertEqual(xmax, lines[0].get_data()[0][-1]) self.assertEqual(ymin, 0) - ax = _check_plot_works(neg_df.plot, kind='area', stacked=stacked) + ax = _check_plot_works(neg_df.plot.area, stacked=stacked) ymin, ymax = ax.get_ylim() self.assertEqual(ymax, 0) @@ -1797,29 +1806,29 @@ def test_bar_colors(self): default_colors = plt.rcParams.get('axes.color_cycle') df = DataFrame(randn(5, 5)) - ax = df.plot(kind='bar') + ax = df.plot.bar() self._check_colors(ax.patches[::5], facecolors=default_colors[:5]) tm.close() custom_colors = 'rgcby' - ax = df.plot(kind='bar', color=custom_colors) + ax = df.plot.bar(color=custom_colors) self._check_colors(ax.patches[::5], facecolors=custom_colors) tm.close() from matplotlib import cm # Test str -> colormap functionality - ax = df.plot(kind='bar', colormap='jet') + ax = df.plot.bar(colormap='jet') rgba_colors = lmap(cm.jet, np.linspace(0, 1, 5)) self._check_colors(ax.patches[::5], facecolors=rgba_colors) tm.close() # Test colormap functionality - ax = df.plot(kind='bar', colormap=cm.jet) + ax = df.plot.bar(colormap=cm.jet) rgba_colors = lmap(cm.jet, np.linspace(0, 1, 5)) self._check_colors(ax.patches[::5], facecolors=rgba_colors) tm.close() - ax = df.ix[:, [0]].plot(kind='bar', color='DodgerBlue') + ax = df.ix[:, [0]].plot.bar(color='DodgerBlue') self._check_colors([ax.patches[0]], facecolors=['DodgerBlue']) tm.close() @@ -1832,17 +1841,17 @@ def test_bar_linewidth(self): df = DataFrame(randn(5, 5)) # regular - ax = df.plot(kind='bar', linewidth=2) + ax = df.plot.bar(linewidth=2) for r in ax.patches: self.assertEqual(r.get_linewidth(), 2) # stacked - ax = df.plot(kind='bar', stacked=True, linewidth=2) + ax = df.plot.bar(stacked=True, linewidth=2) for r in ax.patches: self.assertEqual(r.get_linewidth(), 2) # subplots - axes = df.plot(kind='bar', linewidth=2, subplots=True) + axes = df.plot.bar(linewidth=2, subplots=True) self._check_axes_shape(axes, axes_num=5, layout=(5, 1)) for ax in axes: for r in ax.patches: @@ -1855,33 +1864,33 @@ def test_bar_barwidth(self): width = 0.9 # regular - ax = df.plot(kind='bar', width=width) + ax = df.plot.bar(width=width) for r in ax.patches: self.assertEqual(r.get_width(), width / len(df.columns)) # stacked - ax = df.plot(kind='bar', stacked=True, width=width) + ax = df.plot.bar(stacked=True, width=width) for r in ax.patches: self.assertEqual(r.get_width(), width) # horizontal regular - ax = df.plot(kind='barh', width=width) + ax = df.plot.barh(width=width) for r in ax.patches: self.assertEqual(r.get_height(), width / len(df.columns)) # horizontal stacked - ax = df.plot(kind='barh', stacked=True, width=width) + ax = df.plot.barh(stacked=True, width=width) for r in ax.patches: self.assertEqual(r.get_height(), width) # subplots - axes = df.plot(kind='bar', width=width, subplots=True) + axes = df.plot.bar(width=width, subplots=True) for ax in axes: for r in ax.patches: self.assertEqual(r.get_width(), width) # horizontal subplots - axes = df.plot(kind='barh', width=width, subplots=True) + axes = df.plot.barh(width=width, subplots=True) for ax in axes: for r in ax.patches: self.assertEqual(r.get_height(), width) @@ -1899,28 +1908,28 @@ def test_bar_barwidth_position(self): @slow def test_bar_bottom_left(self): df = DataFrame(rand(5, 5)) - ax = df.plot(kind='bar', stacked=False, bottom=1) + ax = df.plot.bar(stacked=False, bottom=1) result = [p.get_y() for p in ax.patches] self.assertEqual(result, [1] * 25) - ax = df.plot(kind='bar', stacked=True, bottom=[-1, -2, -3, -4, -5]) + ax = df.plot.bar(stacked=True, bottom=[-1, -2, -3, -4, -5]) result = [p.get_y() for p in ax.patches[:5]] self.assertEqual(result, [-1, -2, -3, -4, -5]) - ax = df.plot(kind='barh', stacked=False, left=np.array([1, 1, 1, 1, 1])) + ax = df.plot.barh(stacked=False, left=np.array([1, 1, 1, 1, 1])) result = [p.get_x() for p in ax.patches] self.assertEqual(result, [1] * 25) - ax = df.plot(kind='barh', stacked=True, left=[1, 2, 3, 4, 5]) + ax = df.plot.barh(stacked=True, left=[1, 2, 3, 4, 5]) result = [p.get_x() for p in ax.patches[:5]] self.assertEqual(result, [1, 2, 3, 4, 5]) - axes = df.plot(kind='bar', subplots=True, bottom=-1) + axes = df.plot.bar(subplots=True, bottom=-1) for ax in axes: result = [p.get_y() for p in ax.patches] self.assertEqual(result, [-1] * 5) - axes = df.plot(kind='barh', subplots=True, left=np.array([1, 1, 1, 1, 1])) + axes = df.plot.barh(subplots=True, left=np.array([1, 1, 1, 1, 1])) for ax in axes: result = [p.get_x() for p in ax.patches] self.assertEqual(result, [1] * 5) @@ -1929,12 +1938,12 @@ def test_bar_bottom_left(self): def test_bar_nan(self): df = DataFrame({'A': [10, np.nan, 20], 'B': [5, 10, 20], 'C': [1, 2, 3]}) - ax = df.plot(kind='bar') + ax = df.plot.bar() expected = [10, 0, 20, 5, 10, 20, 1, 2, 3] result = [p.get_height() for p in ax.patches] self.assertEqual(result, expected) - ax = df.plot(kind='bar', stacked=True) + ax = df.plot.bar(stacked=True) result = [p.get_height() for p in ax.patches] self.assertEqual(result, expected) @@ -1948,13 +1957,13 @@ def test_plot_scatter(self): index=list(string.ascii_letters[:6]), columns=['x', 'y', 'z', 'four']) - _check_plot_works(df.plot, x='x', y='y', kind='scatter') - _check_plot_works(df.plot, x=1, y=2, kind='scatter') + _check_plot_works(df.plot.scatter, x='x', y='y') + _check_plot_works(df.plot.scatter, x=1, y=2) - with tm.assertRaises(ValueError): - df.plot(x='x', kind='scatter') - with tm.assertRaises(ValueError): - df.plot(y='y', kind='scatter') + with tm.assertRaises(TypeError): + df.plot.scatter(x='x') + with tm.assertRaises(TypeError): + df.plot.scatter(y='y') # GH 6951 axes = df.plot(x='x', y='y', kind='scatter', subplots=True) @@ -1966,8 +1975,8 @@ def test_plot_scatter_with_c(self): index=list(string.ascii_letters[:6]), columns=['x', 'y', 'z', 'four']) - axes = [df.plot(kind='scatter', x='x', y='y', c='z'), - df.plot(kind='scatter', x=0, y=1, c=2)] + 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 self.assertEqual(ax.collections[0].cmap.name, 'Greys') @@ -1979,15 +1988,15 @@ def test_plot_scatter_with_c(self): self.assertEqual(ax.collections[0].colorbar._label, 'z') cm = 'cubehelix' - ax = df.plot(kind='scatter', x='x', y='y', c='z', colormap=cm) + ax = df.plot.scatter(x='x', y='y', c='z', colormap=cm) self.assertEqual(ax.collections[0].cmap.name, cm) # verify turning off colorbar works - ax = df.plot(kind='scatter', x='x', y='y', c='z', colorbar=False) + ax = df.plot.scatter(x='x', y='y', c='z', colorbar=False) self.assertIs(ax.collections[0].colorbar, None) # verify that we can still plot a solid color - ax = df.plot(x=0, y=1, c='red', kind='scatter') + ax = df.plot.scatter(x=0, y=1, c='red') self.assertIs(ax.collections[0].colorbar, None) self._check_colors(ax.collections, facecolors=['r']) @@ -2001,7 +2010,7 @@ def test_plot_scatter_with_c(self): red_rgba = [1.0, 0.0, 0.0, 1.0] green_rgba = [0.0, 1.0, 0.0, 1.0] rgba_array = np.array([red_rgba, green_rgba]) - ax = df.plot(kind='scatter', x='A', y='B', c=rgba_array) + ax = df.plot.scatter(x='A', y='B', c=rgba_array) # expect the face colors of the points in the non-colormap path to be # identical to the values we supplied, normally we'd be on shaky ground # comparing floats for equality but here we expect them to be @@ -2014,7 +2023,7 @@ def test_plot_scatter_with_c(self): # are dependent on the spring colormap, which may change its colors # later. float_array = np.array([0.0, 1.0]) - df.plot(kind='scatter', x='A', y='B', c=float_array, cmap='spring') + df.plot.scatter(x='A', y='B', c=float_array, cmap='spring') @slow def test_plot_bar(self): @@ -2022,27 +2031,27 @@ def test_plot_bar(self): index=list(string.ascii_letters[:6]), columns=['one', 'two', 'three', 'four']) - _check_plot_works(df.plot, kind='bar') - _check_plot_works(df.plot, kind='bar', legend=False) - _check_plot_works(df.plot, kind='bar', subplots=True) - _check_plot_works(df.plot, kind='bar', stacked=True) + _check_plot_works(df.plot.bar) + _check_plot_works(df.plot.bar, legend=False) + _check_plot_works(df.plot.bar, subplots=True) + _check_plot_works(df.plot.bar, stacked=True) df = DataFrame(randn(10, 15), index=list(string.ascii_letters[:10]), columns=lrange(15)) - _check_plot_works(df.plot, kind='bar') + _check_plot_works(df.plot.bar) df = DataFrame({'a': [0, 1], 'b': [1, 0]}) - ax = _check_plot_works(df.plot, kind='bar') + ax = _check_plot_works(df.plot.bar) self._check_ticks_props(ax, xrot=90) - ax = df.plot(kind='bar', rot=35, fontsize=10) + ax = df.plot.bar(rot=35, fontsize=10) self._check_ticks_props(ax, xrot=35, xlabelsize=10, ylabelsize=10) - ax = _check_plot_works(df.plot, kind='barh') + ax = _check_plot_works(df.plot.barh) self._check_ticks_props(ax, yrot=0) - ax = df.plot(kind='barh', rot=55, fontsize=11) + ax = df.plot.barh(rot=55, fontsize=11) self._check_ticks_props(ax, yrot=55, ylabelsize=11, xlabelsize=11) def _check_bar_alignment(self, df, kind='bar', stacked=False, @@ -2175,7 +2184,7 @@ def test_bar_log_no_subplots(self): # no subplots df = DataFrame({'A': [3] * 5, 'B': lrange(1, 6)}, index=lrange(5)) - ax = df.plot(kind='bar', grid=True, log=True) + ax = df.plot.bar(grid=True, log=True) tm.assert_numpy_array_equal(ax.yaxis.get_ticklocs(), expected) @slow @@ -2185,8 +2194,7 @@ def test_bar_log_subplots(self): expected = np.hstack((.1, expected, 1e4)) ax = DataFrame([Series([200, 300]), - Series([300, 500])]).plot(log=True, kind='bar', - subplots=True) + Series([300, 500])]).plot.bar(log=True, subplots=True) tm.assert_numpy_array_equal(ax[0].yaxis.get_ticklocs(), expected) tm.assert_numpy_array_equal(ax[1].yaxis.get_ticklocs(), expected) @@ -2198,7 +2206,7 @@ def test_boxplot(self): numeric_cols = df._get_numeric_data().columns labels = [com.pprint_thing(c) for c in numeric_cols] - ax = _check_plot_works(df.plot, kind='box') + ax = _check_plot_works(df.plot.box) self._check_text_labels(ax.get_xticklabels(), labels) tm.assert_numpy_array_equal(ax.xaxis.get_ticklocs(), np.arange(1, len(numeric_cols) + 1)) @@ -2207,7 +2215,7 @@ def test_boxplot(self): # different warning on py3 if not PY3: - axes = _check_plot_works(df.plot, kind='box', + axes = _check_plot_works(df.plot.box, subplots=True, logy=True) self._check_axes_shape(axes, axes_num=3, layout=(1, 3)) @@ -2216,14 +2224,14 @@ def test_boxplot(self): self._check_text_labels(ax.get_xticklabels(), [label]) self.assertEqual(len(ax.lines), self.bp_n_objects) - axes = series.plot(kind='box', rot=40) + axes = series.plot.box(rot=40) self._check_ticks_props(axes, xrot=40, yrot=0) tm.close() - ax = _check_plot_works(series.plot, kind='box') + ax = _check_plot_works(series.plot.box) positions = np.array([1, 6, 7]) - ax = df.plot(kind='box', positions=positions) + ax = df.plot.box(positions=positions) numeric_cols = df._get_numeric_data().columns labels = [com.pprint_thing(c) for c in numeric_cols] self._check_text_labels(ax.get_xticklabels(), labels) @@ -2237,12 +2245,12 @@ def test_boxplot_vertical(self): labels = [com.pprint_thing(c) for c in numeric_cols] # if horizontal, yticklabels are rotated - ax = df.plot(kind='box', rot=50, fontsize=8, vert=False) + ax = df.plot.box(rot=50, fontsize=8, vert=False) self._check_ticks_props(ax, xrot=0, yrot=50, ylabelsize=8) self._check_text_labels(ax.get_yticklabels(), labels) self.assertEqual(len(ax.lines), self.bp_n_objects * len(numeric_cols)) - axes = _check_plot_works(df.plot, kind='box', subplots=True, + axes = _check_plot_works(df.plot.box, subplots=True, vert=False, logx=True) self._check_axes_shape(axes, axes_num=3, layout=(1, 3)) self._check_ax_scales(axes, xaxis='log') @@ -2251,7 +2259,7 @@ def test_boxplot_vertical(self): self.assertEqual(len(ax.lines), self.bp_n_objects) positions = np.array([3, 2, 8]) - ax = df.plot(kind='box', positions=positions, vert=False) + ax = df.plot.box(positions=positions, vert=False) self._check_text_labels(ax.get_yticklabels(), labels) tm.assert_numpy_array_equal(ax.yaxis.get_ticklocs(), positions) self.assertEqual(len(ax.lines), self.bp_n_objects * len(numeric_cols)) @@ -2262,15 +2270,15 @@ def test_boxplot_return_type(self): index=list(string.ascii_letters[:6]), columns=['one', 'two', 'three', 'four']) with tm.assertRaises(ValueError): - df.plot(kind='box', return_type='NOTATYPE') + df.plot.box(return_type='NOTATYPE') - result = df.plot(kind='box', return_type='dict') + result = df.plot.box(return_type='dict') self._check_box_return_type(result, 'dict') - result = df.plot(kind='box', return_type='axes') + result = df.plot.box(return_type='axes') self._check_box_return_type(result, 'axes') - result = df.plot(kind='box', return_type='both') + result = df.plot.box(return_type='both') self._check_box_return_type(result, 'both') @slow @@ -2278,13 +2286,13 @@ def test_boxplot_subplots_return_type(self): df = self.hist_df # normal style: return_type=None - result = df.plot(kind='box', subplots=True) + result = df.plot.box(subplots=True) self.assertIsInstance(result, np.ndarray) self._check_box_return_type(result, None, expected_keys=['height', 'weight', 'category']) for t in ['dict', 'axes', 'both']: - returned = df.plot(kind='box', return_type=t, subplots=True) + returned = df.plot.box(return_type=t, subplots=True) self._check_box_return_type(returned, t, expected_keys=['height', 'weight', 'category'], check_ax_title=False) @@ -2324,29 +2332,29 @@ def test_hist_df(self): df = DataFrame(randn(100, 4)) series = df[0] - ax = _check_plot_works(df.plot, kind='hist') + ax = _check_plot_works(df.plot.hist) expected = [com.pprint_thing(c) for c in df.columns] self._check_legend_labels(ax, labels=expected) - axes = _check_plot_works(df.plot, kind='hist', subplots=True, logy=True) + axes = _check_plot_works(df.plot.hist, subplots=True, logy=True) self._check_axes_shape(axes, axes_num=4, layout=(4, 1)) self._check_ax_scales(axes, yaxis='log') - axes = series.plot(kind='hist', rot=40) + axes = series.plot.hist(rot=40) self._check_ticks_props(axes, xrot=40, yrot=0) tm.close() - ax = series.plot(kind='hist', normed=True, cumulative=True, bins=4) + ax = series.plot.hist(normed=True, cumulative=True, bins=4) # height of last bin (index 5) must be 1.0 self.assertAlmostEqual(ax.get_children()[5].get_height(), 1.0) tm.close() - ax = series.plot(kind='hist', cumulative=True, bins=4) + ax = series.plot.hist(cumulative=True, bins=4) self.assertAlmostEqual(ax.get_children()[5].get_height(), 100.0) tm.close() # if horizontal, yticklabels are rotated - axes = df.plot(kind='hist', rot=50, fontsize=8, orientation='horizontal') + axes = df.plot.hist(rot=50, fontsize=8, orientation='horizontal') self._check_ticks_props(axes, xrot=0, yrot=50, ylabelsize=8) def _check_box_coord(self, patches, expected_y=None, expected_h=None, @@ -2384,7 +2392,7 @@ def test_hist_df_coord(self): columns=['A', 'B', 'C']) for df in [normal_df, nan_df]: - ax = df.plot(kind='hist', bins=5) + 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]), @@ -2392,7 +2400,7 @@ def test_hist_df_coord(self): 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(kind='hist', bins=5, stacked=True) + 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]), @@ -2400,7 +2408,7 @@ def test_hist_df_coord(self): 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(kind='hist', bins=5, stacked=True, subplots=True) + 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]), @@ -2411,7 +2419,7 @@ def test_hist_df_coord(self): if self.mpl_ge_1_3_1: # horizontal - ax = df.plot(kind='hist', bins=5, orientation='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]), @@ -2419,7 +2427,7 @@ def test_hist_df_coord(self): 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(kind='hist', bins=5, stacked=True, orientation='horizontal') + 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]), @@ -2427,7 +2435,7 @@ def test_hist_df_coord(self): 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(kind='hist', bins=5, stacked=True, + 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])) @@ -2487,12 +2495,12 @@ def test_df_legend_labels(self): self._check_legend_labels(ax, labels=['a', 'b (right)', 'c', 'g', 'h', 'i']) # scatter - ax = df.plot(kind='scatter', x='a', y='b', label='data1') + ax = df.plot.scatter(x='a', y='b', label='data1') self._check_legend_labels(ax, labels=['data1']) - ax = df2.plot(kind='scatter', x='d', y='e', legend=False, - label='data2', ax=ax) + ax = df2.plot.scatter(x='d', y='e', legend=False, + label='data2', ax=ax) self._check_legend_labels(ax, labels=['data1']) - ax = df3.plot(kind='scatter', x='g', y='h', label='data3', ax=ax) + ax = df3.plot.scatter(x='g', y='h', label='data3', ax=ax) self._check_legend_labels(ax, labels=['data1', 'data3']) # ensure label args pass through and @@ -2683,7 +2691,7 @@ def test_area_colors(self): custom_colors = 'rgcby' df = DataFrame(rand(5, 5)) - ax = df.plot(kind='area', color=custom_colors) + ax = df.plot.area(color=custom_colors) self._check_colors(ax.get_lines(), linecolors=custom_colors) poly = [o for o in ax.get_children() if isinstance(o, PolyCollection)] self._check_colors(poly, facecolors=custom_colors) @@ -2695,7 +2703,7 @@ def test_area_colors(self): self.assertTrue(h.get_alpha() is None) tm.close() - ax = df.plot(kind='area', colormap='jet') + ax = df.plot.area(colormap='jet') jet_colors = lmap(cm.jet, np.linspace(0, 1, len(df))) self._check_colors(ax.get_lines(), linecolors=jet_colors) poly = [o for o in ax.get_children() if isinstance(o, PolyCollection)] @@ -2708,7 +2716,7 @@ def test_area_colors(self): tm.close() # When stacked=True, alpha is set to 0.5 - ax = df.plot(kind='area', colormap=cm.jet, stacked=False) + ax = df.plot.area(colormap=cm.jet, stacked=False) self._check_colors(ax.get_lines(), linecolors=jet_colors) poly = [o for o in ax.get_children() if isinstance(o, PolyCollection)] jet_with_alpha = [(c[0], c[1], c[2], 0.5) for c in jet_colors] @@ -2725,29 +2733,29 @@ def test_hist_colors(self): default_colors = self.plt.rcParams.get('axes.color_cycle') df = DataFrame(randn(5, 5)) - ax = df.plot(kind='hist') + ax = df.plot.hist() self._check_colors(ax.patches[::10], facecolors=default_colors[:5]) tm.close() custom_colors = 'rgcby' - ax = df.plot(kind='hist', color=custom_colors) + ax = df.plot.hist( color=custom_colors) self._check_colors(ax.patches[::10], facecolors=custom_colors) tm.close() from matplotlib import cm # Test str -> colormap functionality - ax = df.plot(kind='hist', colormap='jet') + ax = df.plot.hist( colormap='jet') rgba_colors = lmap(cm.jet, np.linspace(0, 1, 5)) self._check_colors(ax.patches[::10], facecolors=rgba_colors) tm.close() # Test colormap functionality - ax = df.plot(kind='hist', colormap=cm.jet) + ax = df.plot.hist( colormap=cm.jet) rgba_colors = lmap(cm.jet, np.linspace(0, 1, 5)) self._check_colors(ax.patches[::10], facecolors=rgba_colors) tm.close() - ax = df.ix[:, [0]].plot(kind='hist', color='DodgerBlue') + ax = df.ix[:, [0]].plot.hist(color='DodgerBlue') self._check_colors([ax.patches[0]], facecolors=['DodgerBlue']) ax = df.plot(kind='hist', color='green') @@ -2764,16 +2772,16 @@ def test_kde_colors(self): custom_colors = 'rgcby' df = DataFrame(rand(5, 5)) - ax = df.plot(kind='kde', color=custom_colors) + ax = df.plot.kde(color=custom_colors) self._check_colors(ax.get_lines(), linecolors=custom_colors) tm.close() - ax = df.plot(kind='kde', colormap='jet') + ax = df.plot.kde(colormap='jet') rgba_colors = lmap(cm.jet, np.linspace(0, 1, len(df))) self._check_colors(ax.get_lines(), linecolors=rgba_colors) tm.close() - ax = df.plot(kind='kde', colormap=cm.jet) + ax = df.plot.kde(colormap=cm.jet) rgba_colors = lmap(cm.jet, np.linspace(0, 1, len(df))) self._check_colors(ax.get_lines(), linecolors=rgba_colors) @@ -2848,47 +2856,47 @@ def _check_colors(bp, box_c, whiskers_c, medians_c, caps_c='k', fliers_c='b'): default_colors = self.plt.rcParams.get('axes.color_cycle') df = DataFrame(randn(5, 5)) - bp = df.plot(kind='box', return_type='dict') + bp = df.plot.box(return_type='dict') _check_colors(bp, default_colors[0], default_colors[0], default_colors[2]) tm.close() dict_colors = dict(boxes='#572923', whiskers='#982042', medians='#804823', caps='#123456') - bp = df.plot(kind='box', color=dict_colors, sym='r+', return_type='dict') + bp = df.plot.box(color=dict_colors, sym='r+', return_type='dict') _check_colors(bp, dict_colors['boxes'], dict_colors['whiskers'], dict_colors['medians'], dict_colors['caps'], 'r') tm.close() # partial colors dict_colors = dict(whiskers='c', medians='m') - bp = df.plot(kind='box', color=dict_colors, return_type='dict') + bp = df.plot.box(color=dict_colors, return_type='dict') _check_colors(bp, default_colors[0], 'c', 'm') tm.close() from matplotlib import cm # Test str -> colormap functionality - bp = df.plot(kind='box', colormap='jet', return_type='dict') + bp = df.plot.box(colormap='jet', return_type='dict') jet_colors = lmap(cm.jet, np.linspace(0, 1, 3)) _check_colors(bp, jet_colors[0], jet_colors[0], jet_colors[2]) tm.close() # Test colormap functionality - bp = df.plot(kind='box', colormap=cm.jet, return_type='dict') + bp = df.plot.box(colormap=cm.jet, return_type='dict') _check_colors(bp, jet_colors[0], jet_colors[0], jet_colors[2]) tm.close() # string color is applied to all artists except fliers - bp = df.plot(kind='box', color='DodgerBlue', return_type='dict') + bp = df.plot.box(color='DodgerBlue', return_type='dict') _check_colors(bp, 'DodgerBlue', 'DodgerBlue', 'DodgerBlue', 'DodgerBlue') # tuple is also applied to all artists except fliers - bp = df.plot(kind='box', color=(0, 1, 0), sym='#123456', return_type='dict') + bp = df.plot.box(color=(0, 1, 0), sym='#123456', return_type='dict') _check_colors(bp, (0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), '#123456') with tm.assertRaises(ValueError): # Color contains invalid key results in ValueError - df.plot(kind='box', color=dict(boxes='red', xxxx='blue')) + df.plot.box(color=dict(boxes='red', xxxx='blue')) def test_default_color_cycle(self): import matplotlib.pyplot as plt @@ -2912,6 +2920,17 @@ def test_unordered_ts(self): ydata = ax.lines[0].get_ydata() tm.assert_numpy_array_equal(ydata, np.array([1.0, 2.0, 3.0])) + def test_kind_both_ways(self): + df = DataFrame({'x': [1, 2, 3]}) + for kind in plotting._common_kinds: + if not _ok_for_gaussian_kde(kind): + continue + 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') + def test_all_invalid_plot_data(self): df = DataFrame(list('abcd')) for kind in plotting._common_kinds: @@ -2949,12 +2968,12 @@ def test_invalid_kind(self): def test_hexbin_basic(self): df = self.hexbin_df - ax = df.plot(kind='hexbin', x='A', y='B', gridsize=10) + ax = df.plot.hexbin(x='A', y='B', gridsize=10) # TODO: need better way to test. This just does existence. self.assertEqual(len(ax.collections), 1) # GH 6951 - axes = df.plot(x='A', y='B', kind='hexbin', subplots=True) + axes = df.plot.hexbin(x='A', y='B', subplots=True) # hexbin should have 2 axes in the figure, 1 for plotting and another is colorbar self.assertEqual(len(axes[0].figure.axes), 2) # return value is single axes @@ -2964,11 +2983,10 @@ def test_hexbin_basic(self): def test_hexbin_with_c(self): df = self.hexbin_df - ax = df.plot(kind='hexbin', x='A', y='B', C='C') + ax = df.plot.hexbin(x='A', y='B', C='C') self.assertEqual(len(ax.collections), 1) - ax = df.plot(kind='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=np.std) self.assertEqual(len(ax.collections), 1) @slow @@ -2976,45 +2994,45 @@ def test_hexbin_cmap(self): df = self.hexbin_df # Default to BuGn - ax = df.plot(kind='hexbin', x='A', y='B') + ax = df.plot.hexbin(x='A', y='B') self.assertEqual(ax.collections[0].cmap.name, 'BuGn') cm = 'cubehelix' - ax = df.plot(kind='hexbin', x='A', y='B', colormap=cm) + ax = df.plot.hexbin(x='A', y='B', colormap=cm) self.assertEqual(ax.collections[0].cmap.name, cm) @slow def test_no_color_bar(self): df = self.hexbin_df - ax = df.plot(kind='hexbin', x='A', y='B', colorbar=None) + ax = df.plot.hexbin(x='A', y='B', colorbar=None) self.assertIs(ax.collections[0].colorbar, None) @slow def test_allow_cmap(self): df = self.hexbin_df - ax = df.plot(kind='hexbin', x='A', y='B', cmap='YlGn') + ax = df.plot.hexbin(x='A', y='B', cmap='YlGn') self.assertEqual(ax.collections[0].cmap.name, 'YlGn') with tm.assertRaises(TypeError): - df.plot(kind='hexbin', x='A', y='B', cmap='YlGn', - colormap='BuGn') + df.plot.hexbin(x='A', y='B', cmap='YlGn', + colormap='BuGn') @slow def test_pie_df(self): df = DataFrame(np.random.rand(5, 3), columns=['X', 'Y', 'Z'], index=['a', 'b', 'c', 'd', 'e']) with tm.assertRaises(ValueError): - df.plot(kind='pie') + df.plot.pie() - ax = _check_plot_works(df.plot, kind='pie', y='Y') + ax = _check_plot_works(df.plot.pie, y='Y') self._check_text_labels(ax.texts, df.index) - ax = _check_plot_works(df.plot, kind='pie', y=2) + ax = _check_plot_works(df.plot.pie, y=2) self._check_text_labels(ax.texts, df.index) - axes = _check_plot_works(df.plot, kind='pie', subplots=True) + axes = _check_plot_works(df.plot.pie, subplots=True) self.assertEqual(len(axes), len(df.columns)) for ax in axes: self._check_text_labels(ax.texts, df.index) @@ -3023,7 +3041,7 @@ def test_pie_df(self): labels = ['A', 'B', 'C', 'D', 'E'] color_args = ['r', 'g', 'b', 'c', 'm'] - axes = _check_plot_works(df.plot, kind='pie', subplots=True, + axes = _check_plot_works(df.plot.pie, subplots=True, labels=labels, colors=color_args) self.assertEqual(len(axes), len(df.columns)) @@ -3036,7 +3054,7 @@ def test_pie_df_nan(self): for i in range(4): df.iloc[i, i] = np.nan fig, axes = self.plt.subplots(ncols=4) - df.plot(kind='pie', subplots=True, ax=axes, legend=True) + df.plot.pie(subplots=True, ax=axes, legend=True) base_expected = ['0', '1', '2', '3'] for i, ax in enumerate(axes): @@ -3206,14 +3224,14 @@ def test_errorbar_scatter(self): df_err = DataFrame(np.random.randn(5, 2) / 5, index=range(5), columns=['x', 'y']) - ax = _check_plot_works(df.plot, kind='scatter', x='x', y='y') + ax = _check_plot_works(df.plot.scatter, x='x', y='y') self._check_has_errorbars(ax, xerr=0, yerr=0) - ax = _check_plot_works(df.plot, kind='scatter', x='x', y='y', xerr=df_err) + ax = _check_plot_works(df.plot.scatter, x='x', y='y', xerr=df_err) self._check_has_errorbars(ax, xerr=1, yerr=0) - ax = _check_plot_works(df.plot, kind='scatter', x='x', y='y', yerr=df_err) + ax = _check_plot_works(df.plot.scatter, x='x', y='y', yerr=df_err) self._check_has_errorbars(ax, xerr=0, yerr=1) - ax = _check_plot_works(df.plot, kind='scatter', x='x', y='y', + ax = _check_plot_works(df.plot.scatter, x='x', y='y', xerr=df_err, yerr=df_err) self._check_has_errorbars(ax, xerr=1, yerr=1) @@ -3223,12 +3241,12 @@ def _check_errorbar_color(containers, expected, has_err='has_xerr'): # GH 8081 df = DataFrame(np.random.randn(10, 5), columns=['a', 'b', 'c', 'd', 'e']) - ax = df.plot(kind='scatter', x='a', y='b', xerr='d', yerr='e', c='red') + ax = df.plot.scatter(x='a', y='b', xerr='d', yerr='e', c='red') self._check_has_errorbars(ax, xerr=1, yerr=1) _check_errorbar_color(ax.containers, 'red', has_err='has_xerr') _check_errorbar_color(ax.containers, 'red', has_err='has_yerr') - ax = df.plot(kind='scatter', x='a', y='b', yerr='e', color='green') + ax = df.plot.scatter(x='a', y='b', yerr='e', color='green') self._check_has_errorbars(ax, xerr=0, yerr=1) _check_errorbar_color(ax.containers, 'green', has_err='has_yerr') @@ -3586,6 +3604,15 @@ def test_hist_single_row(self): df = DataFrame({"Name": ["AAA"], "ByCol": [1], "Mark": [85]}) df["Mark"].hist(by=df["ByCol"], bins=bins) + 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() + df.groupby('z')['x'].plot.line() + tm.close() + def assert_is_valid_plot_return_object(objs): import matplotlib.pyplot as plt diff --git a/pandas/tools/plotting.py b/pandas/tools/plotting.py index 9eab385a7a2a5..3337a978961c4 100644 --- a/pandas/tools/plotting.py +++ b/pandas/tools/plotting.py @@ -11,6 +11,7 @@ import numpy as np from pandas.util.decorators import cache_readonly, deprecate_kwarg +from pandas.core.base import PandasObject import pandas.core.common as com from pandas.core.common import AbstractMethodError from pandas.core.generic import _shared_docs, _shared_doc_kwargs @@ -1462,8 +1463,12 @@ def _post_plot_logic(self, ax, data): class ScatterPlot(PlanePlot): _kind = 'scatter' - def __init__(self, data, x, y, c=None, **kwargs): - super(ScatterPlot, self).__init__(data, x, y, **kwargs) + def __init__(self, data, x, y, s=None, c=None, **kwargs): + if s is None: + # hide the matplotlib default for size, in case we want to change + # the handling of this argument later + s = 20 + super(ScatterPlot, self).__init__(data, x, y, s=s, **kwargs) if com.is_integer(c) and not self.data.columns.holds_integer(): c = self.data.columns[c] self.c = c @@ -2367,10 +2372,12 @@ def _plot(data, x=None, y=None, subplots=False, a single number (e.g. `mean`, `max`, `sum`, `std`).""" series_note = "" -_shared_doc_df_kwargs = dict(klass='DataFrame', klass_kind=df_kind, - klass_coord=df_coord, klass_ax=df_ax, - klass_unique=df_unique, klass_note=df_note) -_shared_doc_series_kwargs = dict(klass='Series', klass_kind=series_kind, +_shared_doc_df_kwargs = dict(klass='DataFrame', klass_obj='df', + klass_kind=df_kind, klass_coord=df_coord, + klass_ax=df_ax, klass_unique=df_unique, + klass_note=df_note) +_shared_doc_series_kwargs = dict(klass='Series', klass_obj='s', + klass_kind=series_kind, klass_coord=series_coord, klass_ax=series_ax, klass_unique=series_unique, klass_note=series_note) @@ -2378,6 +2385,11 @@ def _plot(data, x=None, y=None, subplots=False, _shared_docs['plot'] = """ Make plots of %(klass)s using matplotlib / pylab. + *New in version 0.17.0:* Each plot kind has a corresponding method on the + ``%(klass)s.plot`` accessor: + ``%(klass_obj)s.plot(kind='line')`` is equivalent to + ``%(klass_obj)s.plot.line()``. + Parameters ---------- data : %(klass)s @@ -2460,6 +2472,7 @@ def _plot(data, x=None, y=None, subplots=False, """ + @Appender(_shared_docs['plot'] % _shared_doc_df_kwargs) def plot_frame(data, x=None, y=None, kind='line', ax=None, # Dataframe unique subplots=False, sharex=None, sharey=False, layout=None, # Dataframe unique @@ -3384,6 +3397,428 @@ def _set_ticks_props(axes, xlabelsize=None, xrot=None, return axes +class BasePlotMethods(PandasObject): + def __init__(self, data): + self._data = data + + def __call__(self, *args, **kwargs): + raise NotImplementedError + + +class SeriesPlotMethods(BasePlotMethods): + """Series plotting accessor and method + + Examples + -------- + >>> s.plot.line() + >>> s.plot.bar() + >>> s.plot.hist() + + Plotting methods can also be accessed by calling the accessor as a method + with the ``kind`` argument: + ``s.plot(kind='line')`` is equivalent to ``s.plot.line()`` + """ + def __call__(self, kind='line', ax=None, # Series unique + figsize=None, use_index=True, title=None, grid=None, + legend=False, style=None, logx=False, logy=False, loglog=False, + xticks=None, yticks=None, xlim=None, ylim=None, + rot=None, fontsize=None, colormap=None, table=False, + yerr=None, xerr=None, + label=None, secondary_y=False, # Series unique + **kwds): + return plot_series(self._data, kind=kind, ax=ax, figsize=figsize, + use_index=use_index, title=title, grid=grid, + legend=legend, style=style, logx=logx, logy=logy, + loglog=loglog, xticks=xticks, yticks=yticks, + xlim=xlim, ylim=ylim, rot=rot, fontsize=fontsize, + colormap=colormap, table=table, yerr=yerr, + xerr=xerr, label=label, secondary_y=secondary_y, + **kwds) + __call__.__doc__ = plot_series.__doc__ + + def line(self, **kwds): + """ + Line plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.Series.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='line', **kwds) + + def bar(self, **kwds): + """ + Vertical bar plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.Series.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='bar', **kwds) + + def barh(self, **kwds): + """ + Horizontal bar plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.Series.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='barh', **kwds) + + def box(self, **kwds): + """ + Boxplot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.Series.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='box', **kwds) + + def hist(self, bins=10, **kwds): + """ + Histogram + + .. versionadded:: 0.17.0 + + Parameters + ---------- + bins: integer, default 10 + Number of histogram bins to be used + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.Series.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='hist', bins=bins, **kwds) + + def kde(self, **kwds): + """ + Kernel Density Estimate plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.Series.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='kde', **kwds) + + density = kde + + def area(self, **kwds): + """ + Area plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.Series.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='area', **kwds) + + def pie(self, **kwds): + """ + Pie chart + + .. versionadded:: 0.17.0 + + Parameters + ---------- + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.Series.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='pie', **kwds) + + +class FramePlotMethods(BasePlotMethods): + """DataFrame plotting accessor and method + + Examples + -------- + >>> df.plot.line() + >>> df.plot.scatter('x', 'y') + >>> df.plot.hexbin() + + These plotting methods can also be accessed by calling the accessor as a + method with the ``kind`` argument: + ``df.plot(kind='line')`` is equivalent to ``df.plot.line()`` + """ + def __call__(self, x=None, y=None, kind='line', ax=None, # Dataframe unique + subplots=False, sharex=None, sharey=False, layout=None, # Dataframe unique + figsize=None, use_index=True, title=None, grid=None, + legend=True, style=None, logx=False, logy=False, loglog=False, + xticks=None, yticks=None, xlim=None, ylim=None, + rot=None, fontsize=None, colormap=None, table=False, + yerr=None, xerr=None, + secondary_y=False, sort_columns=False, # Dataframe unique + **kwds): + return plot_frame(self._data, kind=kind, x=x, y=y, ax=ax, + subplots=subplots, sharex=sharex, sharey=sharey, + layout=layout, figsize=figsize, use_index=use_index, + title=title, grid=grid, legend=legend, style=style, + logx=logx, logy=logy, loglog=loglog, xticks=xticks, + yticks=yticks, xlim=xlim, ylim=ylim, rot=rot, + fontsize=fontsize, colormap=colormap, table=table, + yerr=yerr, xerr=xerr, secondary_y=secondary_y, + sort_columns=sort_columns, **kwds) + __call__.__doc__ = plot_frame.__doc__ + + def line(self, x=None, y=None, **kwds): + """ + Line plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + x, y : label or position, optional + Coordinates for each point. + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.DataFrame.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='line', x=x, y=y, **kwds) + + def bar(self, x=None, y=None, **kwds): + """ + Vertical bar plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + x, y : label or position, optional + Coordinates for each point. + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.DataFrame.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='bar', x=x, y=y, **kwds) + + def barh(self, x=None, y=None, **kwds): + """ + Horizontal bar plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + x, y : label or position, optional + Coordinates for each point. + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.DataFrame.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='barh', x=x, y=y, **kwds) + + def box(self, by=None, **kwds): + """ + Boxplot + + .. versionadded:: 0.17.0 + + Parameters + --------- + by : string or sequence + Column in the DataFrame to group by. + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.DataFrame.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='box', by=by, **kwds) + + def hist(self, by=None, bins=10, **kwds): + """ + Histogram + + .. versionadded:: 0.17.0 + + Parameters + ---------- + by : string or sequence + Column in the DataFrame to group by. + bins: integer, default 10 + Number of histogram bins to be used + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.DataFrame.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='hist', by=by, bins=bins, **kwds) + + def kde(self, **kwds): + """ + Kernel Density Estimate plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.DataFrame.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='kde', **kwds) + + density = kde + + def area(self, x=None, y=None, **kwds): + """ + Area plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + x, y : label or position, optional + Coordinates for each point. + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.DataFrame.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='area', x=x, y=y, **kwds) + + def pie(self, y=None, **kwds): + """ + Pie chart + + .. versionadded:: 0.17.0 + + Parameters + ---------- + y : label or position, optional + Column to plot. + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.DataFrame.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='pie', y=y, **kwds) + + def scatter(self, x, y, s=None, c=None, **kwds): + """ + Scatter plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + x, y : label or position, optional + Coordinates for each point. + s : scalar or array_like, optional + Size of each point. + c : label or position, optional + Color of each point. + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.DataFrame.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + return self(kind='scatter', x=x, y=y, c=c, s=s, **kwds) + + def hexbin(self, x, y, C=None, reduce_C_function=None, gridsize=None, + **kwds): + """ + Hexbin plot + + .. versionadded:: 0.17.0 + + Parameters + ---------- + x, y : label or position, optional + Coordinates for each point. + C : label or position, optional + The value at each `(x, y)` point. + reduce_C_function : callable, optional + Function of one argument that reduces all the values in a bin to + a single number (e.g. `mean`, `max`, `sum`, `std`). + gridsize : int, optional + Number of bins. + **kwds : optional + Keyword arguments to pass on to :py:meth:`pandas.DataFrame.plot`. + + Returns + ------- + axes : matplotlib.AxesSubplot or np.array of them + """ + if reduce_C_function is not None: + kwds['reduce_C_function'] = reduce_C_function + if gridsize is not None: + kwds['gridsize'] = gridsize + return self(kind='hexbin', x=x, y=y, C=C, **kwds) + + if __name__ == '__main__': # import pandas.rpy.common as com # sales = com.load_data('sanfrancisco.home.sales', package='nutshell')