Mpld3: ENH: twinx/twiny not working.

Created on 7 May 2014  ·  17Comments  ·  Source: mpld3/mpld3

twinx/twiny are not working as expected. I know this is on the TODO list, but there is no open issue for this yet.

It seems only the last dataset is plotted. Here is the official example.

mpld3 returns:
image

tk-agg returns:
image

Code to reproduce

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import mpld3
mpld3.enable_notebook()

fig, ax1 = plt.subplots()
t = np.arange(0.01, 10.0, 0.01)
s1 = np.exp(t)
ax1.plot(t, s1, 'b-')
ax1.set_xlabel('time (s)')
# Make the y-axis label and tick labels match the line color.
ax1.set_ylabel('exp', color='b')
for tl in ax1.get_yticklabels():
    tl.set_color('b')


ax2 = ax1.twinx()
s2 = np.sin(2*np.pi*t)
ax2.plot(t, s2, 'r.')
ax2.set_ylabel('sin', color='r')
for tl in ax2.get_yticklabels():
    tl.set_color('r')
plt.show()

Most helpful comment

I avoided the issue by setting an alpha on second axis's background.

ax2.patch.set_alpha(0.0)

All 17 comments

PS under Features the last bullet point "additional interactivity tools, such as brushing and box-zoom" can probably be deleted, since box-zoom and brusing are both implemented.

Thanks - I imagine this will be an open issue for quite a while... fixing it would involve some very fundamental changes to the package.

Out of curiosity: is the problem in d3.js, mplexporter or mpld3?

Could perhaps a warning be added? (Twinx x not yet implemented)

Hmm, or better yet, since the frame ticks on the right do get picked up, perhaps I could fake this by drawing a scaled blue line on the first (left axes). Will try tomorrow...

A warning would be a good idea. It should happen in mplexporter/exporter.py. I'm not sure how to best detect this in the Axes object. Probably the first step in figuring out how to implement this is figuring out how to raise a warning when it exists!

twinx() and twiny() result in the creation of an additional axes which has the same location as the original axes. Detecting whether two axes are twins is not too difficult:

import numpy as np
import matplotlib.pyplot as plt


fig = plt.figure()
ax1 = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2)

t = np.arange(0.01, 10.0, 0.01)
s1 = np.exp(t)
ax1.plot(t, s1, 'b-')
ax1.set_xlabel('time (s)')
# Make the y-axis label and tick labels match the line color.
ax1.set_ylabel('exp', color='b')
for tl in ax1.get_yticklabels():
    tl.set_color('b')


ax3 = ax1.twinx()
s2 = np.sin(2*np.pi*t)
ax3.plot(t, s2, 'r.')
ax3.set_ylabel('sin', color='r')
for tl in ax3.get_yticklabels():
    tl.set_color('r')

print ax1.get_shared_x_axes().joined(ax1, ax2)
print ax1.get_shared_x_axes().joined(ax1, ax3)

print ax2.get_shared_x_axes().joined(ax2, ax1)
print ax2.get_shared_x_axes().joined(ax2, ax3)

print ax3.get_shared_x_axes().joined(ax3, ax1)
print ax3.get_shared_x_axes().joined(ax3, ax2)

plt.show()

Given that the two axes are simply stacked, I assumed that everything is passed to D3, which simply draws the two axes on top of each other. I checked this with a simple plugin and this is indeed the case. Both the exponential Line2D and the sin Line2D are present at the D3 side. In fact, the figure on the D3 side has two axes. This also explains why the ticks are rendered correctly. There is nothing from the second axes drawn over them.

OK, so both axes are drawn, but one is on top of the other. Because they have an opaque white background, the content of the lower axes is hidden. Also, because mouse events do not propagate through the axes elements, only the top axes responds to zooming.

Fixing this will require some re-working of the mpld3 layout. I wonder how matplotlib itself handles this?

Exactly.

It appears that axes._frameon is used to control the drawing of both the background patch as well as the frame around the patch. Frameon is true for the first axes and false for the second axes. If we now set frameon to True, we no longer see the line belonging to the first axes:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)

t = np.arange(0.01, 10.0, 0.01)
s1 = np.exp(t)
ax1.plot(t, s1, 'b-')
ax1.set_xlabel('time (s)')
# Make the y-axis label and tick labels match the line color.
ax1.set_ylabel('exp', color='b')
for tl in ax1.get_yticklabels():
    tl.set_color('b')

ax2 = ax1.twinx()
s2 = np.sin(2*np.pi*t)
sin = ax2.plot(t, s2, 'r.')
ax2.set_ylabel('sin', color='r')
for tl in ax2.get_yticklabels():
    tl.set_color('r')

print ax1.get_frame_on()
print ax2.get_frame_on()

ax2.set_frame_on(True)

plt.show()

This suggests that this problem is related to #186 and that addressing one should also help in addressing the other.

Ah - that's great! Thanks for digging into that.

How would you want to address this issue? The most obvious solution would be to pass frame_on along and modify the draw method of the mpld3_Axes to take into account whether frame_on is True or False.

From browsing the code, this would entail changes in mplexporter and MPD3Renderer, on the python side, and a change to mpld3Axes on the javascript side.

Yep: mplexporter would have to be updated to pass through frame_on, and if it's False the associated lines and fill color should be transparent.

I avoided the issue by setting an alpha on second axis's background.

ax2.patch.set_alpha(0.0)
ax2.patch.set_alpha(0.0)

allows the lines to show up, but the hover, pan, and zoom tools still do not work on the lower axes. Is there a way to allow the tools to work on all axes?

Unless I'm much mistaken this is still an issue. Using ax2.patch.set_alpha(0.0) I can see both my twin plots, but zoom/pan still only works for the top axes. The both graphics change, bu only the x-axis and secondary y-axis respond to the new scaling.

Another problem I found is if you try to use three x-axis. The third one should be in the right as described in this example, but It's not working as it's supposed to be. The third one overriddes the second. The zoom tool still not working with twinx() method for me. Hope you can fix it soon (If I have free time, I'll try to).

captura de tela de 2017-09-10 01-21-32

@fronchetti this project is no longer maintained, and has not been for a few years. From the README:

Note: mpld3 is no longer being actively maintained: feature requests & bug reports are likely to go unanswered. If you are interested in maintaining this project, please contact one of the repository owners.

There are lots of other web/notebook plotting options for Python: altair, bokeh, bqplot, just to name a few. [disclosure: I am a Bokeh core dev] Or if you are interested in taking ownership of this project, I'd suggest emailing Jake.

Ouch! I didn't saw this information in their website. I just came here looking for issues that could solve my problem. Thank you @bryevdv, I'll try another plotting option.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jakevdp picture jakevdp  ·  4Comments

andreatramacere picture andreatramacere  ·  8Comments

sheldonpark picture sheldonpark  ·  7Comments

dansteingart picture dansteingart  ·  31Comments

arnaudrenaud picture arnaudrenaud  ·  4Comments