2019年3月13日 星期三

Python Plot 雙Y軸對齊任一值(如:對齊0)

用Plot做圖時,覺得雙Y軸對齊會好看多了,但是網路上沒有令人滿意的正解,最接近的是找到這一篇《Python - dual y axis chart, align zero》。

仔細詳讀,可能是個人理解能力衰退了,也不明白是如何運算,直接套用也會讓部分數據被新設的最大值或最小值被切掉。

回到數學,要左右Y軸對齊在某點上,首先要讓第一軸的V1點對到第二軸的V2點出發,之後再做延伸。


接下來,選一軸(通常是AX1)為主,依照AX1對應V1的比例,以V2為中心點,以AX1的比例延伸直到涵蓋AX2的原範圍,確保數據能不被切除。

不過,在此情況下,V1對到AX1最大或最小值,調整AX2會是無解的。此時換成去調整AX1。若再調整AX1也無解,可能是無法對齊或是不需要對齊。

最後,若V1不在AX1內,圖表的Y軸當然不會顯示V1,自然也無法對齊。所以在計算之前,得先確保V1與V2都出現在AX1與AX2內。


def align_yaxis(ax1, v1, ax2, v2):
    # 取得AX1和AX2的範圍
    miny1 , maxy1 = ax1.get_ylim()
    miny2 , maxy2 = ax2.get_ylim()
    print("調整前", miny1, maxy1, miny2, maxy2)
    # 確保 v1 一定出現在AX1
    if v1 > maxy1: maxy1 = v1
    elif v1 < miny1: miny1 = v1
    # 確保 v2 一定出現在AX2
    if v2 > maxy2: maxy2 = v2
    elif v2 < miny2: miny2 = v2
    # 與V上下距離
    upy1 = abs(maxy1 - v1)
    downy1 = abs(v1 - miny1)
    upy2 = abs(maxy2 - v2)
    downy2 = abs(v2 - miny2)
    print("調整中", miny1, maxy1, miny2, maxy2)
    # 有解
    if upy1 > 0 and downy1 > 0:
        up_ratio = upy2 / upy1
        down_ratio = downy2 / downy1
        # 調整AX2
        if up_ratio > down_ratio: # 如果上方需延伸較多,則對齊最大值,依上方比例去調整最小值。
            miny2 = v2 - downy1 * up_ratio
        elif down_ratio > up_ratio:
            maxy2 = v2 + upy1 * down_ratio
        else:
            pass
    # 調整AX2無解,則調整AX1。注意比例是AX2/AX1,故調整AX1時,調整比例需要變成倒數。
    elif upy2 > 0 and downy2 > 0:
        #因為upy1或downy2為0才會來到這裡,若除其中一項將會發生除零的錯誤。
        up_ratio = upy1 / upy2
        down_ratio =  downy1 / downy2
        if up_ratio > down_ratio:
            miny1 = v1 - downy2 * up_ratio
        elif down_ratio > up_ratio:
            maxy1 = v1 + upy2 * down_ratio
        else:
            pass
    # 調整AX1與AX2都無解,則不調整
    else:
        print("無法調整或不需調整")
        pass
    # 重新調整AX1和AX2
    print("調整後",miny1, maxy1, miny2, maxy2)
    ax1.set_ylim(miny1, maxy1)
    ax2.set_ylim(miny2, maxy2)
    return None