GrovePi+の温湿度センサーから得たCSVファイルをmatplotlibでグラフ化(その1)
何やら、持って回った題名ですが要はCSVファイルのグラフ化です。グラフ化するに当たりGrovePi+の温湿度センサーから得たデータを新たな仕様のCSVファイルに保存する必要があったのでちょっと持って回っており、表記の題名となっております。
CSVファイルにしたのなら表計算ソフトでグラフ化した方が良いようにも思われますがmatplotlibはグラフがきれいに描けますのでその魅力に引き寄せられてしまったのです。
リアルタイムグラフ(三軸)
まずは、当ブログの「GrovePi+の温湿度センサー(DHT22)からのデータをmatplotlibでリアルタイムグラフ化」と「GrovePi+で時刻毎に温湿度センサーデーターを得る方法」を融合し、さらに、CSVファイルから再読み込みのため通し番号を一列目に付けるようにしました。(確認用のprint()関数では2列目ですが)CSVファイルのサンプルはここ「temp01.csv」をダウンロード です。本グラフも次にCSVから起こすグラフもそうですが、右に行くほどデータが古くなるグラフとなっています。これは、グラフデータの見せ方としては左右が逆になっており、当方のmatplotlibでの描き方の力量不足によるものです。グラフのX軸が日時毎に示すグラフで右端いっぱいになったところで左端からデータと同時にX軸も切り落として行くグラフに作れれば良かったのですが。
次に、CSVファイルをmatplotlibでグラフ化です。まず、pythonにCSVファイルから変数に1行目の列名を除き、値を転写します。その後、グラフ化し易いよう行列を変換し、リアルタイムグラフと同様になるよう最右列の数値がグラフ左側(0分)となり最左列の数値をグラフ右側としたいので通し番号を逆順にしてからグラフ化しました。さらに、グラフを横長にし、1日の温湿度データを示せる様にしました。また、グラフ化に当たり画面上に示すほかにPNGファイルに画像ファイル化出来るようにしました。
PNG画像ファイル化したCSVファイルから読み込んだ24時間グラフ(三軸)
また、利用にあたっての注意点があります。それは、リアルタイムグラフを動かしながらCSVファイルのグラフを作成すると少々問題が生じます。それは、測定途中で温度と湿度でnan表示が出た後に温度と湿度で-1表示が延々続くことです。理由ははっきり分かりませんが、CSVファイルの読み込み時のバッティングか、matplotlibのバッティングかによりエラーが生じるようです。
詳細説明の前に前提条件ですがRaspberryPi2でPython3、matplotlib2.0.0、pygame1.9.3を使用しています。
CSVファイルにしたのなら表計算ソフトでグラフ化した方が良いようにも思われますがmatplotlibはグラフがきれいに描けますのでその魅力に引き寄せられてしまったのです。
リアルタイムグラフ(三軸)
まずは、当ブログの「GrovePi+の温湿度センサー(DHT22)からのデータをmatplotlibでリアルタイムグラフ化」と「GrovePi+で時刻毎に温湿度センサーデーターを得る方法」を融合し、さらに、CSVファイルから再読み込みのため通し番号を一列目に付けるようにしました。(確認用のprint()関数では2列目ですが)CSVファイルのサンプルはここ「temp01.csv」をダウンロード です。本グラフも次にCSVから起こすグラフもそうですが、右に行くほどデータが古くなるグラフとなっています。これは、グラフデータの見せ方としては左右が逆になっており、当方のmatplotlibでの描き方の力量不足によるものです。グラフのX軸が日時毎に示すグラフで右端いっぱいになったところで左端からデータと同時にX軸も切り落として行くグラフに作れれば良かったのですが。
次に、CSVファイルをmatplotlibでグラフ化です。まず、pythonにCSVファイルから変数に1行目の列名を除き、値を転写します。その後、グラフ化し易いよう行列を変換し、リアルタイムグラフと同様になるよう最右列の数値がグラフ左側(0分)となり最左列の数値をグラフ右側としたいので通し番号を逆順にしてからグラフ化しました。さらに、グラフを横長にし、1日の温湿度データを示せる様にしました。また、グラフ化に当たり画面上に示すほかにPNGファイルに画像ファイル化出来るようにしました。
PNG画像ファイル化したCSVファイルから読み込んだ24時間グラフ(三軸)
また、利用にあたっての注意点があります。それは、リアルタイムグラフを動かしながらCSVファイルのグラフを作成すると少々問題が生じます。それは、測定途中で温度と湿度でnan表示が出た後に温度と湿度で-1表示が延々続くことです。理由ははっきり分かりませんが、CSVファイルの読み込み時のバッティングか、matplotlibのバッティングかによりエラーが生じるようです。
詳細説明の前に前提条件ですがRaspberryPi2でPython3、matplotlib2.0.0、pygame1.9.3を使用しています。
これから、詳細説明です。最初のソフトの融合ですが、既に1分毎に、グラフ化してCSVファイルを作ることをするわけですが、
dt = datetime.datetime.now()
if str(dt.second) == "0":
sleep(1)
sleep(0.1)
をどこに入れ込むかと言うことですが、これは、迷わず
while True:
のすぐ次ぎの行に、
dt = datetime.datetime.now()
if str(dt.second) == "0":
while True:の終わる。ところに
sleep(1)
sleep(0.1)
を入れ込みました。当然if文が入ってきますので元のソースコードは4字分のインデントが入ります。そして、
sleep(55.53)#約1分でグラフ描画されるよう調整
print(datetime.datetime.now())
の行を削ります。
次に、CSVファイルですが、まず、最上部の表題行をグラフ下地が出来た後に入れました。更に、センサーから取り込んだデーターを入れ込む行ですが、これは、グラフの補間が終わった後に入れ込みました。そこには、ソースコードで示すように先頭列に「番号」を表題行及びデーター行へ入れ込みました。
そして、CSVファイルの頭に通し番号を入れるためnumberと言う変数を作りました。numberは、整数の0から始まり、
sleep(1)
sleep(0.1)
の直前に、1を加えるようにしています。
何だか、ウジャウジャ書くとよく分かりませんが、そこはソースコードご覧ください。 プログラムを停止する場合はpygame窓の閉じるボタンを押すと1分以内に止まります。あと、お決まりですがgrove_dht.pyとgrovepi.pyを本プログラムと同じディレクトリ(フォルダー)に入れる必要があります。
dt = datetime.datetime.now()
if str(dt.second) == "0":
sleep(1)
sleep(0.1)
をどこに入れ込むかと言うことですが、これは、迷わず
while True:
のすぐ次ぎの行に、
dt = datetime.datetime.now()
if str(dt.second) == "0":
while True:の終わる。ところに
sleep(1)
sleep(0.1)
を入れ込みました。当然if文が入ってきますので元のソースコードは4字分のインデントが入ります。そして、
sleep(55.53)#約1分でグラフ描画されるよう調整
print(datetime.datetime.now())
の行を削ります。
次に、CSVファイルですが、まず、最上部の表題行をグラフ下地が出来た後に入れました。更に、センサーから取り込んだデーターを入れ込む行ですが、これは、グラフの補間が終わった後に入れ込みました。そこには、ソースコードで示すように先頭列に「番号」を表題行及びデーター行へ入れ込みました。
そして、CSVファイルの頭に通し番号を入れるためnumberと言う変数を作りました。numberは、整数の0から始まり、
sleep(1)
sleep(0.1)
の直前に、1を加えるようにしています。
何だか、ウジャウジャ書くとよく分かりませんが、そこはソースコードご覧ください。 プログラムを停止する場合はpygame窓の閉じるボタンを押すと1分以内に止まります。あと、お決まりですがgrove_dht.pyとgrovepi.pyを本プログラムと同じディレクトリ(フォルダー)に入れる必要があります。
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import pygame
from pygame.locals import *
import sys
import time
import datetime
import grovepi#GrovePiを使うときに必要
import math
import csv
from time import sleep #ループを1分ごとに動作させるためsleep()を使うため必要
from matplotlib.font_manager import FontProperties#日本語化に必要#Grove Temperature & Humidity Sensor ProをGrovePiのD4ポートにつなぐ
#このファイルで the White colored sensorを使うため以下を設定
#ケーブル線はSIG,NC,VCC,GND
sensor = 4 # デジタルポート4に設定(後でgrovepiで変数を渡す)#温湿度センサーの型(タイプ)
#センサーの色
blue = 0 # The Blue(青) colored sensor.
white = 1 # The White(白) colored sensor.font_path = '/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf'#日本語化
font_prop = FontProperties(fname=font_path)
plt.rcParams['font.family'] = font_prop.get_name()def main():
temps = [0.0]*120 # 気温格納
humis = [0.0]*120 # 湿度格納
zyoukis = [0.0]*120 # 水蒸気量格納
t = np.arange(0,120,1) # 横軸
number=0 # 通し番号を整数値0から始めるよう定義
pygame.init() # Pygameを初期化
screen = pygame.display.set_mode((300, 100)) # 画面作成(横300×100)
pygame.display.set_caption("温湿度") # タイトルバー
font = pygame.font.Font(None, 50) # 文字の設定
#グラフ下地
fig, host = plt.subplots()
fig.subplots_adjust(right=0.75)#X軸の長さ
#CSVファイル最上部標題行入力
with open('temp01.csv','w',encoding='utf-8')as f:#ファイルオープン'W'は上書きモード。encodingはExcelの場合'shift_jis'の方がよいとも聞く
writer=csv.writer(f, lineterminator='\n')
writer.writerow(['番号','日時','気温(℃)','湿度(%)','水蒸気量(g/m3)'])
while True:
dt = datetime.datetime.now()
if str(dt.second) == "0":
screen.fill((0,0,0)) # 画面のクリア
temp,humi = grovepi.dht(sensor,white)# grovepi内のdht関数にポート番号と型番号を渡し気温、湿度を受け取る
temp = str(temp) # 気温を文字列にする
humi = str(humi) # 湿度を文字列にする
text = font.render(temp + "[℃]" + humi +"[%]", False, (255,255,255)) # 表示する文字の設定
screen.blit(text, (10, 10)) # レンダ,表示位置
pygame.display.flip() # 画面を更新して、変更を反映
# 温度データのリスト更新
temps.pop(119)
humis.pop(119)
zyoukis.pop(119)
temps.insert(0,float(temp)) # 文字列にしたデータ(温度)を少数を含む数値に変換
humis.insert(0,float(humi)) # 文字列にしたデータを(湿度)少数を含む数値に変換
zyoukis.insert(0,(0.794*(humis[0]/100*6.1078*pow(10,(7.5*temps[0])/(temps[0]+237.3))))/(1 +0.00366*temps[0])) # 気温及び相対湿度より空気1立米当たり水蒸気量に変換
print(dt.strftime("%Y-%m-%d %H:%M:%S.%f")+" %i temp = %.1f C humi = %.1f %% zyouki = %.2f g/m^3" % (number,temps[0],humis[0],zyoukis[0]))
# センサーがエラーになってもグラフを補間するためエラーの場合前の数字を代入
if str(temps[0])=='nan' and str(humis[0])=='nan':
temps[0]=temps[1]
humis[0]=humis[1]
zyoukis[0]=zyoukis[1]tt=dt.strftime("%Y/%m/%d %H:%M:%S")#記録日時をtへ代入(端数なし)。
#データをCSV化
with open('temp01.csv','a',encoding='utf-8')as f:#ファイルオープン'a'は追記モード。encodingはExcelの場合'shift_jis'の方がよいとも聞く
writer=csv.writer(f, lineterminator='\n')#末尾は改行としているが、デフォルトも改行のようである。
writer.writerow([number,tt,str(temps[0]),str(humis[0]),str(zyoukis[0])])#データ書き込み。
# f.close()# グラフ表示設定
host = plt.subplot()
par1 = host.twinx()
par2 = host.twinx()
par2.spines["right"].set_position(("axes", 1.2))#右に3番めY軸メモリを作成
p1, = host.plot(t, temps, 'r-',label="気温[℃]") # X軸、Y軸更新
p1.set_ydata(temps)
p2, = par1.plot(t, humis, 'b-',label="湿度[%]") # X軸、Y軸更新
p2.set_ydata(humis)
p3, = par2.plot(t, zyoukis, 'g-',label="水蒸気量[$g/m^3$]") #X軸、Y軸更新
p3.set_ydata(zyoukis)
host.set_xlim(0, 120)#X軸の範囲指定
host.set_ylim(20, 35)#Y1軸の範囲指定
par1.set_ylim(0, 100)#Y2軸の範囲指定
par2.set_ylim(0, 25)#Y3軸の範囲指定
plt.title("Real-time 気温 湿度、水蒸気量[$g/m^3$]")
host.set_xlabel("Time [min]")
host.set_ylabel("気温 [℃]")
par1.set_ylabel("湿度[%]")
par2.set_ylabel("水蒸気量[$g/m^3$]")
tkw = dict(size=4, width=1.5)#メモリの大きさと太さ
host.yaxis.label.set_color(p1.get_color())#Y1軸ラベルの文字色
par1.yaxis.label.set_color(p2.get_color())#Y2軸ラベルの文字色
par2.yaxis.label.set_color(p3.get_color())#Y3軸ラベルの文字色
lines = [p1, p2, p3]
host.legend(lines, [l.get_label() for l in lines])#凡例
plt.grid()#グラフ補助線の記載
plt.pause(.01)#グラフをFigure1に記載(プロット)
plt.clf()#表示窓をクリアにする
for event in pygame.event.get():
# ウィンドウを閉じるボタンが押されたら終了処理
if event.type == QUIT:
pygame.quit()
plt.close()
f.close()
sys.exit()
number=number+1 # 次の通し番号の作成
sleep(1)
sleep(0.1)if __name__ == '__main__':
main()
それではその次のCSVファイルをmatplotlibでグラフ化です。再度、申し上げますが、リアルタイムグラフと同じ感覚で見られるよう、本グラフは左側に行くほど新しいデータになるようしています。
まず、pythonにCSVファイルから変数に1行目の列名を除き、値を転写します。最初はCSVライブラリーを使おうと思ったのですが、「mwSoft>csv/tsvファイルの読み書き」で当方の欲しい事例が示されていたので、numpyを採用しました。
np.loadtxt('temp01.csv',delimiter=',',skiprows=1, dtype=[('col1', 'i'), ('col2', 'S19'), ('col3', 'f4'), ('col4', 'f4'), ('col5', 'f8')])
によりnp.loadtxt()で読み込みます。ファイルはtemp.csvを表の区分けは','です。次が表題行があるので1行飛ばします。その次からデータの読み込み形式です。最初の列(カラム)は通し番号なので整数'i'です。次が日時で半角19文字の文字列、3番目が温度で本当は小数点以下1桁までで良いのですがf1では動作せず2の累乗で作動するようです。ここではf4としていますが、実際には小数点以下6桁となっており、CSVファイルで示された値とは微妙に違っていたりします。(理由は分かりません)、4番目は3番目と同じ、5番目はCSVファイルと同じで小数点以下14桁や15桁になっていたりします。
次にデータの行列変換ですが、「Qiita>Python標準で転置行列」を参考にしています。CSVファイルからPythonにデータを転写した時にタプルとなってしまうので、それもこのときmap()を使ってリストに変換します。試行錯誤をして次のように変数priに渡せば行列変換の後にリスト変換してくれることが分かりましたのでそうしています。(中身はよく分かっていません)
pri=list(map(list, zip(*reader)))
三番目に、通し番号を逆順にします。「Qiita>Python リストの逆順」を参考にしています。
for i in (reversed(pri[0][:])):
pri[0][nam]=i
nam=nam+1
pri変数の一行目を逆順にするというコードです。何だか、これで良いのかチョット考えてしまいます。nam=0とこのコードを省けば、グラフは逆順になりません。
四番目にグラフの横長化です。「ネットワークエンジニアの小学校>Pythonでログ解析(7) ~CSVファイルからグラフを作成する!~」に載っていたグラフの大きさを変えるplt.figure(figsize=(12,3))より横1200ピクセル、縦300ピクセルと出来るとのことだったので本ソースコードでは更に横長にするため、元のソースコードfig, host = plt.subplots()の中にfigsize=(12,3)をまず入れてみて表示の画面サイズも変更できることを確認しました。徐々に枠を広げていってfigsize=(19,5)が当方がモニターとして使っているテレビで最も横幅が長く縦はデフォルト設定に近いためこの値としました。これに合わせて三つのX軸が適当な位置になるように調整しました。
グラフ下地のfig, host = plt.subplots(figsize=(19,5))下の行の値を試行錯誤で調整しました。
fig, host = plt.subplots(figsize=(19,5))
fig.subplots_adjust(left=0.05,right=0.93)
更に、三番目のX軸については、
par2.spines["right"].set_position(("axes", 1.04))
としました。
五番目に1日の温湿度データを示せる様に
host.set_xlim(0, 1440)
としました。
六番目にPNGファイルに画像ファイル化出来るように「チラシ裏のラクガキ>Matplotlibの高解像度画像保存」を参考に、
plt.savefig("temp.png",format = 'png', dpi=300)
行を加えました。
これで、ようやく完成です。全体のソースコードを載せておきます。
まず、pythonにCSVファイルから変数に1行目の列名を除き、値を転写します。最初はCSVライブラリーを使おうと思ったのですが、「mwSoft>csv/tsvファイルの読み書き」で当方の欲しい事例が示されていたので、numpyを採用しました。
np.loadtxt('temp01.csv',delimiter=',',skiprows=1, dtype=[('col1', 'i'), ('col2', 'S19'), ('col3', 'f4'), ('col4', 'f4'), ('col5', 'f8')])
によりnp.loadtxt()で読み込みます。ファイルはtemp.csvを表の区分けは','です。次が表題行があるので1行飛ばします。その次からデータの読み込み形式です。最初の列(カラム)は通し番号なので整数'i'です。次が日時で半角19文字の文字列、3番目が温度で本当は小数点以下1桁までで良いのですがf1では動作せず2の累乗で作動するようです。ここではf4としていますが、実際には小数点以下6桁となっており、CSVファイルで示された値とは微妙に違っていたりします。(理由は分かりません)、4番目は3番目と同じ、5番目はCSVファイルと同じで小数点以下14桁や15桁になっていたりします。
次にデータの行列変換ですが、「Qiita>Python標準で転置行列」を参考にしています。CSVファイルからPythonにデータを転写した時にタプルとなってしまうので、それもこのときmap()を使ってリストに変換します。試行錯誤をして次のように変数priに渡せば行列変換の後にリスト変換してくれることが分かりましたのでそうしています。(中身はよく分かっていません)
pri=list(map(list, zip(*reader)))
三番目に、通し番号を逆順にします。「Qiita>Python リストの逆順」を参考にしています。
for i in (reversed(pri[0][:])):
pri[0][nam]=i
nam=nam+1
pri変数の一行目を逆順にするというコードです。何だか、これで良いのかチョット考えてしまいます。nam=0とこのコードを省けば、グラフは逆順になりません。
四番目にグラフの横長化です。「ネットワークエンジニアの小学校>Pythonでログ解析(7) ~CSVファイルからグラフを作成する!~」に載っていたグラフの大きさを変えるplt.figure(figsize=(12,3))より横1200ピクセル、縦300ピクセルと出来るとのことだったので本ソースコードでは更に横長にするため、元のソースコードfig, host = plt.subplots()の中にfigsize=(12,3)をまず入れてみて表示の画面サイズも変更できることを確認しました。徐々に枠を広げていってfigsize=(19,5)が当方がモニターとして使っているテレビで最も横幅が長く縦はデフォルト設定に近いためこの値としました。これに合わせて三つのX軸が適当な位置になるように調整しました。
グラフ下地のfig, host = plt.subplots(figsize=(19,5))下の行の値を試行錯誤で調整しました。
fig, host = plt.subplots(figsize=(19,5))
fig.subplots_adjust(left=0.05,right=0.93)
更に、三番目のX軸については、
par2.spines["right"].set_position(("axes", 1.04))
としました。
五番目に1日の温湿度データを示せる様に
host.set_xlim(0, 1440)
としました。
六番目にPNGファイルに画像ファイル化出来るように「チラシ裏のラクガキ>Matplotlibの高解像度画像保存」を参考に、
plt.savefig("temp.png",format = 'png', dpi=300)
行を加えました。
これで、ようやく完成です。全体のソースコードを載せておきます。
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import sys
import math
from matplotlib.font_manager import FontProperties#日本語化に必要font_path = '/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf'#日本語化
font_prop = FontProperties(fname=font_path)
plt.rcParams['font.family'] = font_prop.get_name()def main():
nam=0
reader = np.loadtxt('temp01.csv',delimiter=',',skiprows=1, dtype=[('col1', 'i'), ('col2', 'S19'), ('col3', 'f2'), ('col4', 'f2'), ('col5', 'f8')])#CSVファイルの読み込み
pri=list(map(list, zip(*reader)))#行列変換
#リストの逆順
for i in (reversed(pri[0][:])):
pri[0][nam]=i
nam=nam+1
# print(pri)
#グラフ下地
fig, host = plt.subplots(figsize=(19,5))#グラフ枠横縦の大きさ
fig.subplots_adjust(left=0.05,right=0.93)#X軸の長さ
# グラフ表示設定
host = plt.subplot()
par1 = host.twinx()
par2 = host.twinx()
par2.spines["right"].set_position(("axes", 1.04))#右に3番めY軸メモリを作成
p1, = host.plot(pri[0], pri[2], 'r-',label="気温[℃]") # X軸、1番目Y軸の値
p1.set_ydata(pri[2])
p2, = par1.plot(pri[0], pri[3], 'b-',label="湿度[%]") # X軸、2番目Y軸の値
p2.set_ydata(pri[3])
p3, = par2.plot(pri[0], pri[4], 'g-',label="水蒸気量[$g/m^3$]") # X軸、3番目Y軸の値
p3.set_ydata(pri[4])
host.set_xlim(0, 1440)#X軸の範囲指定(1440分=1日)
host.set_ylim(20, 35)#Y1軸の範囲指定(季節によって変えた方が良いかも)
par1.set_ylim(0, 100)#Y2軸の範囲指定
par2.set_ylim(0, 25)#Y3軸の範囲指定
plt.title("time 気温 湿度、水蒸気量[$g/m^3$]")
host.set_xlabel("Time [min]")
host.set_ylabel("気温 [℃]")
par1.set_ylabel("湿度[%]")
par2.set_ylabel("水蒸気量[$g/m^3$]")
tkw = dict(size=4, width=1.5)#メモリの大きさと太さ
host.yaxis.label.set_color(p1.get_color())#Y1軸ラベルの文字色
par1.yaxis.label.set_color(p2.get_color())#Y2軸ラベルの文字色
par2.yaxis.label.set_color(p3.get_color())#Y3軸ラベルの文字色
lines = [p1, p2, p3]
host.legend(lines, [l.get_label() for l in lines])#凡例
plt.grid()#グラフ補助線の記載
plt.savefig("temp01.png",format = 'png', dpi=300)#高解像度画像ファイル保存
plt.show()
if __name__ == '__main__':
main()
さて、今度はリアルタイムグラフが右に行くほど新しい数値になるようにしてみたいと思います。途中でくじけちゃうかもしれませんが。
« 安田純平氏は解放されて良かったが | トップページ | GrovePi+の温湿度センサーから得たCSVファイルをmatplotlibでグラフ化(その2) »
「パソコン・インターネット」カテゴリの記事
- logicool M720 Triathlon マルチデバイスマウスが故障(2022.08.20)
- UbuntuのFirefoxだと雑音が入る(未解決)(2022.02.13)
- イントロンのブログが60,000カウントを達成(2022.01.13)
- GrovePi+をRaspberry Pi 4に導入してみた(上手くゆかなかった)(2021.12.26)
- Raspberry Pi 4でVLCメディアプレイヤー起動時にフリーズがなぜか解消(2021.12.20)
« 安田純平氏は解放されて良かったが | トップページ | GrovePi+の温湿度センサーから得たCSVファイルをmatplotlibでグラフ化(その2) »


コメント