時系列データをMatplotlibで描画する
2020-02-23
- プログラミング
時系列データを描画したい。パターンとして以下の2つを考える。
- 株価
5分刻みで取得した株価データを描画するなど。 - アクセス数
nginxのアクセスログのデータから各時間帯でのアクセス数をヒストグラムにするなど。
この記事ではこれらそのものはやらないが、もう少し単純な例で描画してみる。
やりたいこと
- (時刻, 値)の組の形の時系列データを描画する
株価など。 - 大量のタイムスタンプをヒストグラムとして描画する
アクセス数など。
使うもの
-
Python
3.7.6で確認。 -
Matplotlib
3.1.3で確認。 -
pandas
0.25.3で確認。
pandasはデータ格納用に使うだけ。
なおpandas+Matplotlibでdatetimeを扱うときは以下を実行しないと警告が出るようだ。from pandas.plotting import register_matplotlib_converters register_matplotlib_converters()
方法
目盛の設定
- 目盛の刻み
Date tickersを設定する。たとえば年単位で刻みたいときは YearLocator、分単位はMinuteLocatorを使う。 - 目盛のフォーマット
Date formattersを設定する。 - 設定の反映
刻みはset_major_locatorとset_minor_locator。
フォーマットはset_major_formatterとset_minor_formatter。 - 回転
日付などは長く、そのまま表示すると隣のとかぶって見にくくなりがち。それを避けるために回転する。
setpを使うか、autofmt_xdateを使えばいい。後者は日付型限定なので、汎用的に使える前者を使うことにする。
例1: (時刻, 値)の組の形の時系列データを描画する
import
import matplotlib.pyplot as plt
from matplotlib import dates as mdates
import pandas as pd
from pandas.plotting import register_matplotlib_converters
データ
0時に0、そこから増加して12時に最大(12)になり、
あとは減少して0時に再び0に戻るというサンプルデータを用意する。
DataFrameに格納しておく。
data = [
['2020-01-01 00:00:00', 0],
['2020-01-01 01:00:00', 1],
['2020-01-01 02:00:00', 2],
['2020-01-01 03:00:00', 3],
['2020-01-01 04:00:00', 4],
['2020-01-01 05:00:00', 5],
['2020-01-01 06:00:00', 6],
['2020-01-01 07:00:00', 7],
['2020-01-01 08:00:00', 8],
['2020-01-01 09:00:00', 9],
['2020-01-01 10:00:00', 10],
['2020-01-01 11:00:00', 11],
['2020-01-01 12:00:00', 12],
['2020-01-01 13:00:00', 11],
['2020-01-01 14:00:00', 10],
['2020-01-01 15:00:00', 9],
['2020-01-01 16:00:00', 8],
['2020-01-01 17:00:00', 7],
['2020-01-01 18:00:00', 6],
['2020-01-01 19:00:00', 5],
['2020-01-01 20:00:00', 4],
['2020-01-01 21:00:00', 3],
['2020-01-01 22:00:00', 2],
['2020-01-01 23:00:00', 1],
['2020-01-02 00:00:00', 0],
['2020-01-02 01:00:00', 1],
['2020-01-02 02:00:00', 2],
['2020-01-02 03:00:00', 3],
['2020-01-02 04:00:00', 4],
['2020-01-02 05:00:00', 5],
['2020-01-02 06:00:00', 6],
['2020-01-02 07:00:00', 7],
['2020-01-02 08:00:00', 8],
['2020-01-02 09:00:00', 9],
['2020-01-02 10:00:00', 10],
['2020-01-02 11:00:00', 11],
['2020-01-02 12:00:00', 12],
['2020-01-02 13:00:00', 11],
['2020-01-02 14:00:00', 10],
['2020-01-02 15:00:00', 9],
['2020-01-02 16:00:00', 8],
['2020-01-02 17:00:00', 7],
['2020-01-02 18:00:00', 6],
['2020-01-02 19:00:00', 5],
['2020-01-02 20:00:00', 4],
['2020-01-02 21:00:00', 3],
['2020-01-02 22:00:00', 2],
['2020-01-02 23:00:00', 1],
['2020-01-03 00:00:00', 0],
['2020-01-03 01:00:00', 1],
['2020-01-03 02:00:00', 2],
['2020-01-03 03:00:00', 3],
['2020-01-03 04:00:00', 4],
['2020-01-03 05:00:00', 5],
['2020-01-03 06:00:00', 6],
['2020-01-03 07:00:00', 7],
['2020-01-03 08:00:00', 8],
['2020-01-03 09:00:00', 9],
['2020-01-03 10:00:00', 10],
['2020-01-03 11:00:00', 11],
['2020-01-03 12:00:00', 12],
['2020-01-03 13:00:00', 11],
['2020-01-03 14:00:00', 10],
['2020-01-03 15:00:00', 9],
['2020-01-03 16:00:00', 8],
['2020-01-03 17:00:00', 7],
['2020-01-03 18:00:00', 6],
['2020-01-03 19:00:00', 5],
['2020-01-03 20:00:00', 4],
['2020-01-03 21:00:00', 3],
['2020-01-03 22:00:00', 2],
['2020-01-03 23:00:00', 1],
]
df = pd.DataFrame(data, columns=['datetime', 'val'])
プロット
# pandasでdatetime扱うため
register_matplotlib_converters()
# フォーマット定義
days = mdates.DayLocator()
hours = mdates.HourLocator()
day_fmt = mdates.DateFormatter('%Y/%m/%d')
# グラフ定義
fig, ax = plt.subplots()
x = pd.to_datetime(df['datetime'], format='%Y-%m-%d %H:%M:%S').to_list()
y = df['val'].to_list()
ax.plot(x, y)
ax.grid()
# フォーマット設定
ax.xaxis.set_major_locator(days)
ax.xaxis.set_minor_locator(hours)
ax.xaxis.set_major_formatter(day_fmt)
# 描画範囲の指定
datetime_min, datetime_max = min(x), max(x)
ax.set_xlim(datetime_min, datetime_max)
# x軸のラベルの回転
labels = ax.get_xticklabels()
plt.setp(labels, rotation=45, fontsize=10)
plt.show()
例2: 大量のタイムスタンプをヒストグラムとして描画する
import
import matplotlib.pyplot as plt
from matplotlib import dates as mdates
import pandas as pd
import datetime as dt
import random
from pandas.plotting import register_matplotlib_converters
データ
ヒストグラムを描きたいので、わりと大量にデータを用意する必要がある。
ここでは乱数を用いて、1週間ぶんのタイムスタンプを1000個生成する。
(もっといい方法があるのかもしれないが…。)
# 作成するdatetime数
N = 1000
datetime_from, datetime_to = dt.datetime(2020, 1, 1, 0, 0, 0), dt.datetime(2020, 1, 8, 0, 0, 0)
timestamp_from, timestamp_to = int(datetime_from.timestamp()), int(datetime_to.timestamp())
random_timestamps = [random.randint(timestamp_from, timestamp_to) for _ in range(0, N)]
random_datetimes = [dt.datetime.fromtimestamp(t) for t in random_timestamps]
random_datetimes_str = [dt.strftime("%Y-%m-%d %H:%M:%S") for dt in random_datetimes]
生成したデータはこんなふうになる。
# random_datetimes_str[:10]の結果
['2020-01-06 07:14:19',
'2020-01-01 13:39:55',
'2020-01-01 01:37:01',
'2020-01-02 13:14:58',
'2020-01-02 22:22:01',
'2020-01-06 08:08:00',
'2020-01-05 08:53:54',
'2020-01-07 03:05:51',
'2020-01-05 09:19:09',
'2020-01-04 01:46:38']
プロット
# pandasでdatetime扱うため
register_matplotlib_converters()
# フォーマット定義
days = mdates.DayLocator()
hours = mdates.HourLocator()
day_fmt = mdates.DateFormatter('%Y/%m/%d')
# グラフ定義
fig, ax = plt.subplots()
x = [dt.datetime.strptime(s, "%Y-%m-%d %H:%M:%S") for s in random_datetimes_str]
ax.hist(x, bins=7)
# フォーマット設定
ax.xaxis.set_major_locator(days)
ax.xaxis.set_major_formatter(day_fmt)
ax.xaxis.set_minor_locator(hours)
# 描画範囲の指定
datemin, datemax = min(x), max(x)
ax.set_xlim(datemin, datemax)
ax.grid()
# x軸のラベルの回転
labels = ax.get_xticklabels()
plt.setp(labels, rotation=45, fontsize=10)
plt.show()