Jupyter(matplotlib)でセンサデータの分析をする
自分よくセンサを使ったことする人でセンサを扱うにも得られるデータの分析をしないといけない
今回は自分がよくセンサデータの分析に使っているmatplotlibの使い方について載せておく
matplotlibで何ができるの?
グラフが作れる
データを見れるだけでなく,データ処理の結果も表示できる
言語はpythonを使用しているので,JupyterNotebookで実行する以外にもpython単体でも動きます
なんならvsCodeでJupyter環境構築できます
というかvsCodeでやるのおすすめです
JupyteNotebookで実行するメリット・デメリット
-
メリット
- ファイル出力せずにグラフを確認できる
python単体ではグラフの確認は毎回ファイルで出力する必要あるがJupyterNotebookでは不要
画像で出力されるので右クリDLもできる - セルで実行区間を分けれる
importセル,functionセル,などに分けられる
- ファイル出力せずにグラフを確認できる
-
デメリット
コード補完がない(つらい)
web上のJupyterNotebookでの話なのでvsCodeのJupyter環境ならデメリットなし
Jupyter環境のインストール
大体3つの方法がある
vsCodeで環境構築
前提条件:VSCodeのインストール,python実行環境
してない人は今すぐインストールするのだ
環境構築
jupyterのインストール$pip install jupyter
できない場合は$pip3 install jupyter
を試してみて
VSCodeの拡張機能を追加
↓この辺をインストール
インストールが終わったら適当なファイル名.ipynb
を作成する
ターミナルで% python --version
で現在使用しているPythonのバージョンを確認する
①を押し,②で先ほど確認したバージョンと一致するものを選択
後はファイルに以下のコードを記述して実行しておく
import matplotlib
print(matplotlib.matplotlib_fname())
print(matplotlib.get_cachedir())
書く場所と実行方法
後2つはブラウザでやるのがいいのだ人向け
- anacondaを利用
anacondaをインストールしてJupyteNotebookを起動するだけ
matplotlibなどの必要なライブラリが入っているpythonが付いてくる
これでpythonインストールする方法もある - コマンドを使ってインストール・実行
やったことないので自分で調べてもろて
ライブラリの追加
pip install **
でインストール
日本語を利用できるようにする
デフォルト状態では表内に日本語を使おうとすると文字化けします
↓このように
おすすめフォント(IPAexGothic)
ダウンロードしたフォントはmatplotlib.matplotlib_fname()
でmatplotlibrcのファイルパスを確認し,
フォントファイル(.ttf)をmatplotlibrcのあるフォルダ(mpl-data
)内のfont/ttf内に入れる
フォントの変更方法2種
link
- コード内に
plt.rcParams['font.family'] = "フォント名"
を追加
ファイル内のみで有効 - matplotlibrcの設定を変更
link
python自体の設定を変更する方法
まずはmatplotlibrcを編集
場所はprint(matplotlib.matplotlib_fname())
の場所にある
260行あたりにfont.family: 〜〜〜
があるので,〜〜〜の部分を使いたいフォント名にする
行の最初に#
がある場合は消しておく
print(matplotlib.get_cachedir())
でキャッシュがあるフォルダの場所があるので,中のファイル〜〜〜.json
を全て削除
matplotlibの基本
勉強用データ
今回使用するCSVファイル
階段(踊場あり)で1階から4階までを登った際の気圧と加速度(重力含まない)のデータ
ヘッダ情報
気圧(study_pressure.csv)
- time: センサを取得した実時間[ms](ミリ秒,1000ms = 1s)
- pressure: 気圧データ[hPa]
加速度(study_acc.csv)
- time: センサを取得した実時間[ms]
- x: x軸の加速度[m/s^2]
- y: y軸の加速度[m/s^2]
- z: z軸の加速度[m/s^2]
基本的なコード
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
plt.rcParams["font.size"] = 28
fname = "study"
press = pd.read_csv(fname+"_pressure.csv", encoding = 'utf-8')
acc = pd.read_csv(fname+"_acc.csv",encoding = 'utf-8')
fig = plt.figure(figsize=(20, 20))
ax1 = fig.add_subplot(2, 1, 1)
ax1.grid(color='k', linestyle='dotted', linewidth=1, alpha=0.5, zorder=2)
ax1.set_title("気圧値")
ax1.set_xlabel('data[num]')
ax1.set_ylabel('pressure[$hPa$]')
ax1.get_yaxis().get_major_formatter().set_useOffset(False)
ax1.plot(np.arange(0,len(press)),press["pressure"],label="生データ")
ax1.legend()
ax2 = fig.add_subplot(2, 1, 2)
ax2.grid(color='k', linestyle='dotted', linewidth=1, alpha=0.5, zorder=2)
ax2.set_title("加速度")
ax2.set_xlabel('data[num]')
ax2.set_ylabel('accelaration[$m/s^2$]')
x = ax4.plot(np.arange(0,len(acc)),acc["x"],label="x")
y = ax4.plot(np.arange(0,len(acc)),acc["y"],label="y")
z = ax4.plot(np.arange(0,len(acc)),acc["z"],label="z")
ax2.legend(handles = x + y +z)
出力結果
説明
-
importシリーズ
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
:グラフ表示用 -
plt.rcParams["font.size"] = フォントサイズ
フォントサイズを変更
後で説明するグラフサイズの場合は30くらい -
csv = pd.read_csv("ファイル名(.csv)", encoding = 'utf-8')
csvファイル(ヘッダ(項目名)あり)を読み込む
読み込んだファイルはcsv["ヘッダ"][データ番号]
で確認できる
encoding = 'utf-8'
はヘッダ名が日本語の場合は必須
ヘッダなしの場合csv = pd.read_csv("ファイル名(.csv)", header=None, names=['user_id', 'name'])
で可能
namesの部分でヘッダ名を指定する -
fig = plt.figure(figsize=(横サイズ, 縦サイズ))
グラフのサイズを指定 -
ax1 = fig.add_subplot(行数, 列数, 場所番号)
一つの画像内に複数のグラフを表示させる場合に使用
変数にそれぞれのグラフを指定してあげる
以降ax1.~~~
が出てくるが,plt.~~~~
でもできる(少し関数名が変わるけど)
場所番号は左上を1として右優先で割り振られている
-
ax1.grid(color='k', linestyle='dotted', linewidth=1, alpha=0.5, zorder=2)
グラフ内にグリッドを表示 -
ax1.set_title("タイトル")
グラフのタイトルを指定 -
ax1.set_xlabel('ラベル名')
,ax1.set_ylabel('ラベル名')
x軸・y軸のラベル名を指定 -
ax1.get_yaxis().get_major_formatter().set_useOffset(False)
y軸の数値のオフセット表示をさせないようにする
↓こういうの(y軸)
設定するとこうなる↓
x軸の場合はax1.get_xaxis().get_major_formatter().set_useOffset(False)
-
ax1.plot(x軸データ,y軸データ,label="ラベル名")
グラフにデータを表示
他の表示方法としてacc.plot(x="time", ax=ax4)
もある
こちらだとCSV内のデータを全て出力する -
ax1.legend()
データのラベルを表示
ラベル(凡例)について(表示関係の引数の説明付き)
引数handles
なしの場合はplot時に指定したlabelが表示される
handles
は個別にラベルを指定する用
handles
を使う場合をax2でやってる
コードにはないけど知っておくといいもの
-
ax1.set_xlim([x1,x2])
,ax1.set_ylim([y1,y2])
グラフのx1~x2(y1~y2)区間を表示
x軸を時間に変える
基本的なコードだとx軸はデータ番号になっている
そのため気圧と加速度でx軸の範囲が異なる
きちんと対応づけるためにx軸を時間で表示しよう
今回のデータには時間が入っているのでそれを使う
コードの変更
#気圧
ax1.plot(list(range(0,len(press))), press["pressure"], label="生データ")
-> ax1.plot((press["time"]-press["time"][0])/1000, press["pressure"], label="生データ")
#x軸加速度(y・zについても)
ax4.plot(list(range(0,len(acc))), acc["x"], label="x")
-> ax4.plot((acc["time"]-acc["time"][0])/1000, acc["x"], label="x")
#x軸ラベルも変えておこう(ax2も)
ax1.set_xlabel('data[num]') -> ax1.set_xlabel('time[s]')
x軸を最初のデータからの相対時間で表示させています
出力結果
グラフに点をつける
ax1.plot(x, y, marker='o', markersize=10, color='#00FF00')
ax1.scatter(x, y,marker='o', s=100, color = '#00FF00')
でも可(少し変わるので注意)
(x, y)に指定した形(marker)の点をつける
markersize(scatterの場合は's',起点の大きさが違うので注意)で大きさ,colorで色を指定
markerの種類
x,yには配列も渡せるけど,plot
の場合だと点と点を折れ線グラフで表示してしまう(元々グラフ表示用だから)
scatter
のほうはなぜかplot
で出した線の下に描画される(なぜ?)
自分は線の上に出したいのでplot
を複数回繰り返す方法してる(求:もっといい方法)
背景に色をつける(ラベリング)
ax1.axvspan(x1, x2, color="#009000", alpha=0.3)
x1からx2までの背景を色color
・透明度alpha
にする
color
はカラーコード指定以外の方法もある link
y軸で指定する場合はax1.axhspan(y1, y2, color="#009000", alpha=0.3)
背景色や点のラベルを作成する
背景色
背景色のラベルを入れた変数を用意してlegend
の引数handles
に入れる
from matplotlib.patches import Patch
from matplotlib import patches
legend_marker = [Patch(facecolor='#009000', alpha=0.3, label='緑'), Patch(facecolor='#000090', alpha=0.3, label='青'), Patch(facecolor='#900000', alpha=0.3, label='赤')]
fig.legend(handles=legend_marker)
点
scatter
を生成して引数にlabel
を指定してあげるだけ
plot
だとグラフのラベルになる
point = ax1.scatter(x, y,marker='o', s=100, color = '#00FF00', label="点")
# 背景色のラベルとの併用方法
fig.legend(handles=legend_marker + [Point])
実際にデータ処理をしてみる
階段を登っている区間をラベリングしてみよう
加速度データでは極値を走査して点で表示して,さらに閾値以上の極大値に別の点をつける
やること
- 気圧
- ノイズ除去する
- 推定・結果表示
- 加速度
- 加速度をノルムにする
- ノイズ除去する
- 極値走査
- 閾値処理
加速度:加速度をノルムにする
加速度を使う際に軸方向を考慮しない場合はノルムを使うのが多い.
ノルムとは加速度全体の大きさを表すもの.
ベクトルの合成結果の大きさでもある.
大きさなので値は正になる.
ノルムの式norm = √(x^2 + y^2 + z^2)
pythonだと(x**2 + y**2 + z**2)**0.5
で出せる
気圧・加速度:ノイズ除去する
センサデータはたとえ静止状態でも値が微小に変化する(この微小な変化がノイズ).
ノイズが推定に影響を与える場合があるのでノイズをできるだけ除去する.
ノイズ除去にはフィルタを使う.
フィルタはさまざまな種類がある.
今回は移動平均フィルタ(p.20以降)を使う.
pythonではrolling関数がある.
press["pressure"].rolling(window=5).mean()
結果としてほぼ水平部分が滑らかな線になっている(window = 気圧:5,加速度:3).
一部拡大した図
振幅が減衰してるね
気圧:推定・結果表示
気圧センサから階移動区間推定・背景色でラベリング
高さが1m変化すると気圧は0.1hPa変化する.
変化の部分を捉えようとする方法もあるけど,
変化していない部分を先に捉えて,それ以外の部分を階段使用中とする方法で行う.
変化していない部分をラベリングした結果(閾値:0.05hPa,3s)
後はラベリングしていない区間を階段としてやれば完了
最終的な階移動区間推定のラベリング結果
コード例
pressFlat = []# 変化していない部分の保存用
iBefore = 0# 比較するデータ番号
for i in range(5,len(lowPass)):
if i == 5: iBefore = i # 比較するデータ番号を保存
if abs(lowPass[i]-lowPass[iBefore]) > 0.05:
# 現在のデータと比較し,気圧が0.05hPa以上の時
# 図にラベリングする x軸の区間
first = (press["time"][iBefore] - firstPressTime)/1000
final = (press["time"][i] - firstPressTime)/1000
if final - first > 3:
# x軸の区間が3秒以上の時
pressFlat.append([first,final])# 変化していない部分を保存
ax1.axvspan(first,final, color="#009000", alpha=0.3)# 変化していない部分をラベリング
iBefore = i# 比較するデータ番号を変更
# 階段利用区間をラベリング
for i in range(1,len(pressFlat)):
ax1.axvspan(pressFlat[i-1][1],pressFlat[i][0], color="#900000", alpha=0.3)
加速度:極値走査
極値かの確認方法はある値が前後より大きいor小さい値がなければ極値になる
結果
コード例
maxd = [] #極大値リスト, [時間(x軸), 加速度(y軸)]
mind = [] #極小値リスト
n = 1 #極値走査時に見る前後のデータ数
#極値走査
for i in range(3+n,len(lowNorm)-n):
time = (acc["time"][i] - firstAccTime)/1000 #現在データの時間情報(x軸)
maxdF = True #極大値かどうか
mindF = True #極小値かどうか
#現在データが極値かどうか
for j in range(i-n, i+n+1):
if lowNorm[i] < lowNorm[j]: maxdF = False #現在データより値が大きいデータがあるので極大値でない
if lowNorm[i] > lowNorm[j]: mindF = False #現在データより値が小さいデータがあるので極小値でない
if maxdF: maxd.append([time, lowNorm[i]]) #極大値ならリストに追加
if mindF: mind.append([time, lowNorm[i]]) #極小値ならリストに追加
for i in maxd: #極大値を表示
ax2.plot(i[0], i[1], marker='^', markersize=10, color='#FF0000')
for i in mind: #極小値を表示
ax2.plot(i[0], i[1], marker='v', markersize=10, color='#0000FF')
#凡例用
maxdP = ax2.scatter(maxd[0][0], maxd[0][1], marker='^', s=100, color='#FF0000', label="極大値")
mindP = ax2.scatter(mind[0][0], mind[0][1], marker='v', s=100, color='#0000FF', label="極小値")
ax2.legend(handles=[maxdP, mindP])
加速度:閾値処理
極大値のリストから閾値処理するだけ
結果
コード例
for i in maxd: #極大値を表示
ax2.plot(i[0], i[1], marker='^', markersize=10, color='#FF0000')
if i[1] >= 5.0: ax2.plot(i[0], i[1], marker='o', markersize=10, color='#00FF00') #追加
#凡例用
thP = ax2.scatter(stepPoint[0][0], stepPoint[0][1], marker='o', s=100, color='#00FF00', label="5m/s^2以上の極大値") #追加
ax2.legend(handles=[maxdP, mindP, thP]) #変更
おまけ
グラフアニメーションを作成する方法
音のグラフを見たいときに使う・使った
ffmpeg
のインストール必要
fig = plt.figure(figsize=(20, 15))
ax1 = fig.add_subplot(1, 1, 1)
ax1.grid(color='k', linestyle='dotted', linewidth=1, alpha=0.5, zorder=2)
ax1.get_yaxis().get_major_formatter().set_useOffset(False)
ax1.set_facecolor("#EEEEEE")
ax1.set_title(f"audio: time={time[0]}")
ax1.set_xlabel('data[$num$]')
ax1.set_ylabel('amplitude')
ax1.set_xlim([-1.0,len(buffer[0])+1.0])
ax1.set_ylim([-30000,30000])
line1, = ax1.plot(range(len(buffer[0])), buffer[0])
def update1(i):
ax1.set_title(f"audio: time={time[i]}") # グラフタイトル
line1.set_data(range(len(buffer[i])), buffer[i]) # グラフ線の設定と描画
# 処理状況の表示
print('\r {}/{}'.format(str(i+1), str(len(buffer))), end='')
ani = animation.FuncAnimation(fig, update1, interval=40, frames=len(buffer))
ani.save(f"{fname}.mp4", writer="ffmpeg", dpi=200)
いろいろなグラフの書き方
棒グラフの書き方
箱ひげ図の書き方
エラーバー付きの点
エラーバー付きの棒グラフ
エラーバーは標準偏差を表すのに使ったりする
グラフをコード内で保存させる
fig.savefig("ファイル名.png")
拡張子は画像系やpdfを指定できる
この方法で保存したpngは背景が透明になっている
(VSCodeの結果表示から保存すると背景が白になる)
複数のグラフを1つのpdfファイルにまとめたい場合
from matplotlib.backends.backend_pdf import PdfPages
pdf = PdfPages("ファイル名.pdf")
pdf.savefig(fig1)
pdf.savefig(fig2)
pdf.close()
Discussion