matplotlib中具有共享双轴(和轴箭头)的交互式子图?

时间:2019-10-16 09:14:00

标签: python matplotlib

我已经检查了以下相关问题:

...不幸的是,我无法真正使用它们来找到解决问题的方法:

因此,我有两个函数,我想在每个子图中绘制它们-但是,它们应该有一个共享的x轴。但是,我也想要一个双x轴-在一种情况下,x轴代表百分比(从0到100);在另一种情况下,x轴表示相同的值,表示为16位整数(相应的范围是从0到65535)。最后,我要在子图中绘制箭头。

通读SO上的示例,我几乎可以做到这一点-除非当我开始使用交互式绘图时,一旦我开始移动或缩放绘图,就可以做到:

  • 在交互式绘图中移动/缩放时,两个x轴上的标签/值不同步(只有%轴会根据缩放的变化而变化,16位的值与实例化时相同)
  • 箭头可以缩放和移动-即使我只是希望它们保持在各自的图线边界处。

有什么方法可以做到这一点?对于下面粘贴的代码(Windows 10上的MSYS2 / MINGW64上的Matplotlib 3.1.1,Python 3.7.4),这是我另外得到的动画gif的结果:

Figure_1

代码是这样的:

#!/usr/bin/env python

import matplotlib
print("matplotlib.__version__ {}".format(matplotlib.__version__))
import matplotlib.pyplot as plt
import numpy as np

# https://stackoverflow.com/questions/33737736/matplotlib-axis-arrow-tip
def arrowed_spines(fig, ax):

  xmin, xmax = ax.get_xlim()
  ymin, ymax = ax.get_ylim()

  ## removing the default axis on all sides:
  #for side in ['bottom','right','top','left']:
  #  ax.spines[side].set_visible(False)
  #
  ## removing the axis ticks
  #plt.xticks([]) # labels
  #plt.yticks([])
  #ax.xaxis.set_ticks_position('none') # tick markers
  #ax.yaxis.set_ticks_position('none')

  # get width and height of axes object to compute
  # matching arrowhead length and width
  dps = fig.dpi_scale_trans.inverted()
  bbox = ax.get_window_extent().transformed(dps)
  width, height = bbox.width, bbox.height

  # manual arrowhead width and length
  hw = 1./20.*(ymax-ymin)
  hl = 1./20.*(xmax-xmin)
  lw = 1. # axis line width
  ohg = 0.3 # arrow overhang

  # compute matching arrowhead length and width
  yhw = hw/(ymax-ymin)*(xmax-xmin)* height/width
  yhl = hl/(xmax-xmin)*(ymax-ymin)* width/height

  # draw x and y axis
  # for some reason, for a straight line here at ymin, the 4th arg needs to be 0, not y?!
  ax.arrow(xmin, ymin, xmax-xmin, 0, fc='k', ec='k', lw = lw,
       head_width=hw, head_length=hl, overhang = ohg,
       length_includes_head= True, clip_on = False)

  ax.arrow(0, ymin, 0., ymax-ymin, fc='k', ec='k', lw = lw,
       head_width=yhw, head_length=yhl, overhang = ohg,
       length_includes_head= True, clip_on = False)


def main():
  xdata = np.arange(0, 101, 1) # 0 to 100, both included
  ydata1 = np.sin(0.01*xdata*np.pi/2)
  ydata2 = 10*np.sin(0.01*xdata*np.pi/4)

  fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(9, 6), dpi=120, gridspec_kw={'height_ratios': [2, 1]})
  fig.subplots_adjust(hspace=0)

  pl21, = ax1.plot(xdata, ydata1, color="Red")
  pl22, = ax2.plot(xdata, ydata2, color="Khaki")

  # zeroline
  ax1.axhline(y=0, color='gray', linestyle='-', linewidth=0.5)

  arrowed_spines(fig, ax1)
  arrowed_spines(fig, ax2)

  ax2.set_xlim(0, 100)
  ax2.set_xlabel('IN [%]', fontsize = 9)
  ax2.xaxis.set_label_coords(0.5, -0.1)

  # https://stackoverflow.com/questions/31803817/how-to-add-second-x-axis-at-the-bottom-of-the-first-one-in-matplotlib
  ax22 = ax2.twiny() # instantiate a second axes that shares the same y-axis
  # ax2.get_shared_x_axes().join(ax2, ax22) # this kills the plotting entirely; https://stackoverflow.com/questions/42973223/
  ax22.set_xlim(0, 65535)
  # Move twinned axis ticks and label from top to bottom
  ax22.xaxis.set_ticks_position("bottom")
  ax22.xaxis.set_label_position("bottom")
  # Offset the twin axis below the host
  ax22.spines["bottom"].set_position(("axes", -0.2))
  # Turn on the frame for the twin axis, but then hide all
  # but the bottom spine
  ax22.set_frame_on(True)
  ax22.patch.set_visible(False)
  ax22.spines["bottom"].set_visible(True)
  ax22.set_xlabel('IN [16-bit]', fontsize = 9)
  ax22.xaxis.set_label_coords(0.5, -0.34)

  plt.show()

# ENTRY POINT
if __name__ == '__main__':
  main()

0 个答案:

没有答案