Skip to content

Commit bc7f851

Browse files
committed
WIP/ENH: plot methods accessors
Fixes pandas-dev#9124 This PR adds plotting sub-methods like `df.plot.scatter()` as an alternative to using `df.plot(kind='scatter')`. I've added meaningful function signatures and documentation for a few of these methods, but I would greatly appreciate help to fill in the rest -- this is a lot of documentation to write! The entire point of this PR, of course, is to have better introspection and docstrings. Todo list: - [ ] Function signatures - [ ] `area` - [ ] `line` - [ ] `bar` - [ ] `barh` - [x] `box` - [x] `hexbin` - [x] `hist` - [ ] `kde`/`density` - [ ] `pie` - [x] `scatter` - [ ] Docstrings - [ ] `area` - [ ] `line` - [ ] `bar` - [ ] `barh` - [ ] `box` - [ ] `hexbin` - [x] `hist` - [ ] `kde`/`density` - [ ] `pie` - [ ] `scatter` - [ ] Tests (just a few) - [ ] Plotting docs - [ ] API docs - [ ] Release notes
1 parent c805378 commit bc7f851

File tree

3 files changed

+154
-57
lines changed

3 files changed

+154
-57
lines changed

pandas/core/frame.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@
4545
OrderedDict, raise_with_traceback)
4646
from pandas import compat
4747
from pandas.sparse.array import SparseArray
48-
from pandas.util.decorators import deprecate, Appender, Substitution, \
49-
deprecate_kwarg
48+
from pandas.util.decorators import (cache_readonly, deprecate, Appender,
49+
Substitution, deprecate_kwarg)
5050

5151
from pandas.tseries.period import PeriodIndex
5252
from pandas.tseries.index import DatetimeIndex
@@ -5001,7 +5001,7 @@ def _put_str(s, space):
50015001

50025002
import pandas.tools.plotting as gfx
50035003

5004-
DataFrame.plot = gfx.plot_frame
5004+
DataFrame.plot = cache_readonly(gfx.FramePlotMethods)
50055005
DataFrame.hist = gfx.hist_frame
50065006

50075007

pandas/core/series.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2702,7 +2702,7 @@ def _try_cast(arr, take_fast_path):
27022702

27032703
import pandas.tools.plotting as _gfx
27042704

2705-
Series.plot = _gfx.plot_series
2705+
Series.plot = cache_readonly(_gfx.SeriesPlotMethods)
27062706
Series.hist = _gfx.hist_series
27072707

27082708
# Add arithmetic!

pandas/tools/plotting.py

Lines changed: 150 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import numpy as np
1212

1313
from pandas.util.decorators import cache_readonly, deprecate_kwarg
14+
from pandas.core.base import PandasObject
1415
import pandas.core.common as com
1516
from pandas.core.generic import _shared_docs, _shared_doc_kwargs
1617
from pandas.core.index import Index, MultiIndex
@@ -2461,60 +2462,12 @@ def _plot(data, x=None, y=None, subplots=False,
24612462
24622463
"""
24632464

2464-
@Appender(_shared_docs['plot'] % _shared_doc_df_kwargs)
2465-
def plot_frame(data, x=None, y=None, kind='line', ax=None, # Dataframe unique
2466-
subplots=False, sharex=True, sharey=False, layout=None, # Dataframe unique
2467-
figsize=None, use_index=True, title=None, grid=None,
2468-
legend=True, style=None, logx=False, logy=False, loglog=False,
2469-
xticks=None, yticks=None, xlim=None, ylim=None,
2470-
rot=None, fontsize=None, colormap=None, table=False,
2471-
yerr=None, xerr=None,
2472-
secondary_y=False, sort_columns=False, # Dataframe unique
2473-
**kwds):
2474-
return _plot(data, kind=kind, x=x, y=y, ax=ax,
2475-
subplots=subplots, sharex=sharex, sharey=sharey,
2476-
layout=layout, figsize=figsize, use_index=use_index,
2477-
title=title, grid=grid, legend=legend,
2478-
style=style, logx=logx, logy=logy, loglog=loglog,
2479-
xticks=xticks, yticks=yticks, xlim=xlim, ylim=ylim,
2480-
rot=rot, fontsize=fontsize, colormap=colormap, table=table,
2481-
yerr=yerr, xerr=xerr,
2482-
secondary_y=secondary_y, sort_columns=sort_columns,
2483-
**kwds)
2484-
2485-
2486-
@Appender(_shared_docs['plot'] % _shared_doc_series_kwargs)
2487-
def plot_series(data, kind='line', ax=None, # Series unique
2488-
figsize=None, use_index=True, title=None, grid=None,
2489-
legend=False, style=None, logx=False, logy=False, loglog=False,
2490-
xticks=None, yticks=None, xlim=None, ylim=None,
2491-
rot=None, fontsize=None, colormap=None, table=False,
2492-
yerr=None, xerr=None,
2493-
label=None, secondary_y=False, # Series unique
2494-
**kwds):
24952465

2496-
import matplotlib.pyplot as plt
2497-
"""
2498-
If no axes is specified, check whether there are existing figures
2499-
If there is no existing figures, _gca() will
2500-
create a figure with the default figsize, causing the figsize=parameter to
2501-
be ignored.
2502-
"""
2503-
if ax is None and len(plt.get_fignums()) > 0:
2504-
ax = _gca()
2505-
ax = getattr(ax, 'left_ax', ax)
2506-
# is there harm in this?
2507-
if label is None:
2508-
label = data.name
2509-
return _plot(data, kind=kind, ax=ax,
2510-
figsize=figsize, use_index=use_index, title=title,
2511-
grid=grid, legend=legend,
2512-
style=style, logx=logx, logy=logy, loglog=loglog,
2513-
xticks=xticks, yticks=yticks, xlim=xlim, ylim=ylim,
2514-
rot=rot, fontsize=fontsize, colormap=colormap, table=table,
2515-
yerr=yerr, xerr=xerr,
2516-
label=label, secondary_y=secondary_y,
2517-
**kwds)
2466+
def plot_frame(data, *args, **kwargs):
2467+
"""Depreated; use data.plot instead"""
2468+
return data.plot(*args, **kwargs)
2469+
2470+
plot_series = plot_frame
25182471

25192472

25202473
_shared_docs['boxplot'] = """
@@ -3361,6 +3314,150 @@ def _set_ticks_props(axes, xlabelsize=None, xrot=None,
33613314
return axes
33623315

33633316

3317+
class BasePlotMethods(PandasObject):
3318+
def __init__(self, data):
3319+
self._data = data
3320+
3321+
def __call__(self, *args, **kwargs):
3322+
raise NotImplementedError
3323+
3324+
# WIP: Each of these methods needs to be updated to add specific keyword
3325+
# arguments. Some of these methods should probably be moved to the more
3326+
# specific SeriesPlotMethods/FramePlotMethods below. Help would be
3327+
# appreciated!
3328+
3329+
def line(self, *args, **kwargs):
3330+
"""Needs docstring"""
3331+
return self(*args, kind='line', **kwargs)
3332+
3333+
def bar(self, *args, **kwargs):
3334+
"""Needs docstring"""
3335+
return self(*args, kind='bar', **kwargs)
3336+
3337+
def barh(self, *args, **kwargs):
3338+
"""Needs docstring"""
3339+
return self(*args, kind='barh', **kwargs)
3340+
3341+
def box(self, column=None, by=None, ax=None, fontsize=None, rot=0,
3342+
grid=True, figsize=None, layout=None, return_type=None, **kwds):
3343+
"""Needs docstring"""
3344+
return self(kind='box', column=column, by=by, ax=ax, fontsize=fontsize,
3345+
rot=rot, grid=grid, figsize=figsize, layout=layout,
3346+
return_type=return_type, **kwds)
3347+
3348+
def kde(self, *args, **kwargs):
3349+
"""Needs docstring"""
3350+
return self(*args, kind='kde', **kwargs)
3351+
3352+
density = kde
3353+
3354+
def area(self, *args, **kwargs):
3355+
"""Needs docstring"""
3356+
return self(*args, kind='area', **kwargs)
3357+
3358+
def pie(self, *args, **kwargs):
3359+
"""Needs docstring"""
3360+
return self(*args, kind='pie', **kwargs)
3361+
3362+
3363+
class SeriesPlotMethods(BasePlotMethods):
3364+
"""Series plotting accessor
3365+
3366+
Examples
3367+
--------
3368+
>>> s.plot.line()
3369+
>>> s.plot.bar()
3370+
>>> s.plot.hist()
3371+
3372+
The plotting methods can also be accessed by directly calling the
3373+
accessor with the ``kind`` argument:
3374+
``s.plot(kind='line')`` is equivalent to ``s.plot.line()``
3375+
"""
3376+
@Appender(_shared_docs['plot'] % _shared_doc_series_kwargs)
3377+
def __call__(self, kind='line', ax=None, figsize=None, use_index=True,
3378+
title=None, grid=None, legend=False, style=None, logx=False,
3379+
logy=False, loglog=False, xticks=None, yticks=None, xlim=None,
3380+
ylim=None, rot=None, fontsize=None, colormap=None,
3381+
table=False, yerr=None, xerr=None, label=None,
3382+
secondary_y=False, **kwds):
3383+
import matplotlib.pyplot as plt
3384+
# If no axes is specified, check whether there are existing figures If
3385+
# there is no existing figures, _gca() will create a figure with the
3386+
# default figsize, causing the figsize=parameter to be ignored.
3387+
if ax is None and len(plt.get_fignums()) > 0:
3388+
ax = _gca()
3389+
ax = getattr(ax, 'left_ax', ax)
3390+
# is there harm in this?
3391+
if label is None:
3392+
label = self._data.name
3393+
return _plot(self._data, kind=kind, ax=ax,
3394+
figsize=figsize, use_index=use_index, title=title,
3395+
grid=grid, legend=legend,
3396+
style=style, logx=logx, logy=logy, loglog=loglog,
3397+
xticks=xticks, yticks=yticks, xlim=xlim, ylim=ylim,
3398+
rot=rot, fontsize=fontsize, colormap=colormap, table=table,
3399+
yerr=yerr, xerr=xerr,
3400+
label=label, secondary_y=secondary_y,
3401+
**kwds)
3402+
3403+
def hist(self, by=None, ax=None, grid=True, xlabelsize=None, xrot=None,
3404+
ylabelsize=None, yrot=None, figsize=None, bins=10, **kwds):
3405+
return self(kind='hist', by=by, ax=ax, grid=grid,
3406+
xlabelsize=xlabelsize, xrot=xrot, ylabelsize=ylabelsize,
3407+
yrot=yrot, figsize=figsize, bins=bins, **kwds)
3408+
hist.__doc__ = hist_series.__doc__
3409+
3410+
3411+
class FramePlotMethods(BasePlotMethods):
3412+
"""DataFrameFrame plotting accessor
3413+
3414+
Examples
3415+
--------
3416+
>>> df.plot.line()
3417+
>>> df.plot.scatter('x', 'y')
3418+
>>> df.plot.hexbin()
3419+
3420+
The plotting methods can also be accessed by directly calling the
3421+
accessor with the ``kind`` argument:
3422+
``df.plot(kind='line')`` is equivalent to ``df.plot.line()``
3423+
"""
3424+
@Appender(_shared_docs['plot'] % _shared_doc_df_kwargs)
3425+
def __call__(self, x=None, y=None, kind='line', ax=None, subplots=False,
3426+
sharex=True, sharey=False, layout=None, figsize=None,
3427+
use_index=True, title=None, grid=None, legend=True,
3428+
style=None, logx=False, logy=False, loglog=False, xticks=None,
3429+
yticks=None, xlim=None, ylim=None, rot=None, fontsize=None,
3430+
colormap=None, table=False, yerr=None, xerr=None,
3431+
secondary_y=False, sort_columns=False, **kwds):
3432+
return _plot(self._data, kind=kind, x=x, y=y, ax=ax,
3433+
subplots=subplots, sharex=sharex, sharey=sharey,
3434+
layout=layout, figsize=figsize, use_index=use_index,
3435+
title=title, grid=grid, legend=legend,
3436+
style=style, logx=logx, logy=logy, loglog=loglog,
3437+
xticks=xticks, yticks=yticks, xlim=xlim, ylim=ylim,
3438+
rot=rot, fontsize=fontsize, colormap=colormap, table=table,
3439+
yerr=yerr, xerr=xerr,
3440+
secondary_y=secondary_y, sort_columns=sort_columns,
3441+
**kwds)
3442+
3443+
def hist(self, column=None, by=None, grid=True, xlabelsize=None,
3444+
xrot=None, ylabelsize=None, yrot=None, ax=None, sharex=False,
3445+
sharey=False, figsize=None, layout=None, bins=10, **kwds):
3446+
return self(kind='hist', column=column, by=by, grid=grid,
3447+
xlabelsize=xlabelsize, xrot=xrot, ylabelsize=ylabelsize,
3448+
yrot=yrot, ax=ax, sharex=sharex, sharey=sharey,
3449+
figsize=figsize, layout=layout, bins=bins, **kwds)
3450+
hist.__doc__ = hist_frame.__doc__
3451+
3452+
def scatter(self, x, y, c=None, **kwargs):
3453+
"""Needs docstring"""
3454+
return self(kind='scatter', x=x, y=y, c=c, **kwargs)
3455+
3456+
def hexbin(self, x, y, C=None, **kwargs):
3457+
"""Needs docstring"""
3458+
return self(kind='hexbin', x=x, y=y, C=C, **kwargs)
3459+
3460+
33643461
if __name__ == '__main__':
33653462
# import pandas.rpy.common as com
33663463
# sales = com.load_data('sanfrancisco.home.sales', package='nutshell')

0 commit comments

Comments
 (0)