ENH: plot functions accept multiple axes and layout kw
State: closed

- Added `layout` keyword to `plot_frame` (Closes #6667)
- Allow to pass multiple axes to `plot_frame`, `hist` and `boxplot` (Closes #5353, Closes #6970, Closes #7069)

```
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import pandas.util.testing as tm

n = 20
gender = tm.choice(['Male', 'Female'], size=n)
classroom = tm.choice(['A', 'B', 'C'], size=n)
df = pd.DataFrame({'gender': gender,
                          'classroom': classroom,
                          'height': np.random.normal(66, 4, size=n),
                          'weight': np.random.normal(161, 32, size=n),
                          'category': np.random.randint(4, size=n)})

fig, axes = plt.subplots(6, 3, figsize=(6, 7))
df.boxplot(by='category', column=['height', 'weight', 'category'], ax=axes[0])
df.groupby('classroom').boxplot(column=['height', 'weight', 'category'], ax=axes[1])
df.hist(column=['height', 'weight', 'category'], ax=axes[2])
df.hist(by='classroom', ax=axes[3])
df.plot(subplots=True, ax=axes[4], legend=False)
df.plot(kind='pie', subplots=True, ax=axes[5], legend=False)
plt.subplots_adjust(hspace=1, bottom=0.05)
```
### Result

![multiax](https://cloud.githubusercontent.com/assets/1696302/3561467/f1ce4c82-09c2-11e4-8623-08b1bd519ebc.png)


Labels:
name: Bug
name: Enhancement
name: Visualization
description: plotting
name: API Design

Comments:
can u provide some doc updates in the main ottomh section using layout?

Yeah, will do. 

And there are 2 things to confirm:
- Grouped box plot always sets fixed figure title, but is this actually required?
  https://github.com/pydata/pandas/blob/master/pandas/tools/plotting.py#L2775
- Some plot function adjusts subplots margins internally, and it corrupts user defined layout. I think this kind of adjustment should be left to users.
  https://github.com/pydata/pandas/blob/master/pandas/tools/plotting.py#L2516

Docs are added.

cc @onesandzeroes, @Amyunimus, @cheflo 
Pls check if anything related to your original reports.

Looks good to me, I think it addresses #7069 pretty well.

Side-note: if you've reworked the `_subplots()` function to only create the required number of axes, then you might have solved #7528 in the process, I'll try to have a look at that later.

@onesandzeroes Thanks for your review.

Regarding your side note, `hist` and `boxplot` are returning 2-d  array which has the same shape as the `layout`. To reduce the number of axes, this behavior will be affected.

@sinhrks pls comment on @onesandzeroes point about #7528 as well.thanks

My comment for #7528 is on above.

> Regarding your side note, hist and boxplot are returning 2-d array which has the same shape as the layout. To reduce the number of axes, this behavior will be affected.

If fixing #7528 by reducing the number of axes, API change is required for returning axes. Currently, `hist` and `boxplot` with 3 subplots and (2, 2) layout returns (2, 2) array of `AxesSubplot`, for example. (And `plot_frame` in this PR also follows this behavior)

Looks like this fixes the axes issue I struggled with, thanks!

This fixes my original SO (http://stackoverflow.com/questions/23301072/plotting-pandas-boxplots-next-to-each-other-in-ipython-notebook) issue.

Thanks. Any comments for below, or leave it for now?

> Grouped box plot always sets fixed figure title, but is this actually required?
> https://github.com/pydata/pandas/blob/master/pandas/tools/plotting.py#L2775
> 
> Some plot function adjusts subplots margins internally, and it corrupts user defined layout. I think this kind of adjustment should be left to users.
> https://github.com/pydata/pandas/blob/master/pandas/tools/plotting.py#L2516

@cpcloud want to take a look?

@TomAugspurger can you have a look

@sinhrks this should be before  #7998? 

Looks like #7998 is on top of this one. I'll review this first so we can merge them separately.

gr8 thanks @TomAugspurger 

This is looking good.

### Layout keyword
- @sinhrks could you explain why you chose to accept `layout` tuples that contain more spots than needed by the subplots, rather than being strict? I'm assuming it's so you can do `df.plot(subplots=True, layout=layout)` and the fill in the extra spaces later with different plots. Maybe add the reason to the docs for `layout`.
- Feature request: allow for layouts like (2, -1) that will automatically fill in the other dimension needed to contain the subplots. Like numpy's reshape (can do in a separate PR)
- `df.plot(layout=(2, 2))` just ignores the `layout` keyword. Intentional, or should we warn/raise saying that you should pass `subplots=True`?

### Multiple Axes
- `df.plot(ax=axes)` requires that the number of axes match exactly. Inconsistent with the layout keyword. Should we adjust this to match the `layout` behavior?

possibly more coming

I can see users getting annoyed about the UserWarning when passing axes.

Can we change the defaults on `sharex` and `sharey` to None and check if they're explicitly set to True?
Then in the code you'll check

``` python
if sharex is None:
    sharex = True
if sharey is None:
    sharey = True
```

so that the default is the same.

That way we have 3 cases:
1. `df.plot(subplots=True, axes=axes)`  # sharex=sharey=None; Don't warn.
2. `df.plot(subplots=True, axes=axes, sharex | sharey=True)`  # warn / raise; I'd say raise
3. `df.plot(subplots=True, axes=axes, sharex & sharey=False)`  # dont warn

> @sinhrks could you explain why you chose to accept layout tuples that contain more spots than needed by the subplots, rather than being strict? I'm assuming it's so you can do df.plot(subplots=True, layout=layout) and the fill in the extra spaces later with different plots. Maybe add the reason to the docs for layout.

There are cases which can mismatch, 3 columns with 2 x 2 layout for example.

About warnings:

> df.plot(layout=(2, 2)) just ignores the layout keyword. Intentional, or should we warn/raise saying that you should pass subplots=True?

Currently ignored without warnings as this is for subplots. Warning is displayed in cases which may different from user's expectation. 

> Can we change the defaults on sharex and sharey to None and check if they're explicitly set to True?

Waning must be controlled in each plotting functions to do this, because of initial value differences. This makes plotting functions complex more than required. Thus possible options I think are remove warnings completely, or follow the current behavior.

> df.plot(ax=axes) requires that the number of axes match exactly. Inconsistent with the layout keyword. Should we adjust this to match the layout behavior?

I think `layout` is an exception because it may mismatch inevitably as described. `ax` can't have such cases, thus I prefer explicit error.

Related to #7045. Shoud fix basic policies before going to warnings details

Great, thanks for clearing those points up.

Unless you have any objections, I'll merge this soon and we'll follow up with
- warnings: #7045 
- boxplot part of df.plot() #7998 
- ability to pass `layout=(n, -1)` https://github.com/pydata/pandas/issues/8071

@sinhrks some errors in the doc build at the moment, and originating from this PR I think:

```
>>>-------------------------------------------------------------------------
Warning in /tmp/doc/source/visualization.rst at block ending on line 344
Specify :okwarning: as an option in the ipython:: block to suppress this message
----------------------------------------------------------------------------
/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/matplotlib/pyplot.py:412: 
RuntimeWarning: More than 20 figures have been opened. Figures created through the
pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume
too much memory. (To control this warning, see the rcParam `figure.max_num_figures`).
max_open_warning, RuntimeWarning)
<<<-------------------------------------------------------------------------

>>>-------------------------------------------------------------------------
Warning in /tmp/doc/source/visualization.rst at block ending on line 1069
Specify :okwarning: as an option in the ipython:: block to suppress this message
----------------------------------------------------------------------------
/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/matplotlib/axes.py:4747: 
UserWarning: No labeled objects found. Use label='...' kwarg on individual plots.
warnings.warn("No labeled objects found. "
<<<-------------------------------------------------------------------------

>>>-------------------------------------------------------------------------
Exception in /tmp/doc/source/visualization.rst at block ending on line 1087
Specify :okexcept: as an option in the ipython:: block to suppress this message
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-132-99d20e4d6220> in <module>()
----> 1 plt.adjust_subplots(wspace=0.5, hspace=0.5);
AttributeError: 'module' object has no attribute 'adjust_subplots'
<<<-------------------------------------------------------------------------

>>>-------------------------------------------------------------------------
Warning in /tmp/doc/source/visualization.rst at block ending on line 1087
Specify :okwarning: as an option in the ipython:: block to suppress this message
----------------------------------------------------------------------------
/home/travis/virtualenv/python2.7.8/lib/python2.7/site-packages/pandas/tools/plotting.py:3138: 
UserWarning: When passing multiple axes, sharex and sharey are ignored.These settings must
be specified when creating axes
"These settings must be specified when creating axes", UserWarning)
<<<-------------------------------------------------------------------------
```

OK, will check.