matplotlib缩放y轴

时间:2018-04-17 12:29:49

标签: matplotlib plot

我正在使用matplotlib在单个图中显示多个数据集。我想为辅助数据设置多个“外部”y轴,并希望这些轴具有相同的范围,但缩放比主曲线轴的缩放更短。

Current plot

Desired plot

我正在使用twinx()创建其他y轴,如this post

中所述
ax = day6Si.plot( 
  'time', 'pce_rolling'
)

dataPsc[ dataPsc.day == 5 ].plot( 
  'time', 'pce_rolling', ax = ax 
)

ax3 = ax.twinx()
ax3.fill_between( 
  day6Si.time, 0, day6Si.temperature
)

rspine = ax3.spines[ 'right' ]
rspine.set_position( ( 'axes', 1.15 ) )
ax3.set_frame_on( True )
ax3.patch.set_visible( False )
ax3.set_ylim( bottom = 0 )


ax2 = ax.twinx()
ax2.fill_between( 
  day6Si.time, 0, day6Si.intensity
)

rspine = ax2.spines[ 'right' ]
rspine.set_position( ( 'axes', 1.05 ) )
ax2.set_frame_on( True )
ax2.patch.set_visible( False )
ax2.set_ylim( bottom = 0 )

MCV示例

td = np.linspace( 0, np.pi, 101 )
df = pd.DataFrame( data = {
  'main': np.sin( td ),
  'secondary': 10 * np.sin( td )
} )

ax = df.plot( df.index.values, 'main' )

ax2 = ax.twinx()
ax2.fill_between( 
  df.index.values, 0, df.secondary, 
  facecolor = '#a05050', 
  label = 'Secondary'
)

rspine = ax2.spines[ 'right' ]
rspine.set_position( ( 'axes', 1.05 ) )
ax2.set_frame_on( True )
ax2.patch.set_visible( False )
ax2.set_ylim( bottom = 0 )
ax2.set_ylabel( 'Secondary' )

ax.set_ylim( 0, 1 )
ax.legend( [ 'Main', 'Secondary' ] )

1 个答案:

答案 0 :(得分:0)

完成这项工作的关键是操纵每个轴的Bbox。该解决方案构建了this advice for creating multiple y axes

mpl = matplotlib,np = numpy,pd = pandas

  1. 创建主轴ax

  2. 使用ax.get_position()获取Bbox个轴,然后使用axpos获取边界点get_points()

  3. 使用ax.twinx()创建辅助轴ax2

  4. 使用ax2ax2posmpl.transforms.Bbox( np.copy( axpos ) )创建Bbox。请务必创建原始Bbox点的副本,否则两者都将被修改。

  5. 缩放辅助边界框。这可以通过设置水平缩放的x1属性或垂直缩放的y1属性来完成。 (例如ax2pos.y1 = 0.5* ax2pos.y1

  6. 使用set_position()设置辅助轴的边界框。

  7. 使用ax2.spines[ 'top' ].set_visible( False )关闭顶部(或右侧,水平缩放)spine

  8. 您可能还需要考虑:

    • 切换每个轴patch,以便不使用ax.patch.set_visible()在以前的图层上绘画。

    • 使用ax.set_zorder()调整轴的绘制顺序。

    • 由于缩放,您可能需要调整保存图形时使用的边界框。这可以通过创建另一个Bbox并将其作为bbox_inches参数传递给savefig()来完成。

    MCV解决方案

    td = np.linspace( 0, np.pi, 101 )
    df = pd.DataFrame( data = {
      'main': np.sin( td ),
      'secondary': 10 * np.sin( td )
    } )
    
    ax = df.plot( df.index.values, 'main' )
    axpos = ax.get_position()
    
    ax2 = ax.twinx()
    ax2pos = mpl.transforms.Bbox( np.copy( axpos.get_points() ) ) # clone bounding box
    ax2pos.y1 = ax2pos.y1 * 0.5 # scale y axis
    ax2.set_position( ax2pos ) # set bounding box
    
    ax2.fill_between( 
      df.index.values, 0, df.secondary, 
      facecolor = '#a05050', 
      label = 'Secondary'
    )
    
    ax2.spines[ 'right' ].set_position( ( 'axes', 1.05 ) )
    ax2.spines[ 'top' ].set_visible( False )
    ax2.set_frame_on( True )
    ax2.patch.set_visible( False )
    ax2.set_ylim( bottom = 0 )
    ax2.set_ylabel( 'Secondary' )
    
    ax.set_ylim( 0, 1 )
    ax.legend( [ 'Main', 'Secondary' ] )
    

    完整代码解决方案

    ax = day6Si.plot( 
       'time', 'pce_rolling', kind = 'line', label = 'Si',
        zorder = 40
    )
    
    dataPsc[ dataPsc.day == 5 ].plot( 
        'time', 'pce_rolling', kind = 'line', label = 'PSC',
        zorder = 30, ax = ax 
    )
    
    axpos = ax.get_position()
    ax.set_zorder( 30 )
    ax.patch.set_visible( False )
    
    ax.set_xlim( 124.5, 141.5 )
    ax.set_ylim( 10, 20 )
    ax.set_ylabel( 'PCE (%)' )
    ax.set_xlabel( 'Time' )
    ax.set_xticklabels( [ '4', '6', '8', '10', '12', '14', '16', '18', '20' ] )
    ax.legend( loc = 'upper right', bbox_to_anchor = ( 1, 1 ) )
    
    # temperature
    ax3 = ax.twinx()
    ax3.fill_between( 
        day6Si.time, 0, day6Si.temperature, 
        facecolor = '#f0c5b5',
        label = 'Temperature (Rel)'
    )
    
    ax3pos = mpl.transforms.Bbox( np.copy( axpos.get_points() ) ) # clone bounding box
    ax3pos.y1 = ax3pos.y1 * 0.5 # scale y axis
    ax3.set_position( ax3pos ) # set bounding box
    
    ax3.set_zorder( 10 )
    ax3.spines[ 'right' ].set_position( ( 'axes', 1.025 ) ) # shift y axis
    ax3.set_frame_on( True )
    ax3.spines[ 'top' ].set_visible( False ) # remove top frame line
    ax3.patch.set_visible( True )
    ax3.set_ylim( bottom = 0, top = 60 )
    ax3.set_ylabel( 'Temperature (C)' )
    
    # intensity
    ax2 = ax.twinx()
    ax2.fill_between( 
        day6Si.time, 0, day6Si.intensity, 
        facecolor = '#dddd99',
        label = 'Intensity (Rel)'
    )
    
    ax2pos = mpl.transforms.Bbox( np.copy( axpos.get_points() ) ) # clone bounding box
    ax2pos.y1 = ax2pos.y1 * 0.33 # scale y axis
    ax2.set_position( ax2pos ) # set bounding box
    
    ax2.set_zorder( 20 )
    ax2.spines[ 'right' ].set_position( ( 'axes', 1.125 ) ) # shift y asix
    ax2.set_frame_on( True )
    ax2.spines[ 'top' ].set_visible( False ) # remove top frame
    ax2.patch.set_visible( False )
    ax2.set_ylim( bottom = 0, top = 1 )
    ax2.set_ylabel( 'Intensity (suns)' )
    
    savebox = mpl.transforms.Bbox( [ [ 0, 0 ], [ 10* 1.15, 8 ] ] ) # bounding box in inches for saving
    plt.gcf().savefig( figloc + '/day6.svg', format = 'svg', bbox_inches = savebox )
    

    Result

    <强>更新

    由于matplotlib库中的更新,必须进行一些小的更改才能实现。执行ax.twinx()不再允许您控制第二个轴,因此必须手动将其添加到图中。

    mpl = matplotlib,plt = matplotlib.pyplot,np = numpy,pd = pandas

    1. 使用fig

    2. 创建主要人物ax和轴plt.subplots()
    3. 在步骤2,4和5中像以前一样创建Bbox。

    4. 使用ax2a创建辅助轴fig.add_axes(),并使用所需的边界框。

    5. 通过联合ax2aax2a.twinx()创建正确的y轴。

    6. 清理辅助轴。

    7. 您可能还需要考虑使用ax.set_xlim()对齐它们来对齐主轴和副轴的x轴。

      MCV解决方案

      td = np.linspace( 0, np.pi, 101 )
      df = pd.DataFrame( data = {
        'main': np.sin( td ),
        'secondary': 10 * np.sin( td )
      } )
      
      fig, ax = plt.subplots()
      
      df.plot( df.index.values, 'main', ax = ax )
      axpos = ax.get_position()
      
      ax2pos = mpl.transforms.Bbox( np.copy( axpos.get_points() ) )
      ax2pos.y1 = ax2pos.y1 * 0.5 # scale y axis
      
      ax2a = fig.add_axes( ax2pos ) # create secondary axes
      ax2 = ax2a.twinx() # create right y-axis
      
      ax2.fill_between( 
        df.index.values, 0, df.secondary, 
        facecolor = '#a05050', 
        label = 'Secondary'
      )
      
      ax2.spines[ 'right' ].set_position( ( 'axes', 1.05 ) )
      ax2.spines[ 'top' ].set_visible( False )
      ax2.set_frame_on( True )
      ax2.patch.set_visible( False )
      ax2.set_ylim( bottom = 0 )
      ax2.set_ylabel( 'Secondary' )
      
      # clean up secondary axes tick marks and labels
      ax2a.tick_params( left = False, bottom = False )
      ax2.tick_params( left = False, bottom = False )
      ax2a.set_xticklabels( [] )
      ax2a.set_yticklabels( [] )
      
      ax.set_ylim( 0, 1 )
      ax.legend( [ 'Main', 'Secondary' ] )