How to to enable sharing for the secondary axis (t

2020-03-30 04:53发布

I'm trying to enable sharing for both primary and secondary axis. The example plot is illustrated by the code below. The plot contains two horizontal axes, the primary axis grid is shown in green, while the other axis has red grid.

#!/usr/bin/python

# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt

FIRST = 0.0
LAST  = 2.0
STEP  = 0.01

t = np.arange(FIRST, LAST, STEP)

s1 = np.sin(2*np.pi*t)
s2 = np.exp(-t)
s3 = s1*s2

###############################################################################

plt.rc('axes', grid=True)
plt.rc('grid', color='0.75', linestyle='-', linewidth=0.5)

fig3 = plt.figure()
ax1primary = plt.subplot2grid((4,3), (0,0), colspan=3, rowspan=2)
ax2primary = plt.subplot2grid((4,3), (2,0), colspan=3, sharex=ax1primary)
ax3primary = plt.subplot2grid((4,3), (3,0), colspan=3, sharex=ax1primary)

ax1primary.plot(t,s1)
ax1primary.set_yticks(np.arange(-0.9, 1.0, 0.3))
ax1primary.xaxis.grid(color='green')

ax2primary.plot(t[:150],s2[:150])
ax2primary.set_yticks(np.arange(0.3, 1, 0.2))
ax2primary.xaxis.grid(color='green')

ax3primary.plot(t[30:],s3[30:])
ax3primary.plot([0,2],[0.2,0.2],'m')
ax3primary.set_yticks(np.arange(-0.4, 0.7, 0.2))
ax3primary.xaxis.grid(color='green')

INDEX = t[np.where(abs(s3-0.2) < 0.005)[0]]
INDEX = np.append(INDEX, LAST)
INDEX = np.insert(INDEX, 0, FIRST)

ax1secondary = ax1primary.twiny()
ax1secondary.set_xticks(INDEX)
ax1secondary.xaxis.grid(color='red')

ax2secondary = ax2primary.twiny()
ax2secondary.set_xticks(INDEX)
ax2secondary.xaxis.grid(color='red')

ax3secondary = ax3primary.twiny()
ax3secondary.set_xticks(INDEX)
ax3secondary.xaxis.grid(color='red')

plt.tight_layout()
plt.subplots_adjust(hspace=0)

for ax in [ax1primary, ax2primary, ax2secondary, ax3secondary]:
    plt.setp(ax.get_xticklabels(), visible=False)

###############################################################################

plt.show()

On a static figure there is no issue. The problem becomes obvious when you start panning (or zooming) one of the subplots. The primary (green) axis stays perfectly in sync and moves within all subplots, but the secondary (red) axis gets misaligned and moves only within the active subplot.

Is there a way to fix this?


The behavior that I want to achieve is following:

I need one common "primary" x-axis (for all three subplots) with the ticks on the bottom of the figure and another common "secondary" x-axis (for all three subplots) with the ticks on the top of the figure. The primary axis is a standard regularly spaced axis, while the secondary axis shows the customized ticks (for example zero crossings) This is all satisfied in the example above. Now I need it to be satisfied also while panning and zooming subplots.

2条回答
smile是对你的礼貌
2楼-- · 2020-03-30 05:27

Thanks for clarifying your question. The intended use of twiny is to create a second fully independent x-axis with its own scale and offset, which you would then plot into. However in your case you are only using the secondary x-axis created by twiny as a way to display a second set of custom x ticks, and you want this axis to always have exactly the same scale and offset as the parent x-axis.

One approach would be to create a callback that updates the limits of the secondary axis whenever the primary axis gets panned:

from matplotlib.backend_bases import NavigationToolbar2

parents = [ax1primary, ax2primary, ax3primary]
children = [ax1secondary, ax2secondary, ax3secondary]

def callback(event=None):
    # return immediately if the figure toolbar is not in "navigation mode"
    if not isinstance(parents[0].figure.canvas.manager.toolbar,
                      NavigationToolbar2):
        return
    for parent, child in zip(parents, children):
        child.set_xlim(*parent.get_xlim())
        child.set_ylim(*parent.get_ylim())

# connect the callback to the figure canvas
fig3.canvas.mpl_connect('motion_notify_event', callback)
查看更多
甜甜的少女心
3楼-- · 2020-03-30 05:39

Unfortunately the proposed callback solution is not robust enough. Most of the time panning works fine, but zooming is a disaster. Still, too often the grids get misaligned.

Until I find out how to improve the callback solution, I decided to code a custom grid and annotate the values within the plot.

#!/usr/bin/python

# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt

FIRST = 0.0
LAST  = 2.0
STEP  = 0.01

t = np.arange(FIRST, LAST, STEP)

s1 = np.sin(2*np.pi*t)
s2 = np.exp(-t)
s3 = s1*s2

###############################################################################

plt.rc('axes', grid=True)
plt.rc('grid', color='0.75', linestyle='-', linewidth=0.5)

fig3 = plt.figure()
ax1primary = plt.subplot2grid((4,3), (0,0), colspan=3, rowspan=2)
ax2primary = plt.subplot2grid((4,3), (2,0), colspan=3, sharex=ax1primary)
ax3primary = plt.subplot2grid((4,3), (3,0), colspan=3, sharex=ax1primary)

ax1primary.plot(t,s1)
ax1primary.set_yticks(np.arange(-0.9, 1.0, 0.3))
ax1primary.xaxis.grid(color='green')
ax1primary.set_ylim(-1, 1)

ax2primary.plot(t[:150],s2[:150])
ax2primary.set_yticks(np.arange(0.3, 1, 0.2))
ax2primary.xaxis.grid(color='green')
ax2primary.set_ylim(0.2, 1)

ax3primary.plot(t[30:],s3[30:])
ax3primary.plot([0,2],[0.2,0.2],'m')
ax3primary.set_yticks(np.arange(-0.4, 0.7, 0.2))
ax3primary.xaxis.grid(color='green')
ax3primary.set_ylim(-0.6, 0.8)

INDEX = np.where(abs(s3-0.2) < 0.005)[0]

for i in range(0, len(INDEX)):
    ax1primary.annotate(t[INDEX[i]], xy=(t[INDEX[i]], 0))

ax1primary.plot([t[INDEX], t[INDEX]], [-1e9 * np.ones(len(INDEX)), 1e9 * np.ones(len(INDEX))], 'r')
ax2primary.plot([t[INDEX], t[INDEX]], [-1e9 * np.ones(len(INDEX)), 1e9 * np.ones(len(INDEX))], 'r')
ax3primary.plot([t[INDEX], t[INDEX]], [-1e9 * np.ones(len(INDEX)), 1e9 * np.ones(len(INDEX))], 'r')

plt.tight_layout()
plt.subplots_adjust(hspace=0)

for ax in [ax1primary, ax2primary]:
    plt.setp(ax.get_xticklabels(), visible=False)

###############################################################################

plt.show()
查看更多
登录 后发表回答