質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

Q&A

解決済

2回答

2308閲覧

グラフ横軸の時間目盛りの最小、最大を設定したい

F91_

総合スコア16

Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

0グッド

0クリップ

投稿2021/08/13 15:39

編集2021/08/22 14:05

前提・実現したいこと

機械稼働状況グラフの横軸表示を7:00スタートで24:00時終了としたい。
イメージ説明
グラフで機械が稼働している時間と停止している時間を可視化する事が目的です。
※青塗りつぶしが稼働中、無色は停止(y軸は1が最大、0が最小)
###ラズパイ光センサーのデータ記録方法
ラズパイ光センサーにて光を読み取った場合にラズパイ内のエクセルに書き込みし、matplotlibでエクセルデータ読み取り、リアルタイムにグラフ更新をかけています。このグラフにより、機械稼働状況の見える化を行っています。データ記録方法は稼働していた場合にシグナルタワーの稼働ランプを読み取り、現在時刻と1を記録、停止していた場合は停止ランプが作動するので現在時刻と0を記録、無人稼働の場合は稼働ランプと停止ランプの両方が点灯し、現在時刻と2列目に1と記録します。機械の電源が入っていない場合は記録がされないようにしています。光を読み取った時間を記録し、それをグラフにしているだけなのでその日の最初に読み取った時間がグラフにて表示される時間(画像では22:29スタート、終了23:38)となってしまってますが、グラフ表示はスタートを7:00、終了を24:00としたいです。※機械の電源が入っていない時間(シグナルタワーの点灯が全て消灯)も常に読み取るようにすることも考えましたが、休日など常に記録が続いてしまい、エクセルファイルが大量になってしまうので断念しました。
##機械稼働状況をシグナルタワーの点灯パターンにより記録しているコード

python

1dissolution=1 #計測分解能[s] 2rate=0 3def readadc(adcnum,clockpin,mosipin,misopin,cspin): 4 if adcnum> 7 or adcnum<0: 5 return -1 6 GPIO.output(cspin,GPIO.HIGH) 7 GPIO.output(clockpin,GPIO.LOW) 8 GPIO.output(cspin,GPIO.LOW) 9 commandout=adcnum 10 commandout |=0x18 11 commandout<<=3 12 for i in range(5): 13 if commandout & 0x80: 14 GPIO.output(mosipin,GPIO.HIGH) 15 else: 16 GPIO.output(mosipin,GPIO.LOW) 17 commandout<<=1 18 GPIO.output(clockpin,GPIO.HIGH) 19 GPIO.output(clockpin,GPIO.LOW) 20 21 adcout=0 22 for i in range(13) 23 GPIO.output(clockpin,GPIO.HIGH) 24 GPIO.output(clockpin,GPIO.LOW) 25 adcout<<=1 26 if i>0 and GPIO.input(misopin)==GPIO.HIGH: 27 adcout |=0x1 28 GPIO.output(cspin,GPIO.HIGH) 29 return adcout 30GPIO.setmode(GPIO.BCM) 31SPICS=8 32SPIMISO=9 33SPIMOSI=10 34SPICLK=11 35GPIO.setup(SPICLK,GPIO.OUT) 36GPIO.setup(SPIMOSI,GPIO.OUT) 37GPIO.setup(SPIMISO,GPIO.IN) 38GPIO.setup(SPICS,GPIO.OUT) 39today1=datetime.datetime.now() 40start=time.time() 41kikai='D-'+'32' 42try: 43 while True: 44 inputVal0=readadc(0,SPICLK,SPIMOSI,SPIMISO,SPICS) 45 inputVal1=readadc(1,SPICLK,SPIMOSI,SPIMISO,SPICS) 46 now=datetime.datetime.now() 47 now6=now-datetime.timedelta(hours=6) 48 nowday='{0:%Y%m%d}'.format(now6) 49 nt1=now.strftime('%H:%M') 50 nt='{0:%H%M}'.format(now) 51 52 if inputVal0>2000 and inputVal1<2000: 53 time1=time1+1 54 time2=time2+1 55 time10=time10+1 56 if time10 == 60: 57 rcd=[] 58 rcd.append(now.strftime('%H:%M')) 59 rcd.append(1) 60 rcd.append(0) 61 f=open('mydata'+nowday+'.csv','a',newline='') 62 wrtr=csv.writer(f,delimiter=',') 63 wrtr.writerow(rcd) 64 f.close() 65 time10=0 66 data=pd.read_csv('mydata'+nowday+'.csv') 67 x=data.iloc[:,0] 68 x1=x.count() 69 y=data.iloc[:,1] 70 y1=y.sum() 71 z=round(y1/x1*100,1) 72 canvas1.create_text(85,20,text=('機械'),font=("",30,"roman"),tag='Y') 73 canvas2.create_text(200,20,text=('稼働状況'),font=("",30,"roman"),tag='Y') 74 canvas3.create_text(200,20,text=('本日の稼働率'),font=("",30,"roman"),tag='Y') 75 canvas4.create_text(85,20,text=(kikai),font=("",30,"roman"),tag='Y') 76 canvas5.create_text(100,20,text=('稼働中'),font=("",30,"roman"),tag='Y') 77 canvas6.create_text(100,20,text=(''),font=("",30,"roman"),tag='Y') 78 canvas7.create_text(200,20,text=(str(z)+'%'),font=("",40,"roman"),tag='Y') 79 canvas1.update() 80 canvas2.update() 81 canvas3.update() 82 canvas4.update() 83 canvas5.update() 84 canvas6.update() 85 canvas7.update() 86 canvas1.delete('Y') 87 canvas2.delete('Y') 88 canvas3.delete('Y') 89 canvas4.delete('Y') 90 canvas5.delete('Y') 91 canvas6.delete('Y') 92 canvas7.delete('Y') 93 if inputVal1>2000 and inputVal0<2000 94 time1=time1+1 95 time3=time3+1 96 time7=time7+1 97 time11=time11+1 98 if time11 == 60: 99 rcd=[] 100 rcd.append(now.strftime('%H:%M')) 101 rcd.append(0) 102 rcd.append(0) 103 f=open('mydata'+nowday+'.csv','a',newline='') 104 wrtr=csv.writer(f,delimiter=',') 105 wrtr.writerow(rcd) 106 f.close() 107 time11=0 108 data=pd.read_csv('mydata'+nowday+'.csv') 109 x=data.iloc[:,0] 110 x1=x.count() 111 y=data.iloc[:,1] 112 y1=y.sum() 113 z=round(y1/x1*100,1) 114 canvas1.create_text(85,20,text=('機械'),font=("",30,"roman"),tag='Y') 115 canvas2.create_text(200,20,text=('稼働状況'),font=("",30,"roman"),tag='Y') 116 canvas3.create_text(200,20,text=('本日の稼働率'),font=("",30,"roman"),tag='Y')117 canvas4.create_text(85,20,text=(kikai),font=("",30,"roman"),tag='Y') 118 canvas5.create_text(100,20,text=(''),font=("",30,"roman"),tag='Y') 119 canvas6.create_text(100,20,text=('停止中'),font=("",30,"roman"),tag='Y') 120 canvas7.create_text(200,20,text=(str(z)+'%'),font=("",40,"roman"),tag='Y') 121 canvas1.update() 122 canvas2.update() 123 canvas3.update() 124 canvas4.update() 125 canvas5.update() 126 canvas6.update() 127 canvas7.update() 128 canvas1.delete('Y') 129 canvas2.delete('Y') 130 canvas3.delete('Y') 131 canvas4.delete('Y') 132 canvas5.delete('Y') 133 canvas6.delete('Y') 134 canvas7.delete('Y') 135 if inputVal1<2000 and inputVal0<2000: 136 time1=time1+1 137 time3=time3+1 138 time5=time5+1 139 elapsed_time=time.time()-start #(処理時間)=(処理が終わった時間)ー(処理を始めた時間) 140 a=dissolution - elapsed_time 141 sleep(a) 142 start=time.time() 143except KeyboardInterrupt: #cntl+Cで停止 144 pass 145GPIO.cleanup()

光センサーにて記録しているエクセルデータ

ラズベリーパイ光センサーにて、60秒稼働稼働ランプを読み取った場合にエクセルデータ1列目に現在時刻、2列目に1、3列目に0と記録、停止ランプを60秒読み取った場合に1列目に現在時刻、2列目に0と記録、3列目に0、電源が入っていない場合は何も記録なし、無人稼働の場合(稼働ランプ、停止ランプが両方点灯)は2列目が0で3列目が1なります。
イメージ説明

エクセルデータをplotしている該当のソースコード

python

1コード 2try: 3 while True: 4 inputVal0=readadc(0,SPICLK,SPIMOSI,SPIMISO,SPICS) 5 inputVal1=readadc(1,SPICLK,SPIMOSI,SPIMISO,SPICS) 6 now=datetime.datetime.now() 7 now6=now-datetime.timedelta(hours=6) 8 nowday='{0:%Y%m%d}'.format(now6) 9 if inputVal0>2000: 10 time1=time1+1 11 time2=time2+1 12 time10=time10+1 13 if time10 == 60: 14 data=pd.read_csv('mydata'+nowday+'.csv') 15 x=data.iloc[:,0] 16 x1=x.count() 17 y=data.iloc[:,1] 18 y1=y.sum()#2行目の合計 19 z=round(y1/x1*100,1) 20 w=round(y1/60,1) 21 print('稼働時間'+str(w)+'時間') 22 print('機械稼働率'+str(z)+'%') 23 s='稼働時間'+str(w)+'時間'+'機械稼働率'+str(z)+'%' 24 plt.xlabel("時刻") 25 plt.ylabel("1=稼働,0=停止") 26 plt.text(1000,0.4,s)#グラフにコメントを入れる 27 plt.bar(x,y,width=1.0) 28 plt.gca().xaxis.set_major_locator(ticker.MultipleLocator(60)) 29 plt.subplots_adjust(left=0.05, right=1, bottom=0.15, top=1) 30 plt.draw() 31 plt.pause(1) 32 plt.clf() 33 time10=0 34 35 else: 36 if inputVal1>2000: 37 time1=time1+1 38 time3=time3+1 39 time7=time7+1 40 time11=time11+1 41 print(now.strftime('%H:%M')) 42 43 if time11 == 60: 44 data=pd.read_csv('mydata'+nowday+'.csv') 45 x=data.iloc[:,0] 46 x1=x.count() 47 y=data.iloc[:,1] 48 y1=y.sum()#2行目の合計 49 z=round(y1/x1*100,1) 50 w=round(y1/60,1) 51 print('稼働時間'+str(w)+'時間') 52 print('機械稼働率'+str(z)+'%') 53 s='稼働時間'+str(w)+'時間'+'機械稼働率'+str(z)+'%' 54 plt.xlabel("時刻") 55 plt.ylabel("1=稼働,0=停止") 56 plt.text(1000,0.4,s) 57 plt.bar(x,y,width=1.0) 58 plt.gca().xaxis.set_major_locator(ticker.MultipleLocator(60)) 59 plt.subplots_adjust(left=0, right=1, bottom=0.15, top=1) 60 plt.draw() 61 plt.pause(1) 62 plt.clf() 63 time11=0 64 else: 65 time1=time1+1 66 time3=time3+1 67 68 69 elapsed_time=time.time()-start #(処理時間)=(処理が終わった時間)ー(処理を始めた時間) 70 a=dissolution - elapsed_time 71 sleep(1) 72 start=time.time() 73 74except FileNotFoundError: 75 pass

###plt.xlim(07:00,24:00)を入れた場合
7:00と24:00のデータがない場合もあるので8:06~8:23のデータを読み取りましたが、時刻の表示がありませんでした。作業者の機械稼働スタートが何らかの理由で7:30などになってしまう事もあり、逆に24時以前に機械電源オフとなり記録がなくなることもあります。常に7:00~24:00のデータがあるのであればxlimで対応出来ると思いました。
イメージ説明
イメージ説明

##ラズパイに保管しているエクセルデータ
イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

bsdfan

2021/08/17 08:41

csvから読み取った時刻データの型はどうなってますか? 文字列のままプロットしているのでは?
F91_

2021/08/18 03:53 編集

strftime('%H:%M')にてエクセル書き込みしてからそれをそのまま読み取っていますので文字列のままと考えています。plt.xlim(07:00,24:00)が数値に対して文字なので表示されないって事も考えられますかね?それも考えましたが、pd.read_csvにて読み取る際に数値への変換方法などあるか調べたりしていましたが上手くいっていません。
bsdfan

2021/08/18 05:25

文字列でplotすると、categoricalとして扱われるので、上限とか下限とかの概念が存在しません。 07:00~24:00までプロットしたかったら、x軸の値を '07:00'から'24:00'まで埋めてやらないといけません。(ppaulさんの回答の考え方) xlimで制御したいなら、なんらかの数値データに変換してやる必要があります。 が、pandasで時刻だけのデータを扱いにくいのと、24:00というのも時刻として扱えません(23:59までで24:00は翌日の00:00)ので、ちょっと面倒なところがありそうです。
F91_

2021/08/18 11:53

plotをstrftimeで変換せずに700~2400の数値でやってみたりしましたが、この場合760なども存在してしまい、グラフに空白が出来てさすがに時間として見づらいので断念しました。やはり変換方法を考えるしかなさそうですね・・・
guest

回答2

0

ベストアンサー

一般的なことを言えば、

plt.xlim(下限値, 上限値)

で、グラフに表示するX軸の範囲を設定することができます。


以下プロット例です(plt.xlim()ではなくax.set_xlim()を使用していますが、やっていることは同じです)。

イメージ説明

データは「matplotlibにて読み取っているエクセルデータ」に貼っていただいたものを使っていますが(データの読み込みに使っているのはmatplotlibではなくpandasでしょう)、実際には記録自体がされていない時間もあるとのことなので(「光センサーにて記録しているエクセルデータ」の図も23:27と23:28が欠けている)、8時台の連続した2分、9時台からも1分のデータを間引いています。

"稼働時間 xx 時間 機械稼働率 xx %" の文字列については、現在の実際のプロットでどこに配置されているのか質問文からは分からなかったので、適当なところに配置しています。

y軸のラベル "1=稼働,0=停止" は削除して、代わりに軸目盛りラベルを [0, 1] から [停止, 稼働] に変更しています。

上記プロットのコードは以下です。

python

1import datetime 2from io import StringIO 3 4import pandas as pd 5import matplotlib.pyplot as plt 6import matplotlib.dates as mdates 7 8operation_data = """7:58 1 0 97:59 1 0 108:00 1 0 118:01 1 0 128:02 1 0 13(中略) 1410:18 1 0 1510:19 1 0 1610:20 1 0 1710:21 1 0 1810:22 1 0""" 19 20data = pd.read_csv(StringIO(operation_data), sep='\s+', header=None, names=[0,1], index_col=0, parse_dates=True) 21data = data.asfreq('min', fill_value=0) 22observed_data_num = len(data) 23 24d_today = datetime.date.today() 25dt_start = datetime.datetime(d_today.year, d_today.month, d_today.day, 0, 1) 26df_start = pd.DataFrame([[0, 0]], index=[dt_start]) 27 28d_tomorrow = d_today + datetime.timedelta(days=1) 29dt_end = datetime.datetime(d_tomorrow.year, d_tomorrow.month, d_tomorrow.day) 30df_end = pd.DataFrame([[0, 0]], index=[dt_end]) 31 32data = df_start.append([data, df_end]) 33data = data.asfreq('min', fill_value=0) 34 35running_data_num = data[0].sum() 36operation_rate = round(running_data_num / observed_data_num * 100, 1) 37operation_time = round(observed_data_num / 60, 1) 38operation_info = f"稼働時間 {operation_time} 時間 機械稼働率 {operation_rate} %" 39 40fig = plt.figure(figsize=(12.8, 4.8)) 41ax = fig.add_subplot() 42 43ax.text(0.5, 1.03, operation_info, transform=ax.transAxes, fontsize=12, horizontalalignment="center") 44 45ax.bar(data.index, data[0], width=0.0007) 46 47ax.xaxis.set_major_locator(mdates.HourLocator(interval=1)) 48ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) 49ax.set_xlim( 50 xmin=datetime.datetime(d_today.year, d_today.month, d_today.day, 7, 0), 51 xmax=dt_end 52) 53ax.set_xlabel("時 刻", fontsize='large', labelpad=10) 54 55ax.set_yticks([0, 1]) 56ax.set_yticklabels(["停止", "稼働"]) 57 58plt.tight_layout() 59plt.show()

データの扱いについては説明が必要かと思いますので、以下に説明します。

python

1data = pd.read_csv(StringIO(operation_data), sep='\s+', header=None, names=[0,1], index_col=0, parse_dates=True)

header=Noneでデータの1行目が列名として読み込まれることを防ぎます。
names=[0,1]で列名を振り直します(後でDataFrameを結合するために必要)。
index_col=0, parse_dates=Trueで時間の列を日付型(datetime64[ns]型)のインデックスとして読み込みます。日付は自動的に今日の日付が設定されます。

読み込み結果は次のようになります。

01
2021-08-19 07:58:0010
2021-08-19 07:59:0010
2021-08-19 08:00:0010
2021-08-19 08:01:0010
2021-08-19 08:02:0010
...
2021-08-19 10:18:0010
2021-08-19 10:19:0010
2021-08-19 10:20:0010
2021-08-19 10:21:0010
2021-08-19 10:22:0010

python

1data = data.asfreq('min', fill_value=0) 2observed_data_num = len(data)

data.asfreq('min', fill_value=0)で、欠落しているインデックス(記録が行われていない時間)を分単位で補完し、値を 0 で埋めます。たとえば

01
...
2021-08-19 08:26:0010
2021-08-19 08:29:0010
...

の箇所は

01
...
2021-08-19 08:26:0010
2021-08-19 08:27:0000
2021-08-19 08:28:0000
2021-08-19 08:29:0010
...

と補完されます。

observed_data_num = len(data)でデータ数(何分観測したか)を取得しておきます。

python

1d_today = datetime.date.today() 2dt_start = datetime.datetime(d_today.year, d_today.month, d_today.day, 0, 1) 3df_start = pd.DataFrame([[0, 0]], index=[dt_start])

その日の 0 時 1 分のデータを作成します。値は 0 で埋めます。df_startの内容は以下です。

01
2021-08-19 00:01:0000

python

1d_tomorrow = d_today + datetime.timedelta(days=1) 2dt_end = datetime.datetime(d_tomorrow.year, d_tomorrow.month, d_tomorrow.day) 3df_end = pd.DataFrame([[0, 0]], index=[dt_end])

その日の翌日の 0 時 0 分のデータを作成します。値は 0 で埋めます。df_endの内容は以下です。

01
2021-08-20 00:00:0000

python

1data = df_start.append([data, df_end]) 2data = data.asfreq('min', fill_value=0)

その日の 0 時 1 分のデータと、観測済みのデータと、翌日の 0 時 0 分のデータを結合します。

01
2021-08-19 00:01:0000
2021-08-19 07:58:0010
2021-08-19 07:59:0010
2021-08-19 08:00:0010
2021-08-19 08:01:0010
2021-08-19 08:02:0010
...
2021-08-19 10:18:0010
2021-08-19 10:19:0010
2021-08-19 10:20:0010
2021-08-19 10:21:0010
2021-08-19 10:22:0010
2021-08-20 00:00:0000

data.asfreq('min', fill_value=0)で再び、欠落しているインデックスを補完し、値を 0 で埋めます。結果として次のようなデータが完成します。

01
2021-08-19 00:01:0000
2021-08-19 00:02:0000
2021-08-19 00:03:0000
2021-08-19 00:04:0000
2021-08-19 00:05:0000
...
2021-08-19 07:58:0010
2021-08-19 07:59:0010
2021-08-19 08:00:0010
2021-08-19 08:01:0010
2021-08-19 08:02:0010
...
2021-08-19 23:56:0000
2021-08-19 23:57:0000
2021-08-19 23:58:0000
2021-08-19 23:59:0000
2021-08-20 00:00:0000

このデータをプロットします。

プロットのコードについては説明は省略させていただきますが、1箇所だけ補足しますと、

pyton

1ax.bar(data.index, data[0], width=0.0007)

日付型のデータをX軸にすると、width=1.0は 24 時間の幅になります。
ここではひとつのデータは1分の幅になって欲しいので、

1 ÷ (24 × 60) = 0.00069444

で 0.0007 がほぼ1分の幅となります。

もうひとつ補足すると、plt.gca()で取得されるものと、ax = fig.add_subplot()axは、同じものです。


プログラム中にまったく同じコード(プロットのためのコード)が2つ出てくるので、関数にくくり出して一つだけにするとよいと思います。
また、描画のたびにFigureとAxesを生成するのではなく、最初に一つだけ作って、それを最後まで使うとよいと思います。以下のような感じです。

python

1def plot_operation(fig, ax): 2 data = pd.read_csv(StringIO(operation_data), sep='\s+', header=None, names=[0,1], index_col=0, parse_dates=True) 3 4 (中略) 5 6 #fig = plt.figure(figsize=(12.8, 4.8)) # <- 削除 7 #ax = fig.add_subplot() # <- 削除 8 ax.cla() # <- 追加 9 10 (中略) 11 12 plt.tight_layout() 13 plt.draw() 14 plt.pause(1) 15 16fig = plt.figure(figsize=(12.8, 4.8)) 17ax = fig.add_subplot() 18while True: 19 plot_operation(fig, ax) 20 sleep(1)

「観測結果をファイルに保存しているプログラム」と、「観測結果をグラフに表示しているプログラム」は別々とのことですが、おそらく「点灯パターンによってはグラフの読み込みをさせないようにしたかった」ために、前者のプログラムを後者に流用されたのだと思います。
しかしご自分でも書かれているように、「条件としては点灯がある、点灯がない(休日など)の2パターンで良」いのであれば、たとえば以下のようにプログラムを簡略化することができます。そうすればプロットのためのコードは、関数にするまでもなく、ひとつになります。

python

1while True: 2 if inputVal0 > 2000 or inputVal1 > 2000: 3 # プロットのためのコード 4 sleep(60)

あるいは FileNotFoundError を利用して次のようにすることもできます。この場合は条件としては、厳密には「ファイルがある、ファイルがない」になります。

python

1while True: 2 try: 3 # プロットのためのコード 4 except FileNotFoundError: 5 continue 6 finally: 7 sleep(60)

(追記)すみません、以下は取り消します。グラフが描画されるのは約1分に1回でしたね。簡略化したプログラムでテストしていたら勘違いをしてしまいました。

質問文にプログラムの全体が掲載されていないので正確には分からないのですが、プログラムのループ構造の大枠は以下のようになっているのではないでしょうか?

python

1while true: 2 if inputVal0>2000: 3 # プロットのプログラム 4 else: 5 if inputVal0>2000: 6 # プロットのプログラム 7 else: 8 pass 9 sleep(1)

この構造だと、プロットのプログラム中でも plt.pause(1) を行なっていますので、稼働中は sleep(1) と合わせて2秒おきにループが回ることになりますがそれで大丈夫ですか?
もし1秒おきにループが回った方がいいなら、

python

1while true: 2 if inputVal0>2000: 3 # プロットのプログラム 4 else: 5 if inputVal0>2000: 6 # プロットのプログラム 7 else: 8 sleep(1)

とするとよいと思います。
描画のためにかかる時間が加算されるので、これでも1秒おきにならないと思うので、その時はplt.pause(1)の数字を、1 -> 0.9 -> 0.8 -> 0.7 -> 0.6... とどんどん小さくして試してみるといいです。私の環境では、0.1 でもちゃんと描画されました。


なるべく元のプログラムを変更しない形に書き直したコードを追記します。
稼働率の計算・表示の部分のコードは削除してあります。データの補完の開始時刻は7:00にしてあります。

python

1import datetime 2 3import pandas as pd 4import matplotlib.pyplot as plt 5import matplotlib.dates as mdates 6 7data=pd.read_csv('mydata'+nowday+'.csv', header=None, names=[0,1], index_col=0, parse_dates=True) 8x=data.index 9y=data.iloc[:,0] 10 11d_today = datetime.date.today() 12dt_start = datetime.datetime(d_today.year, d_today.month, d_today.day, 7, 0) 13df_start = pd.DataFrame([[0, 0]], index=[dt_start]) 14d_tomorrow = d_today + datetime.timedelta(days=1) 15dt_end = datetime.datetime(d_tomorrow.year, d_tomorrow.month, d_tomorrow.day) 16df_end = pd.DataFrame([[0, 0]], index=[dt_end]) 17data = df_start.append([data, df_end]) 18data = data.asfreq('min', fill_value=0) 19 20plt.figure(figsize=(12.8, 4.8)) 21plt.xlabel("時刻") 22plt.ylabel("1=稼働,0=停止") 23plt.bar(x,y,width=0.0007) 24plt.gca().xaxis.set_major_locator(mdates.HourLocator(interval=1)) 25plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) 26plt.gca().set_xlim( 27 xmin=datetime.datetime(d_today.year, d_today.month, d_today.day, 7, 0), 28 xmax=dt_end 29) 30plt.gca().set_yticks([0, 1]) 31plt.tight_layout() 32plt.show()

イメージ説明

投稿2021/08/13 22:29

編集2021/08/23 04:36
etherbeg

総合スコア1195

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

F91_

2021/08/16 03:22

ご回答ありがとうございます。plt.xlim(下限値, 上限値)も試してみましたが、7時にデータがない場合に、時刻の表示がされませんでした。
etherbeg

2021/08/16 03:46

されていることは、24時間常にセンサで観測し、24時間常にファイルに記録し、24時間常にグラフに表示している、ということで間違いないですか。 7時以前24時以降はグラフの軸が表示されているだけで空白のグラフ画像を表示し続けるということですか。7時以前24時以降はグラフの表示自体をしないという選択肢はないですか。 あと、data=pd.read_csv('mydata'+nowday+'.csv') で読み込んでいるCSVファイルのサンプルデータを1時間分ほど質問文に追記していただけますか。エクセルファイルに読み込んだ状態の画像ではなく、実際にCSVファイルにテキストで記載されている状態のデータがどのようなものかを知りたいです。
F91_

2021/08/16 04:37 編集

すみません。ご指摘の通り情報が少ないのですぐに可能な限り捕捉致します。 センサ観測ですが、ランプの点灯パターンにより、①機械稼働、②停止機械、③無人稼働、④電源OFFの4パターンとなっています。④は光を何も観測していない状態としています。また、④の場合はエクセル書き込みをしないようにしています。休日などもデータ書き込みとなってしまうので、データが大量になってしまうと考えた為です。解決策がない場合は仕方なく、電源オフも観測すればxlimで対応出来るかと思いました。 7時以前24時以降はグラフの表示自体をしないという選択で大丈夫です。そのような方法があれば大歓迎です。ただし、繰り返し処理で次の日も観測した場合も自動でグラフ表示をし続けたい、また、グラフ表示サイズも全く同じにしたいです。私の少ない知識で表示を続ける事とグラフ表示サイズを一定に保つというコードが書けない為、常にグラフ表示という選択をしています。
etherbeg

2021/08/16 09:31

質問ばかりで申し訳ないのですが、よろしければ教えてください。 観測結果をファイルに保存しているプログラムと、観測結果をグラフに表示しているプログラムは、同じプログラムですか? つまり上の質問文の「光を記録しているコード」と、「グラフを表示させている該当のソースコード」は、同じプログラムファイル内にあって、実行されているプログラムはひとつだけですか? それとも別々のプログラムファイルで、別々に実行されている? 基本的にプログラムは夜間、休日も含めて常時起動しっぱなしですか? センサーは1秒おきにランプを観測して、観測結果は1秒ごとに累積していって、いずれかの点灯パターンが連続して60秒続いた時点でファイルに稼働状況として記録されて、ファイルに記録されたタイミングで累積の観測結果は0に戻されて、また1秒ごとに観測して累積していく、という理解であってますか? その場合、点灯パターンが例えば50秒続いたところで変わってしまったら、その50秒の稼働状況はデータとしては記録されずに欠落してしまうと思うのですが、稼働状況が移行するタイミングではありうることなので、その程度の誤差は仕方ないよね、という感じでしょうか? 質問文の「グラフを表示させている該当のソースコード」は、上のセンサーの観測結果の1秒ごとの記録と、1分ごとのグラフの書き換えの、両方をしているコード、という理解であっていますか?
F91_

2021/08/16 12:19 編集

プログラムを実現したいのでいくらでも説明します。 ①観測結果をファイルに保存しているプログラムと、観測結果をグラフに表示しているプログラムは別々に実行しています。単純に2つのプログラムをエラーなしで1つにする技術がなかったからです。 ②プログラムは夜間、休日も常時起動です。機械稼働状況の自動計測が目的なので常時起動しています。 ③観測結果の書き込みについてはご指摘の理解で合っています。50秒で点灯パターンが変わってしまった場合は50秒のままなので、再度50秒の点灯パターンになった場合に50秒からスタートします。ただし、日付変更タイミングでこのような事態が起きた場合は対応出来ませんので、ある程度の誤差は仕方ないよねになってしまっています。 ④質問文の「グラフを表示させている該当のソースコード」は記録は行っていません。グラフの表示のみです。点灯パターンによってはグラフの読み込みをさせないようにしたかったので、点灯パターンによってグラフを読み取るようにしています。休日はエクセル作成が行われないのでいつも読み取りにしてしまうとエラーが発生し、プログラムが停止となってしまうからです。実はこの方法では2つのプログラム実行では誤差が発生し、プログラム停止が発生してしまっています。また、条件としては点灯がある、点灯がない(休日など)の2パターンで良かったと反省しております。
etherbeg

2021/08/17 04:13

詳しくありがとうございます。 手元で試しているのですが、上手くいかないのでお時間ください。
etherbeg

2021/08/19 04:50

回答に追記しました。
F91_

2021/08/19 09:49

ここまでやっていただきありがとうございます!土日にじっくりやってみます!
etherbeg

2021/08/20 22:36 編集

プロットのコードを1つにする方法について、回答の末尾に追記しました。
F91_

2021/08/21 11:47

ありがとうございます!!!早速試しましたが、以下のエラーが発生しました。 ValueError: Error parsing datetime string "mydata20210821.csv" at position 0 During handling of the above exception, another exception occurred: おそらくまだ時刻部分のデータ変換が上手くいっていないかと思いました。etherbeg様に自分が作成しているデータの形を上手く伝えきれていないことが原因だと思います。とても自己解決出来そうなレベルではありませんのでここまでお手数をおかけし続け申し訳ございませんが、ご教示いただけないでしょうか。 ※情報が足りなければおっしゃって下さい プログラムの内容としてはxlimを使用する為0:00~24:00の間で空白の部分も全て1分毎にデータを埋め込む必要があったと解釈しています。ただ0:00~6:59の間も必要となった理由はわかりませんでした。 計測+エクセル書き込みプログラムとデータplotプログラムを1つにする方法は再度質問に上げようかと検討していましたが、アドバイスをいただけましたので上手くいくかわかりませんがやってみます。工場が停電する時もあり、ラズパイの電源がどうしても切れてしまい、再起動時のプログラムも再実行が必要で2つ実行するのは手間でしかありませんした。 関数にくくり出して一つだけにするというのはご指摘通りです。プログラムを全て公開していなかったのは関数にくくり出しておらず、何度も同じプログラムを書いてしまっていたからです。しかし、ここまでヒントをいただけるのであれば関数を使用して作り直しも可能かと思っております。 お粗末な書き方ですが、質問分には使用していたプログラムを隠す事なく更新しておきます。修正した方が良い場所があればご指摘下さい。
etherbeg

2021/08/21 23:29

すみません、昨晩の間にいろいろとご返答できればよかったのですが、昨日は帰宅後、朝まで前後不覚で寝てしまい、今日も一日仕事で(汗 > 自分が作成しているデータの形を上手く伝えきれていない そうですね、貼っていただいたデータと実際のデータの形が違うのではないかというのは私も思っていました。 でもこれは私の言い方が悪かったですね。「保存されているcsvファイルをメモ帳で開いて、先頭から1時間分程度でいいのでコピーして、それをそのまま、質問文中にコードブロックで囲んで貼ってほしい」といえば良かったです。 それによく見れば質問文中の「ラズパイ光センサーのデータ記録方法」のコードにもちゃんとヒントがありました。 wrtr=csv.writer(f,delimiter=',') この1行に気づいていれば、実際のデータの形は推測できていたはずなのです。 とりあえず data = pd.read_csv(StringIO(operation_data), sep='\s+', header=None, names=[0,1], index_col=0, parse_dates=True) はキーワード引数から sep='\s+' を削除して data = pd.read_csv(StringIO(operation_data), header=None, names=[0,1], index_col=0, parse_dates=True) として試してみてください。
etherbeg

2021/08/22 08:46

あ、StringIOは私が勝手に使っていただけなので、 data = pd.read_csv('mydata'+nowday+'.csv'), header=None, names=[0,1], index_col=0, parse_dates=True) でしたね。失礼しました。
etherbeg

2021/08/22 12:39 編集

> 0:00~6:59の間も必要となった理由 実際の現場の事情が分からないので、念のため遊びを持たせておいた、ということです。必要だから、というより、そうしとけば安全だろう、という感じです。 機械の稼働開始が7時30分とかになることもある、というのは書かれていたので分かったのですが、それが7時前、たとえば6時50とかになる可能性については、あるともないとも分からなかったので、もしそういうことがあったとしても、プログラムが一貫性を持って動作するようにしておいた、ということです。 またもし仮に、グラフの表示を7時からでなく6時からにしたい、ということがあったとしても、データ処理の部分は書き換えなくてすみますしね。 遊びを持たせただけなので、0:00じゃなくても、6:00でも3:00でも良かったのです。ただ、プログラムがやることなので、0:00でも6:00でも一瞬で終わることで同じだから、という理由と、24時を回っても稼働していた時のことを考えたら、0:00からにしといたほうが良いのでは、と何となく思って0:00にしたのです。 24時前に機械が停止することがあるということは書かれていたので分かっていたのですが、24時以降も機械が動いている可能性については書かれていなかったので、もし24時以降動いていてもプログラムがエラーになったりしないよう、一貫性を持って動作するようにということは何となく考えていました。 ただちゃんと考えてなかったので、コメントをいただいて気がついたのですが、24時以降も動いていたら、今のプログラムだとデータの整合性がとれなくなり出ますね。0:00以降、機械が停止するまでのデータがダブって作成されてしまう。 もしそうなった場合に、具体的にどのような動作になるか、つまりプログラム停止など大きな不具合として現れるか、それとも無視できるような小さな不具合で終わるか、それとも目に見えるような影響は一切出ずに終わるか、は検証してみないと分かりません。 (ただデータがダブったとしても、それはグラフ表示のための一時的なデータで、記録されるわけではないので、データそのものに関しては、そんなに深刻にならなくてもいいかのなとは思います。) > 計測+エクセル書き込みプログラムとデータplotプログラムを1つにする 私はプログラムがひとつなのか分かれているのかを(プログラムの正確な理解のために)知りたかっただけで、ひとつにするようアドバイスはしたつもりはありませんでした。 個人的には、ひとつにせず分けておいた方がいいのではないかなあ…、と思います。グラフ表示のプログラムでもし何か不具合があってエラーが出たら、データの記録プログラムも巻き添えを食って停止してしまうからです。 グラフ表示のプログラムは記録されているデータを表示しているだけなので、停止しても再起動すればいいだけですが、データの記録はリアルタイムなので、いったん停止してしまったら、その間のデータは後で取り直すわけにはいかないので…。 そんなに神経質にならなくていい、もし仮にそういうことがあって記録が一部欠けることがあっても構わない、ということであれば、ひとつにしてもいいかなと思います。 (あるいは、グラフ表示でエラーが出ても止まらないように作る、というのも一つの考え方かもしれませんね。)
etherbeg

2021/08/22 12:51

あれ、よく見たら前とプログラム変わってますね。 (data = pd.merge(pd.DataFrame({0:[f'{h:02}:{m:02}' for h in range(7,24) for m in range(0, 60)]+['24:00']}), とその次の行) エラーが出たのはこの行ですか? だとしたら私のコードは関係ないですが…
F91_

2021/08/22 14:42 編集

すみません!!!(data = pd.merge(pd.DataFrame({0:[f'{h:02}:{m:02}' for h in range(7,24) for m in range(0, 60)]+['24:00']}),は先に回答をしていただいた方のプログラムを試していて上手くいってなかったものをそのままコピーしていました。只今修正しました。エラーが発生したのはその行ではありません。 StringIOを外した所、エラーはなくなりましたが、グラフ表示がなく、繰り返し処理がされていない状況です。グラフ表示を関数にしてみましたが、おそらくコピーしただけなので行ずれて何かが機能していないのかもしれません。ここまでやっていただいたので自分で理解して可能な限り粘ってます。 現場の状況ですが、工場の受注が減少傾向にあり、過去は夜勤をやっていたのですが、現在は夜勤を一切やっていません。経費削減の為です。人がいる中での稼働は遅くとも23時30までです。無人稼働もありますが、管理者が稼働状況の大画面表示(稼働状況の可視化)を目指しているので、人がいない時間帯はグラフ表示が必要ないので7時~24時としています。 記録が欠けることは可能な限り防ぎたいです。 工場で機械稼働率を計測していて、稼働状況の記録プログラムは停止したことがないです。グラフプログラムは停止したことが何度もありますが・・・
etherbeg

2021/08/23 01:53 編集

質問文中に貼られているコードは、いま現在実際に現場でエラーなく動いているコードで、グラフ表示がうまくいっていないのは、テスト用の別のコードですよね。 どうしてもうまくいかないようであれば、そのうまくいかない方のコードを質問文中に追加していただければ、私の手元でも試してみます。 ----- 私のプログラムだと24時以降も機械が動いていたらデータの整合性がとれなくなる問題ですが、人がいる中での稼働は遅くとも23時30まででも、無人稼働もあるということは、00:00をすぎてもしばらくの間、機械が無人稼働していて、その稼働状態がファイルに記録される可能性もあるということでしょうか? テストしてみた結果、もしそうであれば、00:01の時点で data = df_start.append([data, df_end]) data = data.asfreq('min', fill_value=0) の箇所で ValueError が発生して、プログラムが停止することがわかりました。詳細は省きますが、これはデータ補完の開始を6:00や7:00にした場合でも、発生します。 もうひとつ問題があって、稼働時間や機械稼働率の計算にも影響があることがわかりました。これは00:00を回ってから機械が稼働していた場合、それをその日の機械の稼働開始と誤って認識してしまうことによるものです。 前者の解決方法はすぐ見つかったのですが、後者の方がちょっと厄介です。 とりあえず、上の私の認識で間違っていないかどうかを、確認したいです(24時以降に機械が無人稼働していたら、その稼働状態がファイルに記録されて、プロットプログラムがリアルタイムにそれを読み込む、という認識で間違いないかどうか)。 その認識で間違っていなければ、現在現場で動いているプログラムでも、稼働時間や機械稼働率の計算の問題は発生しているのではないかと思います(ただ現在稼働中のプログラムでは誤差は小さいと思います)。 ----- 現場での運用の様子ですが、 ・シグナルタワーにセンサーが取り付けられている ・センサーがコードでラズベリーパイに繋がっている ・ラズベリーパイにモニターとキーボードが繋がっている ・ラズベリーパイのモニターにコンソール(ターミナル)の窓が2つあって、一方では稼働状況を記録するプログラムが、もう一方ではグラフを表示するプログラムが常時実行されている ・コンソールの窓2つの他に、グラフが表示されている窓が1つある ・人間はこのモニターを見て稼働状況を確認している こんな感じですか? ラズベリーパイって使ったことがないので、実際の様子の想像がつかなくて…。
F91_

2021/08/23 03:48

①質問分に貼っているコードは実際に工場で使用しています。上手くいかないのはテスト用です。 ②無人稼働もファイルに追記しています。質問分の時数オーバーで割愛していました。無人稼働は必ず人がいない時間帯になるのでグラフ表示なしで考えています。 ③多少は誤差は発生していますが、それは光センサーの誤差だと思っていました。 稼働率の計算は計測プログラムだけで大丈夫です。グラフは稼働している時間帯、停止している時間帯が見えれば大丈夫です。グラフの方は稼働率は見えなくてOKです。すみませんでした。 ④現場の運用 ・シグナルタワーにセンサーが取り付けられている ⇒〇 ・センサーがコードでラズベリーパイに繋がっている ⇒〇 ・ラズベリーパイにモニターとキーボードが繋がっている ⇒× VNCで遠隔操作をしています ・ラズベリーパイのモニターにコンソール(ターミナル)の窓が2つあって、一方では稼働状況を記録するプログラムが、もう一方ではグラフを表示するプログラムが常時実行されている ⇒〇  ・コンソールの窓2つの他に、グラフが表示されている窓が1つある ⇒× ①稼働率をTkinterで表示⇒これがあるのでグラフの稼働表示は不要です、②グラフを表示 窓は2つです。 ・人間はこのモニターを見て稼働状況を確認している ×⇒モニターはまだ未設置で今後検討します。現在はVNCで遠隔操作で見ています。
etherbeg

2021/08/23 04:23

詳しくありがとうございます。 回答の末尾に、なるべく元のプログラムを変更しない形に書き直したコードを追記しました。 グラフでは稼働率の計算・表示は不要とのことなので、その部分のコードは削除してあります。 データ補完の開始時刻も7:00にしてあります。 このコードなら、24時を回って機械が無人稼働していても、エラーで停止することはないと思います。 (データの補完の開始時刻を朝にしたのと、データの補完をするのをプロットの直前の1回だけにしたことにより) 最初からこのコードで回答すれば良かったですね。すみませんでした。
F91_

2021/08/23 12:45

ありがとうございます!!!質問分のグラフ横軸の時間目盛りの最小、最大を設定したいは解決出来たと思います。しかし、グラフの更新が上手くいきません。実はplt.clfやax.clfなどのしくみが理解できていないまま工場で使用しているプログラムはたまたま更新が上手くいっていました。今回のように少しコードが変わると対応が出来ませんでした。今回作成していただいたプログラムは関数も使用しているので効率の良い書き方だと思いますので、今回分をベースに作成したいです。今後新たに機能を追加することもあると思いますので。 まずはこの質問はクローズさせ、再度グラフ更新が上手くいない内容で質問を立ち上げようと思います。引き続き回答をいただけると非常に助かります。
guest

0

python

1 data=pd.read_csv('mydata'+nowday+'.csv')

python

1 data = pd.merge(pd.DataFrame({0:[f'{h:02}:{m:02}' for h in range(7,24) for m in range(0, 60)]+['24:00']}), pd.read_csv('mydata'+nowday+'.csv'), how='left')

に変更してください。

投稿2021/08/13 23:07

ppaul

総合スコア24668

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

F91_

2021/08/14 11:37

ご回答ありがとうございます。試してみた所、pandas.errors.MergeError: No common columns to perform merge on.というエラーが発生しました。結合しようとしている二つのデータの型が違うからだと認識しています。ラズパイ光センサーにて記録しているエクセルデータの時刻はstrftime('%H:%M')にて型変換して書き込みしております。解決策などありますでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問