基本的なアイデア:
- 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))
# 後は同じ
用途例:複数都市の気温と降水量(もっと正の相関がある変数の方がズラして描く意味がある?)
subplotで良い気もする.一般化は植木算を頑張る.