diff --git a/doc/source/release.rst b/doc/source/release.rst index a5d41b9f6a4af..6928991e878d8 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -161,6 +161,17 @@ API Changes - Added ``nunique`` and ``value_counts`` functions to ``Index`` for counting unique elements. (:issue:`6734`) +- ``DataFrame.plot`` and ``Series.plot`` now support a ``table`` keyword for plotting ``matplotlib.Table``. The ``table`` kewyword can receive the following values. + + - ``False``: Do nothing (default). + + - ``True``: Draw a table using the ``DataFrame`` or ``Series`` called ``plot`` method. Data will be transposed to meet matplotlib's default layout. + + - ``DataFrame`` or ``Series``: Draw matplotlib.table using the passed data. The data will be drawn as displayed in print method (not transposed automatically). + + Also, helper function ``pandas.tools.plotting.table`` is added to create a table from ``DataFrame`` and ``Series``, and add it to an ``matplotlib.Axes``. + + Deprecations ~~~~~~~~~~~~ diff --git a/doc/source/v0.14.0.txt b/doc/source/v0.14.0.txt index 58eec9fa0f528..42dca9ae86cf8 100644 --- a/doc/source/v0.14.0.txt +++ b/doc/source/v0.14.0.txt @@ -201,6 +201,17 @@ API changes - Added ``nunique`` and ``value_counts`` functions to ``Index`` for counting unique elements. (:issue:`6734`) +- ``DataFrame.plot`` and ``Series.plot`` now support a ``table`` keyword for plotting ``matplotlib.Table``. The ``table`` kewyword can receive the following values. + + - ``False``: Do nothing (default). + + - ``True``: Draw a table using the ``DataFrame`` or ``Series`` called ``plot`` method. Data will be transposed to meet matplotlib's default layout. + + - ``DataFrame`` or ``Series``: Draw matplotlib.table using the passed data. The data will be drawn as displayed in print method (not transposed automatically). + + Also, helper function ``pandas.tools.plotting.table`` is added to create a table from ``DataFrame`` and ``Series``, and add it to an ``matplotlib.Axes``. + + MultiIndexing Using Slicers ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/source/visualization.rst b/doc/source/visualization.rst index d05ae4b72c2f1..09decc5ed1e25 100644 --- a/doc/source/visualization.rst +++ b/doc/source/visualization.rst @@ -415,6 +415,48 @@ Here is an example of one way to easily plot group means with standard deviation @savefig errorbar_example.png means.plot(yerr=errors, ax=ax, kind='bar') +Plotting With Table +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 0.14 + +Plotting with matplotlib table is now supported in the ``DataFrame.plot`` and ``Series.plot`` by a ``table`` keyword. The ``table`` keyword can accept ``bool``, ``DataFrame`` or ``Series``. The simple way to draw a table is to specify ``table=True``. Data will be transposed to meet matplotlib's default layout. + +.. ipython:: python + + fig, ax = plt.subplots(1, 1) + df = DataFrame(rand(5, 3), columns=['a', 'b', 'c']) + ax.get_xaxis().set_visible(False) # Hide Ticks + + @savefig line_plot_table_true.png + df.plot(table=True, ax=ax) + +Also, you can pass different ``DataFrame`` or ``Series`` for ``table`` keyword. The data will be drawn as displayed in print method (not transposed automatically). If required, it should be transposed manually as below example. + +.. ipython:: python + + fig, ax = plt.subplots(1, 1) + ax.get_xaxis().set_visible(False) # Hide Ticks + @savefig line_plot_table_data.png + df.plot(table=np.round(df.T, 2), ax=ax) + + +Finally, there is a helper function ``pandas.tools.plotting.table`` to create a table from ``DataFrame`` and ``Series``, and add it to an ``matplotlib.Axes``. This function can accept keywords which matplotlib table has. + +.. ipython:: python + + from pandas.tools.plotting import table + fig, ax = plt.subplots(1, 1) + + table(ax, np.round(df.describe(), 2), + loc='upper right', colWidths=[0.2, 0.2, 0.2]) + + @savefig line_plot_table_describe.png + df.plot(ax=ax, ylim=(0, 2), legend=None) + +**Note**: You can get table instances on the axes using ``axes.tables`` property for further decorations. See the `matplotlib table documenation `__ for more. + + .. _visualization.scatter_matrix: Scatter plot matrix diff --git a/pandas/tests/test_graphics.py b/pandas/tests/test_graphics.py index efefc96b51104..5beb5a05a800d 100644 --- a/pandas/tests/test_graphics.py +++ b/pandas/tests/test_graphics.py @@ -309,6 +309,11 @@ def test_errorbar_plot(self): with tm.assertRaises(TypeError): s.plot(yerr=s_err) + def test_table(self): + _check_plot_works(self.series.plot, table=True) + _check_plot_works(self.series.plot, table=self.series) + + @tm.mplskip class TestDataFramePlots(tm.TestCase): def setUp(self): @@ -1335,6 +1340,18 @@ def test_errorbar_asymmetrical(self): tm.close() + def test_table(self): + df = DataFrame(np.random.rand(10, 3), + index=list(string.ascii_letters[:10])) + _check_plot_works(df.plot, table=True) + _check_plot_works(df.plot, table=df) + + ax = df.plot() + self.assert_(len(ax.tables) == 0) + plotting.table(ax, df.T) + self.assert_(len(ax.tables) == 1) + + @tm.mplskip class TestDataFrameGroupByPlots(tm.TestCase): def tearDown(self): diff --git a/pandas/tools/plotting.py b/pandas/tools/plotting.py index c2a929bab77b5..42135e2186468 100644 --- a/pandas/tools/plotting.py +++ b/pandas/tools/plotting.py @@ -793,7 +793,8 @@ def __init__(self, data, kind=None, by=None, subplots=False, sharex=True, ax=None, fig=None, title=None, xlim=None, ylim=None, xticks=None, yticks=None, sort_columns=False, fontsize=None, - secondary_y=False, colormap=None, **kwds): + secondary_y=False, colormap=None, + table=False, **kwds): self.data = data self.by = by @@ -849,6 +850,8 @@ def __init__(self, data, kind=None, by=None, subplots=False, sharex=True, else: self.colormap = colormap + self.table = table + self.kwds = kwds self._validate_color_args() @@ -915,6 +918,7 @@ def generate(self): self._compute_plot_data() self._setup_subplots() self._make_plot() + self._add_table() self._post_plot_logic() self._adorn_subplots() @@ -1005,6 +1009,21 @@ def _compute_plot_data(self): def _make_plot(self): raise NotImplementedError + def _add_table(self): + if self.table is False: + return + elif self.table is True: + from pandas.core.frame import DataFrame + if isinstance(self.data, Series): + data = DataFrame(self.data, columns=[self.data.name]) + elif isinstance(self.data, DataFrame): + data = self.data + data = data.transpose() + else: + data = self.table + ax = self._get_ax(0) + table(ax, data) + def _post_plot_logic(self): pass @@ -1664,7 +1683,6 @@ def _post_plot_logic(self): for ax in self.axes: ax.legend(loc='best') - class BarPlot(MPLPlot): _default_rot = {'bar': 90, 'barh': 0} @@ -2594,6 +2612,47 @@ def _grouped_plot_by_column(plotf, data, columns=None, by=None, return fig, axes +def table(ax, data, rowLabels=None, colLabels=None, + **kwargs): + + """ + Helper function to convert DataFrame and Series to matplotlib.table + + Parameters + ---------- + `ax`: Matplotlib axes object + `data`: DataFrame or Series + data for table contents + `kwargs`: keywords, optional + keyword arguments which passed to matplotlib.table.table. + If `rowLabels` or `colLabels` is not specified, data index or column name will be used. + + Returns + ------- + matplotlib table object + """ + from pandas import DataFrame + if isinstance(data, Series): + data = DataFrame(data, columns=[data.name]) + elif isinstance(data, DataFrame): + pass + else: + raise ValueError('Input data must be dataframe or series') + + if rowLabels is None: + rowLabels = data.index + + if colLabels is None: + colLabels = data.columns + + cellText = data.values + + import matplotlib.table + table = matplotlib.table.table(ax, cellText=cellText, + rowLabels=rowLabels, colLabels=colLabels, **kwargs) + return table + + def _get_layout(nplots): if nplots == 1: return (1, 1)