【matplotlib】センサーデータ等の時系列ステータスを可視化する(カテゴリカル変数)

Posted: , Category: Python , 時系列分析

matplitlibを用いて時系列のセンサーのステータスをプロットする際に、どのような図形にすればよいか色々考えたのですが、ひとまず次のような形式が見やすいのではないでしょうか。

このグラフは、縦軸は日付で、横軸はその日の時間で、グラフの色で機器のステータス状況を示しています。

今回は、センサーのカテゴリカルな時系列データから、上のようなプロットを作る方法について紹介します。

テストデータの作成

ひとまず、次のようなセンサーデータを手動で作成し、DataFrameに変換します。データとしては、毎分おきにデータが取得され、0.5%の確率で機器のステータスが変わるような仮想のデータを作りました。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.gridspec as gridspec
from matplotlib.patches import Rectangle
from matplotlib.colors import ListedColormap
import pandas as pd

from datetime import datetime as dt
from datetime import timedelta as td

from datetime import datetime as dt
from datetime import timedelta as td

a = dt(2020, 10, 11, 1, 12, 32)
b = dt(2020, 10, 16, 1, 23, 21)

dates = [a.replace(hour=0, minute=0, second=0, microsecond=0) + td(days=i) for i in range((b-a).days + 1)]

statuses = ["ABLE", "DISABLE", "ERROR", "STOPPED"]

current = statuses[0]
base_time = a
times = []
status_list = []

while (base_time < b):
    times.append(base_time)
    prob = np.random.random()
    if (prob >= 0.995): # change status in 99% probability
        current = statuses[np.random.randint(0, len(statuses))]
    status_list.append(current)
    base_time += td(minutes=1)

df = pd.DataFrame(data={"datetime": times , "status": status_list })
boxes = np.zeros(((b-a).days + 1) * 3600)

データの加工とプロットの作成

データを加工してプロットを作成するためのコードはこちらです。

for idx_date, date in enumerate(dates):
    for minute in range(3600):
        idx = 3600 * idx_date + minute
        filter_df = df[(df["datetime"] >= date + td(minutes=minute)) & (df["datetime"] < date + td(minutes=minute+1))]
        if (len(filter_df) == 0):
            if (idx == 0):
                boxes[idx] = len(statuses) # len(statuses) == NOT_CLEAR
            else:
                boxes[idx] = boxes[idx - 1]
        else:
            boxes[idx] = statuses.index(filter_df.iloc[-1]["status"])
            
        date + td(minutes=minute)

colors = ["palegreen", "lightcyan", "aliceblue", "lavender", "white"]
cmap = ListedColormap(colors, name="custom")

y_lims = list(map(dt.fromtimestamp, [int(a.timestamp()), int(b.timestamp())]))
y_lims = mdates.date2num(y_lims)

x_lims = list(map(dt.fromtimestamp, 
                  [int(a.replace(hour=0, minute=0, second=0, microsecond=0).timestamp()),
                   int((a + td(days=1)).timestamp())]))
x_lims = mdates.date2num(x_lims)

fig = plt.figure(figsize=(20, 3))

gs = gridspec.GridSpec(20, 10) 
ax1 = plt.subplot(gs[0:20, 0:8]) 
ax2 = plt.subplot(gs[0:20, 8:10])

fuga = boxes.reshape((b-a).days + 1, 3600, 1)

ax1.imshow(fuga, aspect='auto', interpolation="none", cmap=cmap, extent = [0, 3600, y_lims[1], y_lims[0]])
ax1.yaxis_date()
date_format = mdates.DateFormatter('%Y/%m/%d')
ax1.yaxis.set_major_formatter(date_format)
ax1.xaxis.set_major_formatter(mdates.DateFormatter("%M:%S"))

colors = {
    "ABLE": "palegreen",
    "DISABLE": "lightcyan",
    "ERROR": "aliceblue",
    "STOPPED": "lavender",
    "UNCLEAR": "white"
}

cell_width = 312
cell_height = 32
swatch_width = 88
margin = 12

names = list(colors)
n = len(names)
ncols = 1
nrows = n // ncols + int(n % ncols > 0) 

print("nrows is {}".format(nrows))

ax2.set_xlim(0, cell_width * 3)
ax2.set_ylim(cell_height * (nrows-0.5), -cell_height/2.)
ax2.yaxis.set_visible(False)
ax2.xaxis.set_visible(False)
ax2.set_axis_off()

for i, name in enumerate(names):
    row = i % nrows
    col = i // nrows
    y = row * cell_height

    swatch_start_x = cell_width * col
    text_pos_x = cell_width * col + swatch_width + 100

    ax2.text(text_pos_x, y, name, fontsize=14, horizontalalignment='left', verticalalignment='center')

    ax2.add_patch(
        Rectangle(xy=(swatch_start_x, y-9), width=swatch_width,
                  height=18, facecolor=colors[name], edgecolor='0.7')
    )

最初に掲載したようなプロットを得ることができました。

機器センサーの状態によって、うまく時間の経過ごとに色が使い分けられているのがわかります。

このプロットを見ると、いつの時間にどのような状態か一目でわかるので結構よいですね。

課題としては、データ量が増加すると、描写時間が遅くなること、横軸が時間軸で%H:%Mのフォーマットで表示したいのですが、うまくいかないことです。この辺りは、matplotlibのmdatesの仕様を調べて修正していく予定です。修正の方法についてご存知であれば教えてください。

【広告】
統計学的にあなたの悩みを解決します。
仕事やプライベートでお悩みの方は、ベテラン占い師 蓮若菜にご相談ください。

機械学習と情報技術