2012-05-07 27 views
16

Tôi cần hai lớp phủ hai tập dữ liệu với các thang đo trục Y khác nhau trong Matplotlib. Dữ liệu chứa cả giá trị dương và âm. Tôi muốn hai trục chia sẻ một nguồn gốc, nhưng Matplotlib không căn chỉnh hai vảy theo mặc định.Trục Matplotlib với hai vảy chia sẻ nguồn gốc

import numpy as np 
import matplotlib.pyplot as plt 

fig = plt.figure() 
ax1 = fig.add_subplot(111) 
ax2 = ax1.twinx() 

ax1.bar(range(6), (2, -2, 1, 0, 0, 0)) 
ax2.plot(range(6), (0, 2, 8, -2, 0, 0)) 
plt.show() 

Tôi cho rằng chúng ta có thể thực hiện một số tính toán với .get_ylim().set_ylim() hai class hai thang. Có giải pháp dễ dàng hơn không?

Output from the sample above

Trả lời

27

sử dụng align_yaxis() chức năng:

import numpy as np 
import matplotlib.pyplot as plt 

def align_yaxis(ax1, v1, ax2, v2): 
    """adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1""" 
    _, y1 = ax1.transData.transform((0, v1)) 
    _, y2 = ax2.transData.transform((0, v2)) 
    inv = ax2.transData.inverted() 
    _, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2)) 
    miny, maxy = ax2.get_ylim() 
    ax2.set_ylim(miny+dy, maxy+dy) 


fig = plt.figure() 
ax1 = fig.add_subplot(111) 
ax2 = ax1.twinx() 

ax1.bar(range(6), (2, -2, 1, 0, 0, 0)) 
ax2.plot(range(6), (0, 2, 8, -2, 0, 0)) 

align_yaxis(ax1, 0, ax2, 0) 
plt.show() 

enter image description here

14

Để đảm bảo rằng các y-giới hạn được duy trì (vì vậy không có điểm dữ liệu được chuyển ra khỏi cốt truyện) và để cân bằng điều chỉnh cả hai trục y, tôi đã thực hiện một số bổ sung cho câu trả lời của @ HYRY:

def align_yaxis(ax1, v1, ax2, v2): 
    """adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1""" 
    _, y1 = ax1.transData.transform((0, v1)) 
    _, y2 = ax2.transData.transform((0, v2)) 
    adjust_yaxis(ax2,(y1-y2)/2,v2) 
    adjust_yaxis(ax1,(y2-y1)/2,v1) 

def adjust_yaxis(ax,ydif,v): 
    """shift axis ax by ydiff, maintaining point v at the same location""" 
    inv = ax.transData.inverted() 
    _, dy = inv.transform((0, 0)) - inv.transform((0, ydif)) 
    miny, maxy = ax.get_ylim() 
    miny, maxy = miny - v, maxy - v 
    if -miny>maxy or (-miny==maxy and dy > 0): 
     nminy = miny 
     nmaxy = miny*(maxy+dy)/(miny+dy) 
    else: 
     nmaxy = maxy 
     nminy = maxy*(miny+dy)/(maxy+dy) 
    ax.set_ylim(nminy+v, nmaxy+v) 
3

@ câu trả lời drevicko của thất bại cho tôi khi vẽ hai chuỗi sau điểm:

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) 

enter image description here

... vì vậy đây là phiên bản của tôi:

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]) 

Có về nguyên tắc vô hạn khác nhau khả năng sắp xếp các số không (hoặc các giá trị khác, mà các giải pháp được cung cấp khác chấp nhận): bất cứ nơi nào bạn đặt 0 trên trục y, bạn có thể thu phóng từng chuỗi trong số hai chuỗi sao cho phù hợp. Chúng tôi chỉ chọn vị trí như vậy, sau khi chuyển đổi, hai bao gồm một khoảng thời gian theo chiều dọc cùng chiều cao. Hoặc nói cách khác, chúng tôi giảm thiểu chúng cùng một yếu tố so với âm mưu không liên kết. (Điều này không không có nghĩa là 0 là một nửa của cốt truyện: điều này sẽ xảy ra ví dụ như nếu một cốt truyện là tất cả tiêu cực và khác đều tích cực.)

NumPy phiên bản:

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

tôi đã nấu chín lên một giải pháp bắt đầu từ trên đó sẽ sắp xếp bất kỳ số lượng trục:

def align_yaxis_np(axes): 
    """Align zeros of the two axes, zooming them out by same ratio""" 
    axes = np.array(axes) 
    extrema = np.array([ax.get_ylim() for ax in axes]) 

    # reset for divide by zero issues 
    for i in range(len(extrema)): 
     if np.isclose(extrema[i, 0], 0.0): 
      extrema[i, 0] = -1 
     if np.isclose(extrema[i, 1], 0.0): 
      extrema[i, 1] = 1 

    # upper and lower limits 
    lowers = extrema[:, 0] 
    uppers = extrema[:, 1] 

    # if all pos or all neg, don't scale 
    all_positive = False 
    all_negative = False 
    if lowers.min() > 0.0: 
     all_positive = True 

    if uppers.max() < 0.0: 
     all_negative = True 

    if all_negative or all_positive: 
     # don't scale 
     return 

    # pick "most centered" axis 
    res = abs(uppers+lowers) 
    min_index = np.argmin(res) 

    # scale positive or negative part 
    multiplier1 = abs(uppers[min_index]/lowers[min_index]) 
    multiplier2 = abs(lowers[min_index]/uppers[min_index]) 

    for i in range(len(extrema)): 
     # scale positive or negative part based on which induces valid 
     if i != min_index: 
      lower_change = extrema[i, 1] * -1*multiplier2 
      upper_change = extrema[i, 0] * -1*multiplier1 
      if upper_change < extrema[i, 1]: 
       extrema[i, 0] = lower_change 
      else: 
       extrema[i, 1] = upper_change 

     # bump by 10% for a margin 
     extrema[i, 0] *= 1.1 
     extrema[i, 1] *= 1.1 

    # set axes limits 
    [axes[i].set_ylim(*extrema[i]) for i in range(len(extrema))] 

dụ: aligned axes

Các vấn đề liên quan