Repository: pandas-dev/pandas

Issue:
ENH: other plotting tools via plot accessor
- [ ] `scatter_matrix` (deprecate, redirect to `seaborn`)
- [ ] `andrews_curve`
- [ ] `parallel_coordinates`
- [ ] `lag_plot` (maybe rename to `lag`)
- [ ] `autocorrelation_plot` (maybe rename to `autocorrelation`?)
- [ ] `bootstrap_plot` (maybe rename to `bootstrap`?)
- [ ] `radviz`

I think it would be nice to allow [scatter_matrix](http://pandas.pydata.org/pandas-docs/version/0.15.0/visualization.html#scatter-matrix-plot) to be called directly on a DataFrame. Currently, `scatter_matrix` is a separate function that takes a DataFrame as a parameter, but it seems like it would be easy enough to rework to allow it to be called directly on a DataFrame as well. Effectively, the convenience function would look something like:

``` python
def scatter_matrix(self, ...):
    pandas.tools.plotting.scatter_matrix(self.data, ...)
```

Is this feature feasible?



Discussion:
We could probably do this as `DataFrame.plot.scatter_matrix`. I'm not sure why this wasn't ever folded into the `df.plot(kind=)` function.

Also, the standard disclaimer that seaborn [does this better](https://stanford.edu/~mwaskom/software/seaborn/examples/scatterplot_matrix.html)

actually prob _all_ should be added via the plot accessor: http://pandas.pydata.org/pandas-docs/stable/visualization.html#plotting-tools

Something to consider: Maybe we want to deprecate some of these more complex plot types? For example, seaborn does a better job of scatter matrix than we do...

absolutely let's do that. any others?

 `bootstrap`, `autocorrelation`, `lag` for statsmodels?

cc @jseabold
cc @josef-pkt

@shoyer: IMO the scatter matrix is a basic plot type that people would want to see for a DataFrame.

I agree with @shoyer. These plotting methods are rather 'neglected' lately. I think we should either choose to give them more attention, or deprecate them. And personally I think it should not be the focus of pandas. 

But maybe `scatter_matrix` as the IMO the most generic of the plotting methods listed can be the exception?

why don't we defer scatter_mattix to seaborn anyhow? so seaborn becomes an optional dep)

@jreback   how well are optional circular dependencies no supported in various packagings?
I haven't kept up with it, but we get circular dependency pandas - seaborn - statsmodels.
I'm in favor of tight integration, in general.

I'm not a heavy pandas user directly, but I also find some quick plot methods convenient, instead of having to look for the appropriate function in another package.

about plots in statsmodels: plots are mostly in a minimal maintenance state. There is not much effort for a style update to make them look better, e.g. compared to seaborn, because of a lack of developers. 
However, we need them in support of the models and other stats functions and still keep adding more. 
For example, acf and pacf are some of our oldest plot functions and won't go away.

Trying to use seaborn as optional statsmodels dependency to upgrade plots is an idea but doesn't have a champion to look into it and work on it. For many plots the focus of seaborn is exploratory analysis which is more similar to the pandas use, but for us having a model inside the plot is inside out because we need plots inside the models, or after having estimated a model.

using seaborn (or sm using pandas is no problem here)
you can simply import inside the method itself

I'm in favor of not duplicating efforts and seaborn take over "statistics-driven" plotting in general. I do agree that quick and dirty DataFrame.plot for basic matplotlib plots is still useful, of course.

@jreback @jseabold   What about bokeh?  Is there going to be a stats plotting effort there in the new chart interface? 

I think that has more potential (for interactive data exploration like ggvis and due to engineer time) than seaborn. 

@jreback  I was thinking more about the install process, pip, requirements and various distributions. Are those all smart enough by now, or will there be, at least temporary, problems?

In statsmodels all plot functions try except protect matplotlib import, but we would still have to watch out for circular module imports when statsmodels reuses seaborn. However, that's a internal problem not a distribution problem.

well I have been pushing the `engine=seaborn | bokeh | mpl | pandas` for a little bit. IOW for the 'standard' ones, we have an implementation and just defer if the user passes `engine`. For the ones we don't have an impl, then have to defer.

Ideally we would remove _all_ plotting code from pandas (except for say resampling callback and such). And just defer to various engines.

You can do this now via `.pipe`, this would just make the methods slightly easier.

> Ideally we would remove all plotting code from pandas (except for say resampling callback and such). And just defer to various engines.

:+1: 


Resolving patch:
diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py
--- a/pandas/plotting/_core.py
+++ b/pandas/plotting/_core.py
@@ -26,6 +26,7 @@
 from pandas.core.generic import _shared_doc_kwargs, _shared_docs
 
 from pandas.io.formats.printing import pprint_thing
+from pandas.plotting import _misc as misc
 from pandas.plotting._compat import _mpl_ge_3_0_0
 from pandas.plotting._style import _get_standard_colors, plot_params
 from pandas.plotting._tools import (
@@ -2903,6 +2904,15 @@ def pie(self, **kwds):
         """
         return self(kind='pie', **kwds)
 
+    def lag(self, *args, **kwds):
+        return misc.lag_plot(self._parent, *args, **kwds)
+
+    def autocorrelation(self, *args, **kwds):
+        return misc.autocorrelation_plot(self._parent, *args, **kwds)
+
+    def bootstrap(self, *args, **kwds):
+        return misc.bootstrap_plot(self._parent, *args, **kwds)
+
 
 class FramePlotMethods(BasePlotMethods):
     """DataFrame plotting accessor and method
@@ -3598,3 +3608,16 @@ def hexbin(self, x, y, C=None, reduce_C_function=None, gridsize=None,
         if gridsize is not None:
             kwds['gridsize'] = gridsize
         return self(kind='hexbin', x=x, y=y, C=C, **kwds)
+
+    def scatter_matrix(self, *args, **kwds):
+        return misc.scatter_matrix(self._parent, *args, **kwds)
+
+    def andrews_curves(self, class_column, *args, **kwds):
+        return misc.andrews_curves(self._parent, class_column, *args, **kwds)
+
+    def parallel_coordinates(self, class_column, *args, **kwds):
+        return misc.parallel_coordinates(self._parent, class_column,
+                                         *args, **kwds)
+
+    def radviz(self, class_column, *args, **kwds):
+        return misc.radviz(self._parent, class_column, *args, **kwds)


Test patch:
diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py
--- a/pandas/tests/plotting/test_frame.py
+++ b/pandas/tests/plotting/test_frame.py
@@ -2988,6 +2988,22 @@ def test_secondary_axis_font_size(self, method):
         self._check_ticks_props(axes=ax.right_ax,
                                 ylabelsize=fontsize)
 
+    def test_misc_bindings(self, mock):
+        df = pd.DataFrame(randn(10, 10), columns=list('abcdefghij'))
+        p1 = mock.patch('pandas.plotting._misc.scatter_matrix',
+                        return_value=2)
+        p2 = mock.patch('pandas.plotting._misc.andrews_curves',
+                        return_value=2)
+        p3 = mock.patch('pandas.plotting._misc.parallel_coordinates',
+                        return_value=2)
+        p4 = mock.patch('pandas.plotting._misc.radviz',
+                        return_value=2)
+        with p1, p2, p3, p4:
+            assert df.plot.scatter_matrix() == 2
+            assert df.plot.andrews_curves('a') == 2
+            assert df.plot.parallel_coordinates('a') == 2
+            assert df.plot.radviz('a') == 2
+
 
 def _generate_4_axes_via_gridspec():
     import matplotlib.pyplot as plt
diff --git a/pandas/tests/plotting/test_series.py b/pandas/tests/plotting/test_series.py
--- a/pandas/tests/plotting/test_series.py
+++ b/pandas/tests/plotting/test_series.py
@@ -877,3 +877,16 @@ def test_custom_business_day_freq(self):
             freq=CustomBusinessDay(holidays=['2014-05-26'])))
 
         _check_plot_works(s.plot)
+
+    def test_misc_bindings(self, mock):
+        s = Series(randn(10))
+        p1 = mock.patch('pandas.plotting._misc.lag_plot',
+                        return_value=2)
+        p2 = mock.patch('pandas.plotting._misc.autocorrelation_plot',
+                        return_value=2)
+        p3 = mock.patch('pandas.plotting._misc.bootstrap_plot',
+                        return_value=2)
+        with p1, p2, p3:
+            assert s.plot.lag() == 2
+            assert s.plot.autocorrelation() == 2
+            assert s.plot.bootstrap() == 2