Help us understand the problem. What is going on with this article?

複数のグラフを自由に配置する

More than 1 year has passed since last update.

1. この記事について

Jupyter Notebook上で、matplotlibを使ってグラフを描く機会があったので、その時に調べたことを備忘録として記録しておきます。

具体的には下図のようなグラフを描く手順についての説明になります。
download.png

このグラフの特徴としては次のような点が挙げられます。

  • 3つのグラフが1つのplt.figure上に描かれている
  • 3つのグラフの大きさ(幅)は均等ではない
  • 3つのグラフは同じデータを表していて、左は全体、残りの2つは一部を拡大したものになっている
  • 描画の手順は、波形データを1本分読み込んでは3つのグラフに描画して、次の波形を読み込んで...という順番

これらを実現するために

  • matplotlib.gridspecを使用します
  • plt.subplot2grid(...とは書かず、fig.add_subplot(...と書きます

というところがポイントです。

2. 共通データの準備

今回のグラフの描画に特有のデータを準備する手順になりますので、あまり参考になりませんが一応載せておきます。

import pandas as pd
import numpy as np
import os
import glob
import pprint
import math
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec, GridSpecFromSubplotSpec
sns.set(style="whitegrid")

# 周波数軸を作る
freq = np.arange(1., 10.01, 9/400)
df_freq = pd.DataFrame({'Freq': freq})

# TestLimit
lim_home_x1 = np.array([7.345, 7.345, 7.7, 7.7])
lim_home_y1 = [500, 250, 179, 500]
lim_home_x2 = np.array([7.899, 7.899, 8.3, 8.3])
lim_home_y2 = [300, 90, 90, 300]
lim_home_x1l = np.array([1.798, 1.798, 2.048, 2.048])
lim_home_y1l = [500, 250, 230, 500]

3. 一度描画したグラフに追記しない場合

1つのグラフに何本かのデータを描画した後、別のグラフに描画し、もう元のグラフに戻ってデータを追加で描画しない場合は次のような記述の方がシンプルです。
ネット上ではこのようなフォーマットで描画しているケースも多いです。
ですが、グラフに名前がつかない形式なので、先に作ったグラフに戻ってデータを追加で描画できなくなります。

def draw_chart(paths_na, title):
    # TestLimitを描画
    plt.plot(lim_home_x1, lim_home_y1, label='limit', color='skyblue', markerfacecolor='None')
    plt.plot(lim_home_x2, lim_home_y2, label='limit', color='skyblue', markerfacecolor='None')
    plt.plot(lim_home_x1l, lim_home_y1l, label='limit', color='skyblue', markerfacecolor='None')

    # NA波形の描画
    for path in paths_na:
        cfrs = pd.read_csv(path, header=6)
        plt.plot(df_freq['Freq'], cfrs['|Z|[ohm]'], label='na', color='navy')

    # グラフの設定
    plt.title(title)
    plt.ylabel('|Z| /ohm')
    plt.xlabel('freq /MHz')
    plt.ylim(ymin=0, ymax=500)


# 保存されている波形ファイルパスの一覧を作る
paths_na = glob.glob('J:/.../xxx_0001/RetryFrs/*_C00007_*')
title = '#21'

# グラフを描画
plt.figure(1, figsize=(16.5, 4))

# 全体を描画 -------------------------------------------------------------------------
plt.subplot2grid((1,7), (0,0), colspan=3)
draw_chart(paths_na, title)
plt.xlim(xmin=1.0, xmax=10.0)

# 長手ピーク付近を描画 ----------------------------------------------------------------
plt.subplot2grid((1,7), (0,3), colspan=2)
draw_chart(paths_na, title)
plt.xlim(xmin=1.6, xmax=2.3)

# 短手ピーク付近を描画 ----------------------------------------------------------------
plt.subplot2grid((1,7), (0,5), colspan=2)
draw_chart(paths_na, title)
plt.xlim(xmin=7.0, xmax=8.6)

plt.show()

この場合、最初にfigureを準備して

    plt.figure(1, figsize=(16.5, 4))

その中にグラフを順番に追加していきます。
まず1つ目のグラフ。
縦1、横7のグリッド全体のうち、colspan=3のグラフを(0,0)の座標に配置します。

    plt.subplot2grid((1,7), (0,0), colspan=3)

次に2つ目のグラフ。
同じく、(1,7)で縦1、横7のグリッド全体を示し、colspan=2のグラフを追加します。
配置する座標は1つ目のグラフと重ならないように(0,3)となっています。
(実際には縦軸のタイトルが重なりますが)

    plt.subplot2grid((1,7), (0,3), colspan=2)

そして3つ目のグラフ。

    plt.subplot2grid((1,7), (0,5), colspan=2)

下図が実行した結果になります。
glob.globで指定したフォルダには9個の波形ファイルが入っていましたので、9本のデータが描画されています。
download.png

4. グラフに名前を付ける形式

グラフに名前を付ける場合、まず、figureに名前を付ける必要があるようです。

fig = plt.figure(1, figsize=(16.5, 4))

で、グラフは

ax1 = fig.add_subplot(...)

という具合に追加していくようです。
このadd_subplot()にはplt.subplot2grid()のような形式でグラフを配置してくことができないようなので、GridSpecを使う必要があるようです。

まずはグラフの配置だけを行ってみます。

# グラフを準備
fig = plt.figure(1, figsize=(16.5, 4))

# 全体のGridSpec
# 1行、7列を準備する
gs_master = GridSpec(nrows=1, ncols=7)

# 1つ目のグラフの占有する行数、列数、GridSpec内のどこに配置するかを指定する
gs_1 = GridSpecFromSubplotSpec(nrows=1, ncols=3, subplot_spec=gs_master[0, 0:3])
axes_1 = fig.add_subplot(gs_1[:, :])

# 2つ目のグラフの占有する行数、列数、GridSpec内のどこに配置するかを指定する
gs_2 = GridSpecFromSubplotSpec(nrows=1, ncols=2, subplot_spec=gs_master[0, 3:5])
axes_2 = fig.add_subplot(gs_2[:, :])

# 3つ目のグラフの占有する行数、列数、GridSpec内のどこに配置するかを指定する
gs_3 = GridSpecFromSubplotSpec(nrows=1, ncols=2, subplot_spec=gs_master[0, 5:7])
axes_3 = fig.add_subplot(gs_3[:, :])

plt.show()

これを実行すると下図のようになります。

download.png

なお、この方法はこの記事を参考にさせていただきました。

5. グラフに名前を付ける形式で1つのグループの波形を描画

2.のコードを、3.の形式で書き直します。

def draw_chart2(paths_na, title, color, ax):
    # TestLimitを描画
    ax.plot(lim_home_x1, lim_home_y1, label='limit', color='skyblue', markerfacecolor='None')
    ax.plot(lim_home_x2, lim_home_y2, label='limit', color='skyblue', markerfacecolor='None')
    ax.plot(lim_home_x1l, lim_home_y1l, label='limit', color='skyblue', markerfacecolor='None')

    # NA波形の描画
    for path in paths_na:
        cfrs = pd.read_csv(path, header=6)
        ax.plot(df_freq['Freq'], cfrs['|Z|[ohm]'], label='na', color=color)

    # グラフの設定
    ax.set_title(title)
    ax.set_ylabel('|Z| /ohm')
    ax.set_xlabel('freq /MHz')
    ax.set_ylim(ymin=0, ymax=500)


# 保存されている波形ファイルパスの一覧を作る
paths_na = glob.glob('J:/.../xxx_0001/RetryFrs/*_C00007_*')
title = '#21'

# グラフを準備
fig = plt.figure(1, figsize=(16.5, 4))

# 全体のGridSpec
# 1行、7列を準備する
gs_master = GridSpec(nrows=1, ncols=7)

# 1つ目のグラフの占有する行数、列数、GridSpec内のどこに配置するかを指定する
gs_1 = GridSpecFromSubplotSpec(nrows=1, ncols=3, subplot_spec=gs_master[0, 0:3])
axes_1 = fig.add_subplot(gs_1[:, :])

# 2つ目のグラフの占有する行数、列数、GridSpec内のどこに配置するかを指定する
gs_2 = GridSpecFromSubplotSpec(nrows=1, ncols=2, subplot_spec=gs_master[0, 3:5])
axes_2 = fig.add_subplot(gs_2[:, :])

# 3つ目のグラフの占有する行数、列数、GridSpec内のどこに配置するかを指定する
gs_3 = GridSpecFromSubplotSpec(nrows=1, ncols=2, subplot_spec=gs_master[0, 5:7])
axes_3 = fig.add_subplot(gs_3[:, :])

# 全体を描画 -------------------------------------------------------------------------
draw_chart2(paths_na, title, 'navy', axes_1)
axes_1.set_xlim(xmin=1.0, xmax=10.0)

# 長手ピーク付近を描画 ----------------------------------------------------------------
draw_chart2(paths_na, title, 'navy', axes_2)
axes_2.set_xlim(xmin=1.6, xmax=2.3)

# 短手ピーク付近を描画 ----------------------------------------------------------------
draw_chart2(paths_na, title, 'navy', axes_3)
axes_3.set_xlim(xmin=7.0, xmax=8.6)

plt.show()

グラフを準備する部分が無駄に長くなっています。
一度追加したグラフに戻ってデータを追加しない場合は3.の方がコードが見やすいですね。
また、グラフに名前を付ける形式では、グラフ設定をするコマンドがset_xxxというようなフォーマットに変わっています。
グラフの項目の設定についてはここに詳しく書かれています。
実行結果は3.と同じになります。

6. グラフにつけた名前を利用して複数グループの波形を描画する

3つのグループの波形が3つのフォルダに保存されているケースを想定しています。

# グループ別に保存されている波形ファイルのパス
wfms = ['J:/.../xxx_0001/RetryFrs/*_C00007_*',
        'J:/.../xxx_0002/RetryFrs/*_C00007_*',
        'J:/.../xxx_0003/RetryFrs/*_C00007_*']
colors = ['deepskyblue', 'navy', 'blueviolet']
title = '#21'

# グラフを準備
fig = plt.figure(1, figsize=(16.5, 4))

# 全体のGridSpec
# 1行、7列を準備する
gs_master = GridSpec(nrows=1, ncols=7)

# 1つ目のグラフの占有する行数、列数、GridSpec内のどこに配置するかを指定する
gs_1 = GridSpecFromSubplotSpec(nrows=1, ncols=3, subplot_spec=gs_master[0, 0:3])
axes_1 = fig.add_subplot(gs_1[:, :])

# 2つ目のグラフの占有する行数、列数、GridSpec内のどこに配置するかを指定する
gs_2 = GridSpecFromSubplotSpec(nrows=1, ncols=2, subplot_spec=gs_master[0, 3:5])
axes_2 = fig.add_subplot(gs_2[:, :])

# 3つ目のグラフの占有する行数、列数、GridSpec内のどこに配置するかを指定する
gs_3 = GridSpecFromSubplotSpec(nrows=1, ncols=2, subplot_spec=gs_master[0, 5:7])
axes_3 = fig.add_subplot(gs_3[:, :])

for wfm, color in zip(wfms, colors):
    paths_na = glob.glob(wfm)

    # 全体を描画 -------------------------------------------------------------------------
    draw_chart2(paths_na, title, color, axes_1)
    axes_1.set_xlim(xmin=1.0, xmax=10.0)

    # 長手ピーク付近を描画 ----------------------------------------------------------------
    draw_chart2(paths_na, title, color, axes_2)
    axes_2.set_xlim(xmin=1.6, xmax=2.3)

    # 短手ピーク付近を描画 ----------------------------------------------------------------
    draw_chart2(paths_na, title, color, axes_3)
    axes_3.set_xlim(xmin=7.0, xmax=8.6)

plt.show()

これを実行すると冒頭に示したようなグラフとなります。
download.png

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした