Skip to content

Commit 52f4b75

Browse files
committed
Merge pull request #9321 from shoyer/PlotMethods
ENH: plot method accessors
2 parents c266dc3 + 9a65bd5 commit 52f4b75

File tree

10 files changed

+816
-190
lines changed

10 files changed

+816
-190
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{{ fullname }}
2+
{{ underline }}
3+
4+
.. currentmodule:: {{ module.split('.')[0] }}
5+
6+
.. autoaccessorcallable:: {{ [module.split('.')[1], objname]|join('.') }}.__call__

doc/source/api.rst

+51-2
Original file line numberDiff line numberDiff line change
@@ -672,12 +672,34 @@ the Categorical back to a numpy array, so levels and order information is not pr
672672
Plotting
673673
~~~~~~~~
674674

675+
``Series.plot`` is both a callable method and a namespace attribute for
676+
specific plotting methods of the form ``Series.plot.<kind>``.
677+
675678
.. autosummary::
676679
:toctree: generated/
680+
:template: autosummary/accessor_callable.rst
677681

678-
Series.hist
679682
Series.plot
680683

684+
.. autosummary::
685+
:toctree: generated/
686+
:template: autosummary/accessor_method.rst
687+
688+
Series.plot.area
689+
Series.plot.bar
690+
Series.plot.barh
691+
Series.plot.box
692+
Series.plot.density
693+
Series.plot.hist
694+
Series.plot.kde
695+
Series.plot.line
696+
Series.plot.pie
697+
698+
.. autosummary::
699+
:toctree: generated/
700+
701+
Series.hist
702+
681703
Serialization / IO / Conversion
682704
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
683705
.. autosummary::
@@ -946,14 +968,41 @@ Time series-related
946968
DataFrame.tz_convert
947969
DataFrame.tz_localize
948970

971+
.. _api.dataframe.plotting:
972+
949973
Plotting
950974
~~~~~~~~
975+
976+
``DataFrame.plot`` is both a callable method and a namespace attribute for
977+
specific plotting methods of the form ``DataFrame.plot.<kind>``.
978+
979+
.. autosummary::
980+
:toctree: generated/
981+
:template: autosummary/accessor_plot.rst
982+
983+
DataFrame.plot
984+
985+
.. autosummary::
986+
:toctree: generated/
987+
:template: autosummary/accessor_method.rst
988+
989+
DataFrame.plot.area
990+
DataFrame.plot.bar
991+
DataFrame.plot.barh
992+
DataFrame.plot.box
993+
DataFrame.plot.density
994+
DataFrame.plot.hexbin
995+
DataFrame.plot.hist
996+
DataFrame.plot.kde
997+
DataFrame.plot.line
998+
DataFrame.plot.pie
999+
DataFrame.plot.scatter
1000+
9511001
.. autosummary::
9521002
:toctree: generated/
9531003

9541004
DataFrame.boxplot
9551005
DataFrame.hist
956-
DataFrame.plot
9571006

9581007
Serialization / IO / Conversion
9591008
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

doc/source/conf.py

+42-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@
214214
# template names.
215215

216216
# Add redirect for previously existing API pages (which are now included in
217-
# the API pages as top-level functions) based on a template (GH9911)
217+
# the API pages as top-level functions) based on a template (GH9911)
218218
moved_api_pages = [
219219
'pandas.core.common.isnull', 'pandas.core.common.notnull', 'pandas.core.reshape.get_dummies',
220220
'pandas.tools.merge.concat', 'pandas.tools.merge.merge', 'pandas.tools.pivot.pivot_table',
@@ -327,6 +327,7 @@
327327

328328
from sphinx.util import rpartition
329329
from sphinx.ext.autodoc import Documenter, MethodDocumenter, AttributeDocumenter
330+
from sphinx.ext.autosummary import Autosummary
330331

331332

332333
class AccessorLevelDocumenter(Documenter):
@@ -388,6 +389,44 @@ class AccessorMethodDocumenter(AccessorLevelDocumenter, MethodDocumenter):
388389
directivetype = 'method'
389390

390391

392+
class AccessorCallableDocumenter(AccessorLevelDocumenter, MethodDocumenter):
393+
"""
394+
This documenter lets us removes .__call__ from the method signature for
395+
callable accessors like Series.plot
396+
"""
397+
objtype = 'accessorcallable'
398+
directivetype = 'method'
399+
400+
# lower than MethodDocumenter; otherwise the doc build prints warnings
401+
priority = 0.5
402+
403+
def format_name(self):
404+
return MethodDocumenter.format_name(self).rstrip('.__call__')
405+
406+
407+
class PandasAutosummary(Autosummary):
408+
"""
409+
This alternative autosummary class lets us override the table summary for
410+
Series.plot and DataFrame.plot in the API docs.
411+
"""
412+
413+
def _replace_pandas_items(self, display_name, sig, summary, real_name):
414+
# this a hack: ideally we should extract the signature from the
415+
# .__call__ method instead of hard coding this
416+
if display_name == 'DataFrame.plot':
417+
sig = '([x, y, kind, ax, ....])'
418+
summary = 'DataFrame plotting accessor and method'
419+
elif display_name == 'Series.plot':
420+
sig = '([kind, ax, figsize, ....])'
421+
summary = 'Series plotting accessor and method'
422+
return (display_name, sig, summary, real_name)
423+
424+
def get_items(self, names):
425+
items = Autosummary.get_items(self, names)
426+
items = [self._replace_pandas_items(*item) for item in items]
427+
return items
428+
429+
391430
# remove the docstring of the flags attribute (inherited from numpy ndarray)
392431
# because these give doc build errors (see GH issue 5331)
393432
def remove_flags_docstring(app, what, name, obj, options, lines):
@@ -398,3 +437,5 @@ def setup(app):
398437
app.connect("autodoc-process-docstring", remove_flags_docstring)
399438
app.add_autodocumenter(AccessorAttributeDocumenter)
400439
app.add_autodocumenter(AccessorMethodDocumenter)
440+
app.add_autodocumenter(AccessorCallableDocumenter)
441+
app.add_directive('autosummary', PandasAutosummary)

doc/source/visualization.rst

+16-2
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,9 @@ You can plot one column versus another using the `x` and `y` keywords in
121121
Other Plots
122122
-----------
123123

124-
The ``kind`` keyword argument of :meth:`~DataFrame.plot` accepts
125-
a handful of values for plots other than the default Line plot.
124+
Plotting methods allow for a handful of plot styles other than the
125+
default Line plot. These methods can be provided as the ``kind``
126+
keyword argument to :meth:`~DataFrame.plot`.
126127
These include:
127128

128129
* :ref:`'bar' <visualization.barplot>` or :ref:`'barh' <visualization.barplot>` for bar plots
@@ -134,6 +135,19 @@ These include:
134135
* :ref:`'hexbin' <visualization.hexbin>` for hexagonal bin plots
135136
* :ref:`'pie' <visualization.pie>` for pie plots
136137

138+
.. versionadded:: 0.17
139+
140+
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:
141+
142+
.. ipython::
143+
:verbatim:
144+
145+
In [14]: df = pd.DataFrame()
146+
147+
In [15]: df.plot.<TAB>
148+
df.plot.area df.plot.barh df.plot.density df.plot.hist df.plot.line df.plot.scatter
149+
df.plot.bar df.plot.box df.plot.hexbin df.plot.kde df.plot.pie
150+
137151
In addition to these ``kind`` s, there are the :ref:`DataFrame.hist() <visualization.hist>`,
138152
and :ref:`DataFrame.boxplot() <visualization.box>` methods, which use a separate interface.
139153

doc/source/whatsnew/v0.17.0.txt

+30-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ users upgrade to this version.
2929
Highlights include:
3030

3131
- Release the Global Interpreter Lock (GIL) on some cython operations, see :ref:`here <whatsnew_0170.gil>`
32+
- Plotting methods are now available as attributes of the ``.plot`` accessor, see :ref:`here <whatsnew_0170.plot>`
3233
- The sorting API has been revamped to remove some long-time inconsistencies, see :ref:`here <whatsnew_0170.api_breaking.sorting>`
3334
- Support for a ``datetime64[ns]`` with timezones as a first-class dtype, see :ref:`here <whatsnew_0170.tz>`
3435
- 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
116117
.. _dask: https://dask.readthedocs.org/en/latest/
117118
.. _QT: https://wiki.python.org/moin/PyQt
118119

120+
.. _whatsnew_0170.plot:
121+
122+
Plot submethods
123+
^^^^^^^^^^^^^^^
124+
125+
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.
126+
127+
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>(...)``:
128+
129+
.. ipython::
130+
:verbatim:
131+
132+
In [13]: df = pd.DataFrame(np.random.rand(10, 2), columns=['a', 'b'])
133+
134+
In [14]: df.plot.bar()
135+
136+
.. image:: _static/whatsnew_plot_submethods.png
137+
138+
As a result of this change, these methods are now all discoverable via tab-completion:
139+
140+
.. ipython::
141+
:verbatim:
142+
143+
In [15]: df.plot.<TAB>
144+
df.plot.area df.plot.barh df.plot.density df.plot.hist df.plot.line df.plot.scatter
145+
df.plot.bar df.plot.box df.plot.hexbin df.plot.kde df.plot.pie
146+
147+
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.
148+
119149
.. _whatsnew_0170.strftime:
120150

121151
Support strftime for Datetimelikes
@@ -251,7 +281,6 @@ has been changed to make this keyword unnecessary - the change is shown below.
251281
Excel files saved in version 0.16.2 or prior that had index names will still able to be read in,
252282
but the ``has_index_names`` argument must specified to ``True``.
253283

254-
255284
.. _whatsnew_0170.enhancements.other:
256285

257286
Other enhancements

pandas/core/frame.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,14 @@
4747
OrderedDict, raise_with_traceback)
4848
from pandas import compat
4949
from pandas.sparse.array import SparseArray
50-
from pandas.util.decorators import deprecate, Appender, Substitution, \
51-
deprecate_kwarg
50+
from pandas.util.decorators import (cache_readonly, deprecate, Appender,
51+
Substitution, deprecate_kwarg)
5252

5353
from pandas.tseries.period import PeriodIndex
5454
from pandas.tseries.index import DatetimeIndex
5555

5656
import pandas.core.algorithms as algos
57+
import pandas.core.base as base
5758
import pandas.core.common as com
5859
import pandas.core.format as fmt
5960
import pandas.core.nanops as nanops
@@ -5432,7 +5433,7 @@ def _put_str(s, space):
54325433

54335434
import pandas.tools.plotting as gfx
54345435

5435-
DataFrame.plot = gfx.plot_frame
5436+
DataFrame.plot = base.AccessorProperty(gfx.FramePlotMethods, gfx.FramePlotMethods)
54365437
DataFrame.hist = gfx.hist_frame
54375438

54385439

pandas/core/groupby.py

+24
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,28 @@ def _get_binner_for_grouping(self, obj):
295295
def groups(self):
296296
return self.grouper.groups
297297

298+
299+
class GroupByPlot(PandasObject):
300+
"""
301+
Class implementing the .plot attribute for groupby objects
302+
"""
303+
def __init__(self, groupby):
304+
self._groupby = groupby
305+
306+
def __call__(self, *args, **kwargs):
307+
def f(self, *args, **kwargs):
308+
return self.plot(*args, **kwargs)
309+
f.__name__ = 'plot'
310+
return self._groupby.apply(f)
311+
312+
def __getattr__(self, name):
313+
def attr(*args, **kwargs):
314+
def f(self):
315+
return getattr(self.plot, name)(*args, **kwargs)
316+
return self._groupby.apply(f)
317+
return attr
318+
319+
298320
class GroupBy(PandasObject):
299321

300322
"""
@@ -537,6 +559,8 @@ def __getattr__(self, attr):
537559
def __getitem__(self, key):
538560
raise NotImplementedError('Not implemented: %s' % key)
539561

562+
plot = property(GroupByPlot)
563+
540564
def _make_wrapper(self, name):
541565
if name not in self._apply_whitelist:
542566
is_callable = callable(getattr(self._selected_obj, name, None))

pandas/core/series.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2882,7 +2882,7 @@ def __init__(self, *args, **kwargs):
28822882

28832883
import pandas.tools.plotting as _gfx
28842884

2885-
Series.plot = _gfx.plot_series
2885+
Series.plot = base.AccessorProperty(_gfx.SeriesPlotMethods, _gfx.SeriesPlotMethods)
28862886
Series.hist = _gfx.hist_series
28872887

28882888
# Add arithmetic!

0 commit comments

Comments
 (0)