Skip to content

PLT: Cleaner plotting backend API, and unify Series and DataFrame accessors #27009

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 33 commits into from
Jul 3, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f279d16
Some experiments so far
datapythonista Jun 21, 2019
ca5671c
Refactoring of pandas plotting to make the API clearer
datapythonista Jun 23, 2019
8a56ad7
Merge remote-tracking branch 'upstream/master' into plot_api
Jun 25, 2019
196388b
Addressing review comments, and fixing many tests (still some tests f…
Jun 25, 2019
f00ec30
Merge remote-tracking branch 'upstream/master' into plot_api
Jun 25, 2019
1c16faa
Merge remote-tracking branch 'upstream/master' into plot_api
Jun 26, 2019
995d72e
Restoring docstrings of hist_series, hist_frame, boxplot and boxplot_…
Jun 26, 2019
7e45996
Fixing plot accessor docstring (was in the wrong place, and couple of…
Jun 26, 2019
4fbfed0
Fixing hexbin plot tests
Jun 26, 2019
7d7263a
Fixing bug when calling plot twice on the same data, since the data (…
Jun 26, 2019
1a03cbf
Raising missing exception for pie in DataFrame, and fixing accessor s…
Jun 26, 2019
cf7cbc0
Fixing bug that shown the legend for Series plot
Jun 26, 2019
d063e05
Fix linting
Jun 26, 2019
c83551d
Merge remote-tracking branch 'upstream/master' into plot_api
Jun 28, 2019
369fbf1
Merge remote-tracking branch 'upstream/master' into plot_api
Jul 1, 2019
0d146f9
Fixing bug that made reusing the previous plot for dataframes
Jul 1, 2019
34ea1f2
Removing duplicated data type checks
Jul 1, 2019
19489ba
Restoring original position of methods, so the diff is smaller
Jul 1, 2019
9b4fc6d
Fixing name of reuse_plot parameter
Jul 1, 2019
2597bc9
Fixing bug with matplotlib 2
Jul 1, 2019
57c4937
Adding documentation and improving comments, based on Jeff review
Jul 2, 2019
263ee7a
Adding FutureWarning if Series.plot is called with positional arguments
Jul 2, 2019
37fe165
Not passing default matplotlib parameters to backends (all known kwar…
Jul 2, 2019
0cf4514
Fixing test of plotting accessor parameters
Jul 2, 2019
4d70d5d
Temporary not warning for Series.plot positional arguments (looks lik…
Jul 2, 2019
a2330b2
Revert "Temporary not warning for Series.plot positional arguments (l…
Jul 2, 2019
42a1b35
Merge remote-tracking branch 'upstream/master' into plot_api
Jul 2, 2019
34d189f
Adding debug info in the CI for failing test
Jul 2, 2019
5819585
Revert "Adding debug info in the CI for failing test"
Jul 2, 2019
29d7547
Temporary removing the warning, to see if it's causing the andrews_cu…
Jul 2, 2019
37fb064
Merge branch 'master' into PR_TOOL_MERGE_PR_27009
jreback Jul 3, 2019
a5d0fd9
Revert "Temporary removing the warning, to see if it's causing the an…
datapythonista Jul 3, 2019
ce544e1
Removing test that causes parallel_coordinates test to fail
datapythonista Jul 3, 2019
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
2 changes: 1 addition & 1 deletion pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -8051,7 +8051,7 @@ def isin(self, values):

# ----------------------------------------------------------------------
# Add plotting methods to DataFrame
plot = CachedAccessor("plot", pandas.plotting.FramePlotMethods)
plot = CachedAccessor("plot", pandas.plotting.PlotAccessor)
hist = pandas.plotting.hist_frame
boxplot = pandas.plotting.boxplot_frame
sparse = CachedAccessor("sparse", SparseFrameAccessor)
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -4530,7 +4530,7 @@ def to_period(self, freq=None, copy=True):
str = CachedAccessor("str", StringMethods)
dt = CachedAccessor("dt", CombinedDatetimelikeProperties)
cat = CachedAccessor("cat", CategoricalAccessor)
plot = CachedAccessor("plot", pandas.plotting.SeriesPlotMethods)
plot = CachedAccessor("plot", pandas.plotting.PlotAccessor)
sparse = CachedAccessor("sparse", SparseAccessor)

# ----------------------------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions pandas/plotting/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
Plotting public API
"""
from pandas.plotting._core import (
FramePlotMethods, SeriesPlotMethods, boxplot, boxplot_frame,
boxplot_frame_groupby, hist_frame, hist_series)
PlotAccessor, boxplot, boxplot_frame, boxplot_frame_groupby, hist_frame,
hist_series)
from pandas.plotting._misc import (
andrews_curves, autocorrelation_plot, bootstrap_plot,
deregister as deregister_matplotlib_converters, lag_plot,
parallel_coordinates, plot_params, radviz,
register as register_matplotlib_converters, scatter_matrix, table)

__all__ = ['boxplot', 'boxplot_frame', 'boxplot_frame_groupby', 'hist_frame',
'hist_series', 'FramePlotMethods', 'SeriesPlotMethods',
'hist_series', 'PlotAccessor',
'scatter_matrix', 'radviz', 'andrews_curves', 'bootstrap_plot',
'parallel_coordinates', 'lag_plot', 'autocorrelation_plot',
'table', 'plot_params', 'register_matplotlib_converters',
Expand Down
2,411 changes: 1,069 additions & 1,342 deletions pandas/plotting/_core.py

Large diffs are not rendered by default.

39 changes: 33 additions & 6 deletions pandas/plotting/_matplotlib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import matplotlib.pyplot as plt

from pandas._config import get_option

import pandas

from pandas.plotting._matplotlib.boxplot import (
BoxPlot, boxplot, boxplot_frame, boxplot_frame_groupby)
from pandas.plotting._matplotlib.converter import deregister, register
Expand All @@ -13,13 +17,36 @@
from pandas.plotting._matplotlib.timeseries import tsplot
from pandas.plotting._matplotlib.tools import table

PLOT_CLASSES = {'line': LinePlot,
'bar': BarPlot,
'barh': BarhPlot,
'box': BoxPlot,
'hist': HistPlot,
'kde': KdePlot,
'area': AreaPlot,
'pie': PiePlot,
'scatter': ScatterPlot,
'hexbin': HexBinPlot}

if get_option("plotting.matplotlib.register_converters"):
register(explicit=False)


__all__ = ['LinePlot', 'BarPlot', 'BarhPlot', 'HistPlot', 'BoxPlot', 'KdePlot',
'AreaPlot', 'PiePlot', 'ScatterPlot', 'HexBinPlot', 'hist_series',
'hist_frame', 'boxplot', 'boxplot_frame', 'boxplot_frame_groupby',
'tsplot', 'table', 'andrews_curves', 'autocorrelation_plot',
'bootstrap_plot', 'lag_plot', 'parallel_coordinates', 'radviz',
'scatter_matrix', 'register', 'deregister']
def plot(data, kind, **kwargs):
if isinstance(data, pandas.core.dtypes.generic.ABCSeries):
ax = kwargs.get('ax')
if ax is None and len(plt.get_fignums()) > 0:
with plt.rc_context():
ax = plt.gca()
kwargs['ax'] = getattr(ax, 'left_ax', ax)
plot_obj = PLOT_CLASSES[kind](data, **kwargs)
plot_obj.generate()
plot_obj.draw()
return plot_obj.result


__all__ = ['plot', 'hist_series', 'hist_frame', 'boxplot', 'boxplot_frame',
'boxplot_frame_groupby', 'tsplot', 'table', 'andrews_curves',
'autocorrelation_plot', 'bootstrap_plot', 'lag_plot',
'parallel_coordinates', 'radviz', 'scatter_matrix', 'register',
'deregister']
8 changes: 2 additions & 6 deletions pandas/plotting/_matplotlib/timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,8 @@ def _replot_ax(ax, freq, kwargs):

# for tsplot
if isinstance(plotf, str):
# XXX _plot_classes is private and shouldn't be imported
# here. But as tsplot is deprecated, and we'll remove this
# code soon, it's probably better to not overcomplicate
# things, and just leave this the way it was implemented
from pandas.plotting._core import _plot_classes
plotf = _plot_classes()[plotf]._plot
from pandas.plotting._matplotlib import PLOT_CLASSES
plotf = PLOT_CLASSES[plotf]._plot

lines.append(plotf(ax, series.index._mpl_repr(),
series.values, **kwds)[0])
Expand Down
10 changes: 5 additions & 5 deletions pandas/tests/plotting/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -2225,7 +2225,7 @@ def test_unordered_ts(self):
@td.skip_if_no_scipy
def test_kind_both_ways(self):
df = DataFrame({'x': [1, 2, 3]})
for kind in plotting._core._common_kinds:
for kind in plotting.PlotAccessor._common_kinds:

df.plot(kind=kind)
getattr(df.plot, kind)()
Expand All @@ -2235,7 +2235,7 @@ def test_kind_both_ways(self):

def test_all_invalid_plot_data(self):
df = DataFrame(list('abcd'))
for kind in plotting._core._common_kinds:
for kind in plotting.PlotAccessor._common_kinds:

msg = "no numeric data to plot"
with pytest.raises(TypeError, match=msg):
Expand All @@ -2246,7 +2246,7 @@ def test_partially_invalid_plot_data(self):
with tm.RNGContext(42):
df = DataFrame(randn(10, 2), dtype=object)
df[np.random.rand(df.shape[0]) > 0.5] = 'a'
for kind in plotting._core._common_kinds:
for kind in plotting.PlotAccessor._common_kinds:

msg = "no numeric data to plot"
with pytest.raises(TypeError, match=msg):
Expand Down Expand Up @@ -2738,7 +2738,7 @@ def test_memory_leak(self):
import gc

results = {}
for kind in plotting._core._plot_classes().keys():
for kind in plotting.PlotAccessor._all_kinds:

args = {}
if kind in ['hexbin', 'scatter', 'pie']:
Expand Down Expand Up @@ -2936,7 +2936,7 @@ def test_df_grid_settings(self):
# Make sure plot defaults to rcParams['axes.grid'] setting, GH 9792
self._check_grid_settings(
DataFrame({'a': [1, 2, 3], 'b': [2, 3, 4]}),
plotting._core._dataframe_kinds, kws={'x': 'a', 'y': 'b'})
plotting.PlotAccessor._dataframe_kinds, kws={'x': 'a', 'y': 'b'})

def test_invalid_colormap(self):
df = DataFrame(randn(3, 2), columns=['A', 'B'])
Expand Down
14 changes: 7 additions & 7 deletions pandas/tests/plotting/test_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,8 +684,8 @@ def test_boxplot_series(self):
@pytest.mark.slow
def test_kind_both_ways(self):
s = Series(range(3))
kinds = (plotting._core._common_kinds +
plotting._core._series_kinds)
kinds = (plotting.PlotAccessor._common_kinds +
plotting.PlotAccessor._series_kinds)
_, ax = self.plt.subplots()
for kind in kinds:

Expand All @@ -696,7 +696,7 @@ def test_kind_both_ways(self):
def test_invalid_plot_data(self):
s = Series(list('abcd'))
_, ax = self.plt.subplots()
for kind in plotting._core._common_kinds:
for kind in plotting.PlotAccessor._common_kinds:

msg = "no numeric data to plot"
with pytest.raises(TypeError, match=msg):
Expand All @@ -705,13 +705,13 @@ def test_invalid_plot_data(self):
@pytest.mark.slow
def test_valid_object_plot(self):
s = Series(range(10), dtype=object)
for kind in plotting._core._common_kinds:
for kind in plotting.PlotAccessor._common_kinds:
_check_plot_works(s.plot, kind=kind)

def test_partially_invalid_plot_data(self):
s = Series(['a', 'b', 1.0, 2])
_, ax = self.plt.subplots()
for kind in plotting._core._common_kinds:
for kind in plotting.PlotAccessor._common_kinds:

msg = "no numeric data to plot"
with pytest.raises(TypeError, match=msg):
Expand Down Expand Up @@ -781,8 +781,8 @@ def test_table(self):
def test_series_grid_settings(self):
# Make sure plot defaults to rcParams['axes.grid'] setting, GH 9792
self._check_grid_settings(Series([1, 2, 3]),
plotting._core._series_kinds +
plotting._core._common_kinds)
plotting.PlotAccessor._series_kinds +
plotting.PlotAccessor._common_kinds)

@pytest.mark.slow
def test_standard_colors(self):
Expand Down