Repository: pandas-dev/pandas

Issue:
Plot area legend rendered incorrectly when moved out of the plot
#### Code Sample, a copy-pastable example if possible

In area plots, the handles in the legend are made with filled rectangles, as for example in:

``` python
df = pd.DataFrame(np.random.rand(20, 5), columns=['A', 'B', 'C', 'D', 'E'])
df.plot(kind='area', linewidth=0.1)
```

![Result](http://i.imgur.com/8LgUF3f.png)

However, if method `legend()` is used to alter the position of the legend, the handles are rendered as lines, which poses a problem if the line width is small, as in the following example:

``` python
df = pd.DataFrame(np.random.rand(20, 5), columns=['A', 'B', 'C', 'D', 'E'])
df.plot(kind='area', linewidth=0.1).legend(loc="center left", bbox_to_anchor=(1.02, 0.5))
```

![Bad result](http://i.imgur.com/cdF7jje.png)
#### output of `pd.show_versions()`

``` txt
INSTALLED VERSIONS
------------------
commit: None
python: 3.5.1.final.0
python-bits: 64
OS: Linux
OS-release: 3.19.0-37-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8

pandas: 0.17.1
nose: 1.3.7
pip: 8.0.2
setuptools: 19.6.2
Cython: None
numpy: 1.10.4
scipy: 0.16.1
statsmodels: None
IPython: 4.0.1
sphinx: None
patsy: None
dateutil: 2.4.2
pytz: 2015.7
blosc: None
bottleneck: None
tables: 3.2.2
numexpr: 2.4.6
matplotlib: 1.5.1
openpyxl: None
xlrd: 0.9.4
xlwt: None
xlsxwriter: None
lxml: None
bs4: 4.4.1
html5lib: 0.999
httplib2: None
apiclient: None
sqlalchemy: None
pymysql: None
psycopg2: None
Jinja2: None
```



Discussion:
IIRC the area plot legend is a bit of a hack. It's done [here](https://github.com/pydata/pandas/blob/a01644c07f376b4e2eb48d8dfca7222473e3088d/pandas/tools/plotting.py#L1862)

Interested in seeing how much it would be to get this working?

Yes, I was already digging pandas' source and arrived at the conclusion that the function you pointed out is the one wihch builds the legend, at least when it is placed at its default location. However, for some reason which I don't understand (since I'm not familiar with pandas nor matplotlib internal architecture), that is not the one used when you use the method `legend()` to alter its position. For this case, apparently, the one used is the implemented in `LinePlot` class (which happens to be the base class of `AreaPlot`, btw)

I tried to study the code of `BarPlot`, which works fine also when the legend is positioned via `legend()` method, but I got lost, because `BarPlot` does not implement `_add_legend_handle()`, but uses the one in its base class `MPLPlot`.

Some hints about how to debug this issue?

I've found a workaround, just in case anyone has the same problem:

``` python
# Generate and plot the areas
df = pd.DataFrame(np.random.rand(20, 5), columns=['A', 'B', 'C', 'D', 'E'])
a = df.plot(kind='area', linewidth=0.1, alpha=0.6)

# Recreate the legend (reversing labels to preserve the stacked order of the areas)
# and put it at the right of the plot
import matplotlib.patches as mpatches
handles, labels = a.get_legend_handles_labels()
a.legend(reversed([mpatches.Rectangle((0, 0), 1, 1, fc=handle.get_color(), alpha=handle.get_alpha()) 
                   for handle in handles]), reversed(labels), 
         loc="center left", bbox_to_anchor=(1.02, 0.5))
```

![Result](http://i.imgur.com/ddRn0Da.png)

What is the underlying artists that `area` uses?

@tacaswell it's `PolyCollection` (`fill_between`).

It may be a timing to remove the legend hack which @TomAugspurger pointed, showing a warning if mpl < 1.5.0 which supports `PolyCollection` legend.


Resolving patch:
diff --git a/pandas/tools/plotting.py b/pandas/tools/plotting.py
--- a/pandas/tools/plotting.py
+++ b/pandas/tools/plotting.py
@@ -1839,10 +1839,16 @@ def __init__(self, data, **kwargs):
     @classmethod
     def _plot(cls, ax, x, y, style=None, column_num=None,
               stacking_id=None, is_errorbar=False, **kwds):
+
         if column_num == 0:
             cls._initialize_stacker(ax, stacking_id, len(y))
         y_values = cls._get_stacked_values(ax, stacking_id, y, kwds['label'])
-        lines = MPLPlot._plot(ax, x, y_values, style=style, **kwds)
+
+        # need to remove label, because subplots uses mpl legend as it is
+        line_kwds = kwds.copy()
+        if cls.mpl_ge_1_5_0():
+            line_kwds.pop('label')
+        lines = MPLPlot._plot(ax, x, y_values, style=style, **line_kwds)
 
         # get data from the line to get coordinates for fill_between
         xdata, y_values = lines[0].get_data(orig=False)
@@ -1860,18 +1866,21 @@ def _plot(cls, ax, x, y, style=None, column_num=None,
         if 'color' not in kwds:
             kwds['color'] = lines[0].get_color()
 
-        if cls.mpl_ge_1_5_0():  # mpl 1.5 added real support for poly legends
-            kwds.pop('label')
-        ax.fill_between(xdata, start, y_values, **kwds)
+        rect = ax.fill_between(xdata, start, y_values, **kwds)
         cls._update_stacker(ax, stacking_id, y)
-        return lines
+
+        # LinePlot expects list of artists
+        res = [rect] if cls.mpl_ge_1_5_0() else lines
+        return res
 
     def _add_legend_handle(self, handle, label, index=None):
-        from matplotlib.patches import Rectangle
-        # Because fill_between isn't supported in legend,
-        # specifically add Rectangle handle here
-        alpha = self.kwds.get('alpha', None)
-        handle = Rectangle((0, 0), 1, 1, fc=handle.get_color(), alpha=alpha)
+        if not self.mpl_ge_1_5_0():
+            from matplotlib.patches import Rectangle
+            # Because fill_between isn't supported in legend,
+            # specifically add Rectangle handle here
+            alpha = self.kwds.get('alpha', None)
+            handle = Rectangle((0, 0), 1, 1, fc=handle.get_color(),
+                               alpha=alpha)
         LinePlot._add_legend_handle(self, handle, label, index=index)
 
     def _post_plot_logic(self, ax, data):


Test patch: