Skip to content

ENH: plot method accessors #9321

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 11, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/_templates/autosummary/accessor_callable.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{{ fullname }}
{{ underline }}

.. currentmodule:: {{ module.split('.')[0] }}

.. autoaccessorcallable:: {{ [module.split('.')[1], objname]|join('.') }}.__call__
53 changes: 51 additions & 2 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.<kind>``.

.. 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::
Expand Down Expand Up @@ -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.<kind>``.

.. 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
43 changes: 42 additions & 1 deletion doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand All @@ -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)
18 changes: 16 additions & 2 deletions doc/source/visualization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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' <visualization.barplot>` or :ref:`'barh' <visualization.barplot>` for bar plots
Expand All @@ -134,6 +135,19 @@ These include:
* :ref:`'hexbin' <visualization.hexbin>` for hexagonal bin plots
* :ref:`'pie' <visualization.pie>` for pie plots

.. versionadded:: 0.17

You can also create these other plots using the methods ``DataFrame.plot.<kind>`` 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.<TAB>
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() <visualization.hist>`,
and :ref:`DataFrame.boxplot() <visualization.box>` methods, which use a separate interface.

Expand Down
31 changes: 30 additions & 1 deletion doc/source/whatsnew/v0.17.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ users upgrade to this version.
Highlights include:

- Release the Global Interpreter Lock (GIL) on some cython operations, see :ref:`here <whatsnew_0170.gil>`
- Plotting methods are now available as attributes of the ``.plot`` accessor, see :ref:`here <whatsnew_0170.plot>`
- The sorting API has been revamped to remove some long-time inconsistencies, see :ref:`here <whatsnew_0170.api_breaking.sorting>`
- Support for a ``datetime64[ns]`` with timezones as a first-class dtype, see :ref:`here <whatsnew_0170.tz>`
- The default for ``to_datetime`` will now be to ``raise`` when presented with unparseable formats,
Expand Down Expand Up @@ -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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add to the highlites a pointer to this section.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


Plot submethods
^^^^^^^^^^^^^^^

The Series and DataFrame ``.plot()`` method allows for customizing :ref:`plot types<visualization.other>` 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=<kind>, ...)``, you can now also use ``series.plot.<kind>(...)``:

.. 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.<TAB>
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
Expand Down Expand Up @@ -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``.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couple of examples in a code-block maybe? (and maybe an ipython tab completion example)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are good ideas, I'll update, and probably add tab-completion to the plotting docs -- I had forgotten about that.


.. _whatsnew_0170.enhancements.other:

Other enhancements
Expand Down
7 changes: 4 additions & 3 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, maybe we should deprecate DataFrame.hist (and point to DataFrame.plot.hist) as the canonical way?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defaults on DataFrame.hist are different thanDataFrame.plot.hist which I think is why we have both for now. I could go either way on deprecating DataFrame.hist.



Expand Down
24 changes: 24 additions & 0 deletions pandas/core/groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

"""
Expand Down Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deprecate .hist ? (see above)

# Add arithmetic!
Expand Down
Loading