LoginSignup
1
0

More than 3 years have passed since last update.

【Python】縦軸が左右交互に入れ替わる折れ線グラフ

Last updated at Posted at 2019-05-09

タイトルだと分かりにくいので例:
キャプチャ.PNG

基本的なアイデア:
- axesの位置を適宜計算,軸を互い違いにする利点を生かすため若干重複させる(overlap_ratio)
- 不要な軸は消去,背景も透明に(そのままだと重なって表示される)

axis_left_right.py
import numpy as np
import matplotlib.pyplot as plt

def calc_axes_size(ax_num=1, overlap_ratio=0.0, height_all=0.8):
    return height_all / (ax_num - (ax_num - 1) * overlap_ratio)


def plot_alternately(fig, ys, colors=None, options=None,
                     overlap_ratio=0.0,
                     left_all=0.125, bottom_all=0.1, width_all=0.775, height_all=0.8):
    n = len(ys)
    ax_size = calc_axes_size(n, overlap_ratio, height_all)
    if colors is None:
        colors = ['k'] * n
    if options is None:
        options = [{}] * n

    for i, (y, color, option) in enumerate(zip(ys, colors, options)):
        bottom = (1.0 - overlap_ratio) * ax_size * (n - i - 1) + bottom_all
        ax = plt.axes((left_all, bottom, width_all, ax_size))
        ax.set_facecolor((1.0, 1.0, 1.0, 0.0)) # (R,G,B,alpha)

        left, right = True, False
        if i % 2 == 1: # right axis, ax.twinx() does not work
            left, right = False, True

        top = False
        if i == 0:
            top = True

        bottom = False
        if i + 1 == n:
            bottom = True

        # delete axis
        ax.tick_params(labelbottom=bottom, labelleft=left, labelright=right, labeltop=top,
                       bottom     =bottom, left     =left, right     =right, top     =top)
        ax.spines['left'  ].set_visible(left)
        ax.spines['right' ].set_visible(right)
        ax.spines['top'   ].set_visible(top)
        ax.spines['bottom'].set_visible(bottom)

        # change color
        ax.spines['left'  ].set_color(color)
        ax.spines['right' ].set_color(color)
        ax.tick_params(axis='y', colors=color)

        ax.plot(y, color=color, **option)
    plt.show()

上図を生成したコード

def make_test_data(x, seed=0):
    '''
    y = sin(x) + ax + b, 0 < a, b < 1
    '''
    np.random.seed(seed=seed)
    return np.sin(x) + 0.05 * np.random.rand() * x + np.random.rand(*x.shape)


def main(*args):
    plot_length = 100
    plot_number = 4
    x  = np.arange(plot_length)
    ys = [make_test_data(x, i) for i in range(plot_number)]

    cs   = ['r', 'b', 'y', 'g']
    lss  = ['solid', 'dashed', 'dashdot', 'dotted']
    opts = [{'linestyle':ls} for ls in lss]

    fig = plt.figure(figsize=(6, 4))
    plot_alternately(fig, ys, cs, opts, 0.25)
    return

axesの位置の計算を外に出すと,より柔軟に図の操作が可能になる.
例えば,2本の折れ線グラフを1組と見て,組内の重なりと組間の重なりを変える:


def calc_axes_size1(ax_num=1, overlap_ratio=0.0, height_all=0.8):
    return height_all / (ax_num - (ax_num - 1) * overlap_ratio)


def calc_axes_bottoms1(ax_num=2, overlap_ratio=0.0, height_all=0.8):
    ax_size = calc_axes_size(ax_num, overlap_ratio, height_all)
    return [(1.0 - overlap_ratio) * ax_size * (ax_num - i - 1) for i in range(ax_num)]


def calc_axes_size2(ax_num=2, overlap_ratio1=0.0, overlap_ratio2=0.0, height_all=0.8):
    if ax_num % 2 == 1:
        print 'calc_axes_positions ERROR: ax_num =', ax_num
        sys.exit()
    ol_num1 = ax_num / 2
    ol_num2 = (ax_num + 1) / 2 - 1
    return height_all / (ax_num - ol_num1 * overlap_ratio1 - ol_num2 * overlap_ratio2)


def calc_axes_bottoms2(ax_num=2, overlap_ratio1=0.0, overlap_ratio2=0.0, height_all=0.8):
    ax_size = calc_axes_size2(ax_num, overlap_ratio1, overlap_ratio2, height_all)

    bottoms = []
    for i in range(1, ax_num+1):
        ol_num1 = i / 2
        ol_num2 = (i + 1) / 2 - 1
        bottom = height_all - ax_size * (i - overlap_ratio1 * ol_num1 - overlap_ratio2 * ol_num2)
        bottoms.append(bottom)
    return bottoms

# ---------------------------------------------------------------------------------------------------
def plot_alternately(fig, ys, colors=None, options=None,
                     overlap_ratio=0.0,
                     left_all=0.125, bottom_all=0.1, width_all=0.775, height_all=0.8):
    n = len(ys)
    if len(overlap_ratio) == 1:
        ax_size = calc_axes_size1   (n, overlap_ratio[0], height_all)
        bottoms = calc_axes_bottoms1(n, overlap_ratio[0], height_all)
    elif len(overlap_ratio) == 2:
        ax_size = calc_axes_size2   (n, overlap_ratio[0], overlap_ratio[1], height_all)
        bottoms = calc_axes_bottoms2(n, overlap_ratio[0], overlap_ratio[1], height_all)

    if colors is None:
        colors = ['k'] * n
    if options is None:
        options = [{}] * n

    for i, (y, bottom, color, option) in enumerate(zip(ys, bottoms, colors, options)):
        ax = plt.axes((left_all, bottom + bottom_all, width_all, ax_size))
        # 後は同じ

キャプチャ.PNG

用途例:複数都市の気温と降水量(もっと正の相関がある変数の方がズラして描く意味がある?)
subplotで良い気もする.一般化は植木算を頑張る.

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0