Matplotlib axis with two scales shared origin

前端 未结 7 1337
忘了有多久
忘了有多久 2020-11-29 04:17

I need two overlay two datasets with different Y-axis scales in Matplotlib. The data contains both positive and negative values. I want the two axes to share one origin, but

相关标签:
7条回答
  • 2020-11-29 04:51

    @drevicko's answer fails for me when plotting the following two sequences of points:

    l1 = [0.03, -0.6, 1, 0.05]
    l2 = [0.8,  0.9,  1,  1.1]
    fig, ax1 = plt.subplots()
    ax1.plot(l1)
    ax2 = ax1.twinx()
    ax2.plot(l2, color='r')
    align_yaxis(ax1, 0, ax2, 0)
    

    ... so here's my version:

    def align_yaxis(ax1, ax2):
        """Align zeros of the two axes, zooming them out by same ratio"""
        axes = (ax1, ax2)
        extrema = [ax.get_ylim() for ax in axes]
        tops = [extr[1] / (extr[1] - extr[0]) for extr in extrema]
        # Ensure that plots (intervals) are ordered bottom to top:
        if tops[0] > tops[1]:
            axes, extrema, tops = [list(reversed(l)) for l in (axes, extrema, tops)]
    
        # How much would the plot overflow if we kept current zoom levels?
        tot_span = tops[1] + 1 - tops[0]
    
        b_new_t = extrema[0][0] + tot_span * (extrema[0][1] - extrema[0][0])
        t_new_b = extrema[1][1] - tot_span * (extrema[1][1] - extrema[1][0])
        axes[0].set_ylim(extrema[0][0], b_new_t)
        axes[1].set_ylim(t_new_b, extrema[1][1])
    

    There are in principle infinite different possibilities to align the zeros (or other values, which the other provided solutions accept): wherever you place zero on the y axis, you can zoom each of the two series so that it fits. We just pick the position such that, after the transformation, the two cover a vertical interval of same height. Or in other terms, we minimize them of a same factor compared to the non-aligned plot. (This does not mean that 0 is at half of the plot: this will happen e.g. if one plot is all negative and the other all positive.)

    Numpy version:

    def align_yaxis_np(ax1, ax2):
        """Align zeros of the two axes, zooming them out by same ratio"""
        axes = np.array([ax1, ax2])
        extrema = np.array([ax.get_ylim() for ax in axes])
        tops = extrema[:,1] / (extrema[:,1] - extrema[:,0])
        # Ensure that plots (intervals) are ordered bottom to top:
        if tops[0] > tops[1]:
            axes, extrema, tops = [a[::-1] for a in (axes, extrema, tops)]
    
        # How much would the plot overflow if we kept current zoom levels?
        tot_span = tops[1] + 1 - tops[0]
    
        extrema[0,1] = extrema[0,0] + tot_span * (extrema[0,1] - extrema[0,0])
        extrema[1,0] = extrema[1,1] + tot_span * (extrema[1,0] - extrema[1,1])
        [axes[i].set_ylim(*extrema[i]) for i in range(2)]
    
    0 讨论(0)
提交回复
热议问题