Repository: pandas-dev/pandas

Issue:
secondary_y plot shows in separate figure
Hi, don't know if this is my Python/some module version.. but when I try to do the following, I end up with two figures popping up, instead of the expected one:

```
df[[0, 1]].plot(kind='bar', stacked=True, ylim=(0, 100))
df[[2]].plot(secondary_y=True, color='orange', linewidth=2.0)
```

The data in both plots looks right, the left/right y axes are correct etc.. but they're not on the same figure :(

Also if this is of any help, if I do `fig = plt.figure()` before running the commands above, I end up with another completely empty figure shown to me first.. 

Sorry if this is something stupid and/or not caused by pandas..



Discussion:
Hmm, it works if I do `fig, ax = plt.subplots(1, 1)` and then add `ax=ax` to both `df.plot()` calls above.. Might be worth documenting?

However, there are two bugs now:
1. the secondary legend is on top of the first one
2. the plot width is scaled down to the line points, i.e. the first and last bars are cut off by 50%

Erm, executing the secondary_y line plot command first and the bar plot second solves issue 2. Now the plot has enough space for the bars.

Another possibility is doing `ax = df[[0,1]].plot(...)` and then providing this `ax` to the second `df[[2]].plot(..)`, or by doing `df[2].plot(..)` (this is then plotting a series -> added to the existing figure). But, I think your solution of `fig, ax = plt.subplots(1, 1)` is the cleanest and clearest one.

Providing the `ax` is documented (see the docstring and http://pandas.pydata.org/pandas-docs/stable/visualization.html), but maybe not explicitly enough (see also #8776). If you have any suggestions on how to improve it, certainly welcome!

For the other questions, can you provide a small reproducible example? (some runnable code with some dummy data that shows the issue)

Hmm, the two options you mention work indeed, though somehow I'm now missing the first plot's y axis ticks/labels and the second plot's legend altogether.. Here's the snippet with the shortened version (I was doing `df[2]` before and it didn't work because my columns had titles, but otherwise that way works indeed, like the doc says!)

```
from pandas import DataFrame
import matplotlib.pyplot as plt

points = [[50.69922925478467, 23.857641645143786, 2247], [51.11691211658459, 23.30982697233231, 80], [55.37656430470213, 23.25234081849125, 12]]
keys = ['one', 'two', 'three']
df = DataFrame(points, index=keys, columns=['OK', 'OK too', 'Where am I'])

df[[0, 1]].plot(
    kind='bar',
    stacked=True,
    ylim=(0, 100),
)
df['Where am I'].plot(
    color='orange',
    linewidth=2.0,
    mark_right=False,
    secondary_y=True,
)
plt.show()
```

On the other hand, using the following makes the second plot's legend overlap:

```
from pandas import DataFrame
import matplotlib.pyplot as plt

points = [[50.69922925478467, 23.857641645143786, 2247], [51.11691211658459, 23.30982697233231, 80], [55.37656430470213, 23.25234081849125, 12]]
keys = ['one', 'two', 'three']
df = DataFrame(points, index=keys, columns=['Cant see this', 'This OK', 'Move me please'])

fig, ax = plt.subplots(1, 1)
fig = df[[0, 1]].plot(
    kind='bar',
    stacked=True,
    ylim=(0, 100),
    ax=ax
)
fig2 = df[[2]].plot(
    color='orange',
    linewidth=2.0,
    mark_right=False,
    ax=ax,
    secondary_y=True,
)
plt.show()
```

PS. This issue has now gone almost completely off-topic.. but still some issues I think :)


Resolving patch:
diff --git a/pandas/tools/plotting.py b/pandas/tools/plotting.py
--- a/pandas/tools/plotting.py
+++ b/pandas/tools/plotting.py
@@ -926,7 +926,19 @@ def generate(self):
     def _args_adjust(self):
         pass
 
-    def _maybe_right_yaxis(self, ax):
+    def _has_plotted_object(self, ax):
+        """check whether ax has data"""
+        return (len(ax.lines) != 0 or
+                len(ax.artists) != 0 or
+                len(ax.containers) != 0)
+
+    def _maybe_right_yaxis(self, ax, axes_num):
+        if not self.on_right(axes_num):
+            if hasattr(ax, 'left_ax'):
+                # secondary axes may be passed as axes
+                return ax.left_ax
+            return ax
+
         if hasattr(ax, 'right_ax'):
             return ax.right_ax
         else:
@@ -936,7 +948,7 @@ def _maybe_right_yaxis(self, ax):
             orig_ax.right_ax, new_ax.left_ax = new_ax, orig_ax
             new_ax.right_ax = new_ax
 
-            if len(orig_ax.get_lines()) == 0:  # no data on left y
+            if not self._has_plotted_object(orig_ax):  # no data on left y
                 orig_ax.get_yaxis().set_visible(False)
             return new_ax
 
@@ -978,7 +990,15 @@ def result(self):
             else:
                 return self.axes
         else:
-            return self.axes[0]
+            sec_true = isinstance(self.secondary_y, bool) and self.secondary_y
+            all_sec = (com.is_list_like(self.secondary_y) and
+                       len(self.secondary_y) == self.nseries)
+            if (sec_true or all_sec):
+                # if all data is plotted on secondary,
+                # return secondary axes
+                return self.axes[0].right_ax
+            else:
+                return self.axes[0]
 
     def _compute_plot_data(self):
         numeric_data = self.data.convert_objects()._get_numeric_data()
@@ -1128,8 +1148,8 @@ def _make_legend(self):
 
     def _get_ax_legend(self, ax):
         leg = ax.get_legend()
-        other_ax = (getattr(ax, 'right_ax', None) or
-                    getattr(ax, 'left_ax', None))
+        other_ax = (getattr(ax, 'left_ax', None) or
+                    getattr(ax, 'right_ax', None))
         other_leg = None
         if other_ax is not None:
             other_leg = other_ax.get_legend()
@@ -1221,20 +1241,11 @@ def _get_ax(self, i):
         if self.subplots:
             ax = self.axes[i]
 
-            if self.on_right(i):
-                ax = self._maybe_right_yaxis(ax)
-                self.axes[i] = ax
+            ax = self._maybe_right_yaxis(ax, i)
+            self.axes[i] = ax
         else:
             ax = self.axes[0]
-
-            if self.on_right(i):
-                ax = self._maybe_right_yaxis(ax)
-
-                sec_true = isinstance(self.secondary_y, bool) and self.secondary_y
-                all_sec = (com.is_list_like(self.secondary_y) and
-                           len(self.secondary_y) == self.nseries)
-                if sec_true or all_sec:
-                    self.axes[0] = ax
+            ax = self._maybe_right_yaxis(ax, i)
 
         ax.get_yaxis().set_visible(True)
         return ax
@@ -1971,7 +1982,7 @@ def _make_plot(self):
                 kwds['style'] = style
 
             artists = plotf(ax, y, column_num=i, **kwds)
-            self._add_legend_handle(artists[0], label)
+            self._add_legend_handle(artists[0], label, index=i)
 
     def _post_plot_logic(self):
         if self.orientation == 'horizontal':


Test patch:
diff --git a/pandas/tests/test_graphics.py b/pandas/tests/test_graphics.py
--- a/pandas/tests/test_graphics.py
+++ b/pandas/tests/test_graphics.py
@@ -755,6 +755,101 @@ def test_hist_no_overlap(self):
         axes = fig.get_axes()
         self.assertEqual(len(axes), 2)
 
+    @slow
+    def test_hist_secondary_legend(self):
+        # GH 9610
+        df = DataFrame(np.random.randn(30, 4), columns=list('abcd'))
+
+        # primary -> secondary
+        ax = df['a'].plot(kind='hist', legend=True)
+        df['b'].plot(kind='hist', ax=ax, legend=True, secondary_y=True)
+        # both legends are dran on left ax
+        # left and right axis must be visible
+        self._check_legend_labels(ax, labels=['a', 'b (right)'])
+        self.assertTrue(ax.get_yaxis().get_visible())
+        self.assertTrue(ax.right_ax.get_yaxis().get_visible())
+        tm.close()
+
+        # secondary -> secondary
+        ax = df['a'].plot(kind='hist', legend=True, secondary_y=True)
+        df['b'].plot(kind='hist', ax=ax, legend=True, secondary_y=True)
+        # both legends are draw on left ax
+        # left axis must be invisible, right axis must be visible
+        self._check_legend_labels(ax.left_ax, labels=['a (right)', 'b (right)'])
+        self.assertFalse(ax.left_ax.get_yaxis().get_visible())
+        self.assertTrue(ax.get_yaxis().get_visible())
+        tm.close()
+
+        # secondary -> primary
+        ax = df['a'].plot(kind='hist', legend=True, secondary_y=True)
+        # right axes is returned
+        df['b'].plot(kind='hist', ax=ax, legend=True)
+        # both legends are draw on left ax
+        # left and right axis must be visible
+        self._check_legend_labels(ax.left_ax, labels=['a (right)', 'b'])
+        self.assertTrue(ax.left_ax.get_yaxis().get_visible())
+        self.assertTrue(ax.get_yaxis().get_visible())
+        tm.close()
+
+    @slow
+    def test_df_series_secondary_legend(self):
+        # GH 9779
+        df = DataFrame(np.random.randn(30, 3), columns=list('abc'))
+        s = Series(np.random.randn(30), name='x')
+
+        # primary -> secondary (without passing ax)
+        ax = df.plot()
+        s.plot(legend=True, secondary_y=True)
+        # both legends are dran on left ax
+        # left and right axis must be visible
+        self._check_legend_labels(ax, labels=['a', 'b', 'c', 'x (right)'])
+        self.assertTrue(ax.get_yaxis().get_visible())
+        self.assertTrue(ax.right_ax.get_yaxis().get_visible())
+        tm.close()
+
+        # primary -> secondary (with passing ax)
+        ax = df.plot()
+        s.plot(ax=ax, legend=True, secondary_y=True)
+        # both legends are dran on left ax
+        # left and right axis must be visible
+        self._check_legend_labels(ax, labels=['a', 'b', 'c', 'x (right)'])
+        self.assertTrue(ax.get_yaxis().get_visible())
+        self.assertTrue(ax.right_ax.get_yaxis().get_visible())
+        tm.close()
+
+        # seconcary -> secondary (without passing ax)
+        ax = df.plot(secondary_y=True)
+        s.plot(legend=True, secondary_y=True)
+        # both legends are dran on left ax
+        # left axis must be invisible and right axis must be visible
+        expected = ['a (right)', 'b (right)', 'c (right)', 'x (right)']
+        self._check_legend_labels(ax.left_ax, labels=expected)
+        self.assertFalse(ax.left_ax.get_yaxis().get_visible())
+        self.assertTrue(ax.get_yaxis().get_visible())
+        tm.close()
+
+        # secondary -> secondary (with passing ax)
+        ax = df.plot(secondary_y=True)
+        s.plot(ax=ax, legend=True, secondary_y=True)
+        # both legends are dran on left ax
+        # left axis must be invisible and right axis must be visible
+        expected = ['a (right)', 'b (right)', 'c (right)', 'x (right)']
+        self._check_legend_labels(ax.left_ax, expected)
+        self.assertFalse(ax.left_ax.get_yaxis().get_visible())
+        self.assertTrue(ax.get_yaxis().get_visible())
+        tm.close()
+
+        # secondary -> secondary (with passing ax)
+        ax = df.plot(secondary_y=True, mark_right=False)
+        s.plot(ax=ax, legend=True, secondary_y=True)
+        # both legends are dran on left ax
+        # left axis must be invisible and right axis must be visible
+        expected = ['a', 'b', 'c', 'x (right)']
+        self._check_legend_labels(ax.left_ax, expected)
+        self.assertFalse(ax.left_ax.get_yaxis().get_visible())
+        self.assertTrue(ax.get_yaxis().get_visible())
+        tm.close()
+
     @slow
     def test_plot_fails_with_dupe_color_and_style(self):
         x = Series(randn(2))