LoginSignup
67

More than 5 years have passed since last update.

posted at

updated at

PythonというかMatplotlibでスケールや単位の異なる時系列データを整形して表示する

1つのソースの時系列データをグラフ化で特に悩むことはありません。が、ソースの異なる時系列データを表示する方法が分からなかったのでやってみました。結論からいうと得たい結果は得られましたが、いまいちです。

結果は不本意なので、もっとスマートな方法があれば教えて下さい。

やりたいことと課題

例としては、

  • Webアクセスデータはアクセス毎に秒単位で存在(2016-06-2 00:00:00から2016-06-7 00:00:00取得)
  • 同時期のCPUのデータ。1時間に1回、遅れて取得(2016-06-03 12:00:00から2016-06-09 17:00:00取得)

みたいな感じです。
各データを表示することは簡単ですが、取得期間も単位(件数、%等)も異なる場合は厄介です。

そもそもDBやCSVからデータを取得する方法はこちらをご覧ください。

Matplotlibの基本

細かな話の前に、そもそもMatploglibに慣れてないので、簡単におさらい。
最もシンプルなコードは下記のような感じ。subplotを利用しないともっとシンプルですが、以後の記述の互換性のために敢えてsubplot(複数の図をコントロールするための記述)で書いています。

#coding:utf-8
import matplotlib.pyplot as plt

#白紙のキャンバス?を生成
fig = plt.figure()
#1行、1列の1個目の図を描くエリアを用意
ax = fig.add_subplot(1,1,1)

#dataを用意 xとyの個数は一致しておく必要あり
x = [0,1,2,3,4,5]
y = [54,35,32,44,74,45]

#データをセット(プロット)
ax.plot(x,y)

#図を表示
plt.show()

まあ、普通に表示されます。

plt

コードの補足や解説が下の方にありますので、見てみて下さい。

課題の確認

2つの性質の違う2つのデータを準備します。
なお、データや日付はPythonの強力な関数で自動生成します。

データ1

2016/6/2から6/7までの日次データ。データ数は6個。

#coding:utf-8

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(1,1,1)

#0-100の間で乱数を発生させて6要素のリストを作成
y1 = np.random.randint(0,100,6)
#2016-06-02 00:00:00から1日毎のdatetimeを生成(6個生成)
x1 = pd.date_range('2016-06-02 00:00:00',periods=6,freq='d')

ax.plot(x1,y1)

plt.show()

こんな感じ。

plt

データ2

2016/6/3から6/9までの時次データ。データ数は150個。

#coding:utf-8

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(1,1,1)

#5-40の間で乱数を発生させて150要素のリストを生成
y2 = np.random.randint(5,40,150)
#2016-06-03 12:00:00から1時間毎のdatetimeを生成(150個生成)
x2= pd.date_range('2016-06-03 12:00:00',periods=150,freq='H')

ax.plot(x2,y2)

plt.show()

時間単位なので、データ1より、激しい感じ。

plt

上記2つのデータは期間こそかぶっている時期はあるものの、取得期間も性質も違う。
最低限、時間軸(x軸)を合わせて表示したい。

とりあえず表示

とりあえず表示してみます。

#coding:utf-8

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

fig = plt.figure()
#2行1列の図を描く場所を確保
ax1 = fig.add_subplot(2,1,1)
ax2 = fig.add_subplot(2,1,2)

#data1
y1 = np.random.randint(0,100,6)
x1 = pd.date_range('2016-06-02 00:00:00',periods=6,freq='d')

#data2
y2 = np.random.randint(5,40,150)
x2 = pd.date_range('2016-06-03 12:00:00',periods=150,freq='H')

#plot
ax1.plot(x1,y1)
ax2.plot(x2,y2)

plt.show()

それっぽいのですが、時系列が合っておらず、事実上無意味なグラフとなっています。

plt

対応方法

不本意ながら

いろいろ試してみましたPython初心者の私にはとりあえず下記が限界。
両データの共通単位となるダミーデータ(ここではx0,y0)を生成し、それを利用するというもの。

ここでは6/1~6/10のデータを利用してみました。

#coding:utf-8

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

fig = plt.figure()
#2行1列の図を描く場所を確保
ax1 = fig.add_subplot(2,1,1)
ax2 = fig.add_subplot(2,1,2)

#data0 基準とするダミーデータを生成
#yの値を0を10個含むリスト
#xの値は6/1 ~ 6/10までのdatetime
y0 = [0]*10
x0 = pd.date_range('2016-06-01 00:00:00',periods=10,freq='d')

#data1
y1 = np.random.randint(0,100,6)
x1 = pd.date_range('2016-06-02 00:00:00',periods=6,freq='d')

#data2
y2 = np.random.randint(5,40,150)
x2 = pd.date_range('2016-06-03 12:00:00',periods=150,freq='H')

#plot
#ax1
ax1.plot(x0,y0)
ax1.plot(x1,y1)
#ax2
ax2.plot(x0,y0)
ax2.plot(x2,y2)

plt.show()

どうやら、時間軸はあっている感じですが、キャプション等が被ったりして見難い。

plt

見た目を整える

#coding:utf-8

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt

fig = plt.figure()
#2行1列の図を描く場所を確保
ax1 = fig.add_subplot(2,1,1)
ax2 = fig.add_subplot(2,1,2)

#data0
y0 = [0]*10
x0 = pd.date_range('2016-06-01 00:00:00',periods=10,freq='d')

#data1
y1 = np.random.randint(0,100,6)
x1 = pd.date_range('2016-06-02 00:00:00',periods=6,freq='d')

#data2
y2 = np.random.randint(5,40,150)
x2 = pd.date_range('2016-06-03 12:00:00',periods=150,freq='H')

#plot
#ax1
ax1.plot(x0,y0)
ax1.plot(x1,y1,'r')
#ax2
ax2.plot(x0,y0)
ax2.plot(x2,y2,'b')

#整形
#ax1
ax1.set_xticks(x0)
ax1.set_xticklabels(x0,rotation=90,size="small")
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax1.grid()
#ax2
ax2.set_xticks(x0)
ax2.set_xticklabels(x0,rotation=90,size="small")
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax2.grid()

#縦になったキャプションが被るのを防止
plt.subplots_adjust(hspace=0.7,bottom=0.2)

plt.show()

キャプションを縦にしたり、年月日だけ表示などにした。グラフの色も変えてみた。
個人的にはこれで用は足りる。

plt

1つのグラフにマージする

わざわざ2つのグラフに分ける必要も無い場合も多いので1つのグラフに表示してみる。

#coding:utf-8

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt

fig = plt.figure()
#1行1列の図
ax1 = fig.add_subplot(1,1,1)
#レイヤー追加(みたいな?)
ax2 = ax1.twinx()

#data0
y0 = [0]*10
x0 = pd.date_range('2016-06-01 00:00:00',periods=10,freq='d')

#data1
y1 = np.random.randint(0,100,6)
x1 = pd.date_range('2016-06-02 00:00:00',periods=6,freq='d')

#data2
y2 = np.random.randint(5,40,150)
x2 = pd.date_range('2016-06-03 12:00:00',periods=150,freq='H')

#plot
#ax1
ax1.plot(x0,y0)
ax1.plot(x1,y1,'r')

#ax2
ax2.plot(x0,y0)
ax2.plot(x2,y2,'b')

#整形
#ax1
ax1.set_xticks(x0)
ax1.set_xticklabels(x0,rotation=90,size="small")
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax1.grid()
#軸のフォーマット調整
ax1.set_ylabel('pv', color='r')
for tl in ax1.get_yticklabels():
    tl.set_color('r')
#ax2
ax2.set_xticks(x0)
ax2.set_xticklabels(x0,rotation=90,size="small")
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax2.grid()
#軸のフォーマット調整
ax2.set_ylabel('cpu', color='b')
for tl in ax2.get_yticklabels():
    tl.set_color('b')

#縦になったキャプションが被るのを防止
plt.subplots_adjust(hspace=0.7,bottom=0.2)

plt.show()

まあ、こんな感じ。軸の色などを変え、どちらのデータなのかわかりやすくしてみました(わかりにくいけど)。

plt

補足・解説

文字列からdatetimeへの変換

上記の例ではpandasのdate_range()でxデータを生成したため、はじめからdatetime型となっていますが、CSVやDBから取得した場合、文字列となっています。文字列でもそのままうまく処理できるようですが、必要に応じてdatetimeに変換します。

文字列リストからdatetimeリストへの変換は下記のようにします。

y1 = [0.43,0.26,0.33]
d1 = ['2016-06-21 12:00:00','2016-06-23 09:00:00','2016-06-26 18:00:00']
x1 = [dt.datetime.strptime(d,'%Y-%m-%d %H:%M:%S') for d in d1]

軸(X軸)の編集

x軸等の編集について解説します。

キャプションを入れる位置を決める

日付だと少しわかりにくいのですが、例えば、横軸が0から1000の場合、300と600と900の3箇所だけに単位を表示したいとしたい場合、set_xticks([300,600,900])とします。

ここでは、6/1 ~ 6/10で日次でキャプションを入れたいのでダミーで生成したx0をそのまま代入しています。

ax1.set_xticks(x0)

キャプションの表示を決める

キャプションを入れる位置を決めたら、次は、その表示内容、表示書式などを決定します。
例えば上記で、位置としてはset_xticks([300,600,900])としたが、表示上は小、中、大としたい場合は、set_xticklabels(['小','中','大'])となします。rotationで文字の傾きを指定できます。90とすると縦になります。sizeはそのままです。

ここでは、6/1 ~ 6/10の日付をそのまま表示したいので、x0をそのまま代入しています。

ax1.set_xticklabels(x0,rotation=90,size="small")

表示形式をさらに調整

普通にx0を表示すると年月日 時分秒全てが表示され長いので、年月日だけが表示されるようにしています。

ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))

色を付ける

線の色

データの線に色を付けたい場合は、下記のようにします。この例では赤になります。

ax1.plot(x1,y1,'r')

軸の色

軸やキャプションを指定し色を付けたい場合は、下記のようにします。この例では青になります。

ax2.set_ylabel('cpu', color='b')
for tl in ax2.get_yticklabels():
    tl.set_color('b')

余白の調整

hspaceはグラフ間の縦の間隔調整。フラフの高さが1.0とした場合の単位らしい。bottomは下の余白。

plt.subplots_adjust(hspace=0.7,bottom=0.2)

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
What you can do with signing up
67