csvファイルを読み込んでプロットする

csvモジュールを使う

csvを扱うモジュールをインポートし、その中にあるreaderというメソッドを使う。返値は読み込んだデータ全体をオブジェクトとして持っているので一行ずつ取り出すには次のようにfor文を書く。

これを実行し、x_data, y_dataに何が入っているかを確かめること。

下の例では、

1,B0-FC-46-01-14-A6-43-A1-AB-CD-CB-9C-FD-DB-40-13,-63,-63,1.01076,far,2016/07/07 09:41:45.782 1,B0-FC-46-01-14-A6-43-A1-AB-CD-CB-9C-FD-DB-40-13,-60,-63,0.613913253540759,far, 2016/07/07 09:41:46.597 1,B0-FC-46-01-14-A6-43-A1-AB-CD-CB-9C-FD-DB-40-13,-62,-63,0.852140880208678,far, 2016/07/07 09:41:47.561

のように3列目にシグナル強度(rssi)、7列目に年月日が入っている場合を扱っている。

In [1]:
# coding:utf-8
'''
BLEデータの読み込み、プロット
モジュールcsvをつかってみる
'''
import csv

datafile = open('testData.txt', 'r') # ファイル名は適当に変更
csvObject = csv.reader(datafile)

x_data = []
y_data = []
for row in csvObject:
    x_data.append(row[6])
    y_data.append(row[2])
In [2]:
x_data
Out[2]:
['2016/07/07 09:41:45.782',
 '2016/07/07 09:41:46.597',
 '2016/07/07 09:41:47.561',
 '2016/07/07 09:41:47.650',
 '2016/07/07 09:41:48.732',
 '2016/07/07 09:41:49.682',
 '2016/07/07 09:41:50.613',
 '2016/07/07 09:41:51.451',
 '2016/07/07 09:41:51.555',
 '2016/07/07 09:41:52.412']
In [3]:
y_data
Out[3]:
['-63', '-60', '-62', '-60', '-59', '-64', '-57', '-63', '-65', '-71']

x_dataは文字列なのでこのままでは描画できない。日時データであることを認識させなければならない。そのためにはdatetimeモジュールにある文字列型⇒datetime型の変換メソッドを用いる。 strptimeの第1引数は文字列、第2引数は文字列の形式

In [8]:
from datetime import datetime
datetime.strptime(x_data[1],'%Y/%m/%d %H:%M:%S.%f')
Out[8]:
datetime.datetime(2016, 7, 7, 9, 41, 46, 597000)

日時をdatetimeに変換し、matplotlibを利用して描画

横軸のラベルはmatplotlib.datesモジュールを使う必要があることが面倒ではある (下のpandasを使った描画に日付目盛の詳細な設定例があるので参照してほしい。)

図をnotebook内に書き込むには、スクリプトの先頭に %matplotlib inline を書く。

In [20]:
# coding:utf-8
'''
BLEデータの読み込み、プロット
モジュールcsvをつかってみる
'''
import csv
from datetime import datetime
import matplotlib.dates as mdates
import matplotlib.pyplot as plt

datafile = open('ble20170120_1427.csv', 'r')
csvObject = csv.reader(datafile)

x_data = []
y_data = []
for row in csvObject:
    x_data.append(datetime.strptime(row[0],'%Y-%m-%d %H:%M:%S.%f')) # 文字列をdatetimeオブジェクトに変換
    y_data.append(row[2])

plt.plot_date(x_data, y_data, "r-")
# x軸をdatetime用の表示にする
# gcaは get current axesの意味
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
plt.gca().xaxis.set_major_locator(mdates.DayLocator())

plt.show()

plot関数ではなくsubplot関数を利用してみる

いくつものグラフを並べて書くときには、subplot関数を利用するのが便利である。

軸を操作するときなど、それぞれのsubplotの返値をオブジェクト変数に記憶しておいて、そのオブジェクトのメソッドをつかうところに注意

In [18]:
# coding:utf-8
'''
BLEデータの読み込み、プロット
モジュールcsvをつかってみる
'''
import csv
from datetime import datetime
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

datafile = open('ble20170120_1427.csv', 'r')
csvObject = csv.reader(datafile)

x_data_raw = []
x_data = []
y_data = []
for row in csvObject:
    x_data.append(datetime.strptime(row[0],'%Y-%m-%d %H:%M:%S.%f'))
    y_data.append(row[2])

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x_data, y_data)

# x軸の時刻表示
minutes      = mdates.MinuteLocator()
daysFmt = mdates.DateFormatter('%H:%M:%S')
ax.xaxis.set_major_locator(minutes)
ax.xaxis.set_major_formatter(daysFmt)
fig.autofmt_xdate()

plt.show()

pandasの利用

表形式のデータを扱うにはpandasモジュールが便利である。 特に、そのあと統計処理を行う場合には特によく用いられる。

pandaのメソッドplot関数を用いると時系列データを自動認識してくれ、簡単に描ける。もちろん、データをdatetime型に変換しておかなければならない。 pandasのプロット関数の引数はいろいろな形式がある。下の例は、dataFrameの列を整数で指定したもの。

このように、簡単に書けるが、dataFrameの構造は慣れるのに時間がかかる。英語のマニュアルを嫌がらずに読む必要がある。

In [14]:
# coding:utf-8
'''
BLEデータの読み込み、プロット
  header行は無し
  1列目が日時、3列目がrssi
'''
import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

df = pd.read_csv('ble20170120_1427.csv', header=None, encoding='utf-8') # 読み込み
df[0] = pd.to_datetime(df[0])                                           # 1列目を文字列から日時オブジェクトに変換
ax = plt.figure(figsize=(7,4), dpi=100).add_subplot(111)                # 描画ウィンドウの確保
df.plot(x=0, y=2, ax=ax, label="rssi")                                  # 描画ウィンドウに描画
seconds = mdates.SecondLocator(interval=1)                              # 副目盛の定義
ax.xaxis.set_minor_locator(seconds)                                     # 副目盛をセット 
#ax.xaxis.set_minor_formatter(mdates.DateFormatter('%S'))               # 副目盛に文字を入れる
ax.xaxis.set_major_formatter(mdates.DateFormatter('%M:%S'))             # 主目盛のセット
ax.set_xlabel("time")
ax.set_ylabel("rssi")
plt.show()

プロット部分の関数化

次のようにプロットする部分を関数にしておけば、ファイル名を関数の引数にして実行するだけで図示できる。ノートに図を次々書いてメモと一緒に保存しておける。

In [25]:
# coding:utf-8
'''
BLEデータの読み込み、プロット
  header行は無し
  1列目が日時、3列目がrssi
'''
import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

def plotBLEdata(filename): 
    df = pd.read_csv(filename, header=None, encoding='utf-8') # 読み込み
    df[0] = pd.to_datetime(df[0])                                           # 1列目を文字列から日時オブジェクトに変換
    ax = plt.figure(figsize=(7,4), dpi=100).add_subplot(111)                # 描画ウィンドウの確保
    df.plot(x=0, y=2, ax=ax, label="rssi")                                  # 描画ウィンドウに描画
    seconds = mdates.SecondLocator(interval=1)                              # 副目盛の定義
    ax.xaxis.set_minor_locator(seconds)                                     # 副目盛をセット 
    #ax.xaxis.set_minor_formatter(mdates.DateFormatter('%S'))               # 副目盛に文字を入れる
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%M:%S'))             # 主目盛のセット
    ax.set_xlabel("time (min:sec)")
    ax.set_ylabel("rssi")
    plt.show()
In [26]:
%matplotlib inline
plotBLEdata('ble20170120_1427.csv')

複数のBLEアドレスが混在する場合のプロット

In [6]:
%matplotlib auto
# inlineで描画したいときは auto のかわりに inlineを指定
# coding:utf-8
'''
BLEデータの読み込み、プロット
モジュールcsvをつかってみる
'''
import csv
from datetime import datetime
import matplotlib.dates as mdates
import matplotlib.pyplot as plt

datafile = open('./20170202/raspi3_ble20170202.csv', 'r') # 読み込むファイル名
csvObject = csv.reader(datafile)

data = {}
# BLE addressごとのリストにする
for row in csvObject:
    if not row[1] in data:
        data[row[1]] = {'x': [], 'y': []}
    data[row[1]]['x'].append(datetime.strptime(row[0],'%H:%M:%S')) # 文字列をdatetimeオブジェクトに変換
    data[row[1]]['y'].append(row[2])

for addr,  values in data.iteritems():
    plt.plot_date(values['x'], values['y'], "-", label=addr)

# 凡例表示
plt.gca().legend()
# x軸をdatetime用の表示にする
# gcaは get current axesの意味
seconds = mdates.SecondLocator(interval=1)                              # 副目盛の定義
plt.gca().xaxis.set_minor_locator(seconds)                                     # 副目盛をセット 
#ax.xaxis.set_minor_formatter(mdates.DateFormatter('%S'))               # 副目盛に文字を入れる
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%M:%S'))             # 主目盛のセット
plt.gca().set_xlabel("time (min:sec)")
plt.gca().set_ylabel("rssi")
plt.show()
Using matplotlib backend: Qt4Agg

移動平均を表示するようにしたもの

パラメータrolling_widthに移動平均する数を指定

pandasのrolling_meanは時代遅れと警告がでるがとりあえずこれを使った。将来、rollingに直す。

In [2]:
# coding:utf-8
'''
BLEデータの読み込み、プロット
'''
import csv
from datetime import datetime, timedelta
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import time

'''
dtypeがdatetimeのnp.arrayを受け取って、移動平均を返す
'''
def  rolling_datetime(data, window) :
    x = np.array([])
    for i in range(data.size) :
        x = np.append(x, time.mktime(data[i].timetuple()))
    rollingData =pd.rolling_mean(x, window)
    x = np.array([])
    for i in range(rollingData.size) :
        if rollingData[i] == rollingData[i] :
            x = np.append(x, datetime.fromtimestamp(rollingData[i]))
        else:
            x = np.append(x, np.nan)
    return x

'''
パラメータ
'''
# 読み込むファイル名
datafile = open('./20170202/raspi2_ble20170202.csv', 'r')
# 移動平均間隔
rolling_width = 4
# 表示するアドレス一覧
dispAddr = ["fe:ee:81:c1:35:9f", "c0:1c:4d:47:ff:61","ec:e0:9c:03:00:5e"]
# c0:1c:4d:47:ff:61, fe:ee:81:c1:35:9f,  4f:74:da:59:11:9b, 75:a3:00:83:89:74,  ec:e0:9c:03:00:5e

'''
以下計算
'''

data = {}
#時刻の読み込み
csvObject = csv.reader(datafile)
for row in csvObject:
    addr = row[1].strip()
    if not addr in data.keys():
        data[addr] = {'x': np.array([]), 'y': np.array([])}
        # print addr  # 1回だけアドレスを出力
    data[addr]['x'] = np.append(data[addr]['x'], datetime.strptime("20170202 "+row[0],'%Y%m%d %H:%M:%S')) # 文字列をdatetimeオブジェクトに変換
    data[addr]['y'] = np.append(data[addr]['y'],row[2])


# 穴がないようにデータを作成する
plotData = {}
for addr, values in data.iteritems():
    if not addr in dispAddr :
        continue
    plotData[addr] = {'x': np.array([values["x"][0]]), 'y': np.array([values["y"][0]])}
    for i in range(1,values["x"].size) :
        rec_time =values["x"][i-1] + timedelta(seconds=1)
        while values["x"][i] > rec_time :
            plotData[addr]['x'] = np.append(plotData[addr]['x'],rec_time)
            plotData[addr]['y'] = np.append(plotData[addr]['y'], "-110")
            rec_time += timedelta(seconds=1)
        plotData[addr]['x'] = np.append(plotData[addr]['x'], values["x"][i])
        plotData[addr]['y'] = np.append(plotData[addr]['y'], values["y"][i])

# 描画
ax = plt.figure(figsize=(12,4), dpi=100).add_subplot(111)    # 描画ウィンドウの確保
line_style = ["-", "-", "-", "." ,":", "<", ">"] # 線の種類
lineNum =0
for addr,  values in plotData.iteritems():
    if addr in dispAddr :
        #ax.plot_date(values['x'], values['y'], line_style[lineNum], label=addr) # 生データ
        # 移動平均 (空白があるとだめ)
        y = pd.rolling_mean(values["y"],rolling_width)
        x = rolling_datetime(values["x"],rolling_width)
        ax.plot_date(x[rolling_width:], y[rolling_width:], line_style[lineNum], label=addr)   # 移動平均データ
        lineNum+=1

# 凡例表示
ax.legend()
# x軸をdatetime用の表示にする
# gcaは get current axesの意味
seconds = mdates.SecondLocator(interval=1)                              # 副目盛の定義
ax.xaxis.set_minor_locator(seconds)                                     # 副目盛をセット 
#ax.xaxis.set_minor_formatter(mdates.DateFormatter('%S'))               # 副目盛に文字を入れる
ax.xaxis.set_major_formatter(mdates.DateFormatter('%M:%S'))             # 主目盛のセット
ax.set_xlabel("time (min:sec)")
ax.set_ylabel("rssi")
plt.show()
C:\Users\toyoki\Anaconda2\lib\site-packages\ipykernel\__main__.py:61: FutureWarning: pd.rolling_mean is deprecated for ndarrays and will be removed in a future version

移動平均その2: pandasのrollingの利用

pandas 0.18.0よりndarrayに対するrolling_mean()ではなく、DataFrameに対するrolling()メソッドを使うように変更された。

In [73]:
# coding:utf-8
'''
BLEデータの読み込み、プロット
pandas 0.18.0 以降なので現在中断中 plotBleMultiAddr2.pyを利用すること
'''
import csv
from datetime import datetime, timedelta
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

'''
パラメータ
'''
# 読み込むファイル名
datafile = open('./20170202/raspi2_ble20170202.csv', 'r')
# 移動平均間隔
rolling_width = 5
# 表示するアドレス一覧
dispAddr = ["fe:ee:81:c1:35:9f","4f:74:da:59:11:9b", "c0:1c:4d:47:ff:61","ec:e0:9c:03:00:5e","75:a3:00:83:89:74"]
# c0:1c:4d:47:ff:61, fe:ee:81:c1:35:9f,  4f:74:da:59:11:9b, 75:a3:00:83:89:74,  ec:e0:9c:03:00:5e

'''
以下計算
'''

data = {}
#時刻の読み込み
csvObject = csv.reader(datafile)
for row in csvObject:
    addr = row[1].strip()
    if not addr in data.keys():
        data[addr] = {'x': np.array([]), 'y': np.array([])}
        # print addr  # 1回だけアドレスを出力
    data[addr]['x'] = np.append(data[addr]['x'], datetime.strptime(row[0],'%H:%M:%S')) # 文字列をdatetimeオブジェクトに変換
    data[addr]['y'] = np.append(data[addr]['y'],int(row[2]))


# 穴がないようにデータを作成する
df4plot ={}
for addr, values in data.iteritems():
    if not addr in dispAddr :
        continue
    plotData = {'x': np.array([values["x"][0]]), 'y': np.array([values["y"][0]])}
    for i in range(1,values["x"].size) :
        rec_time =values["x"][i-1] + timedelta(seconds=1)
        while values["x"][i] > rec_time :
            plotData['x'] = np.append(plotData['x'],rec_time)
            plotData['y'] = np.append(plotData['y'], "-110")
            rec_time += timedelta(seconds=1)
        plotData['x'] = np.append(plotData['x'], values["x"][i])
        plotData['y'] = np.append(plotData['y'], values["y"][i])
    df4plot[addr] = pd.DataFrame({'rssi': plotData['y']}, index=plotData['x'])

# 描画
ax = plt.figure(figsize=(12,4), dpi=100).add_subplot(111)    # 描画ウィンドウの確保
line_style = ["-", "-", "-.", "." ,":", "<", ">"] # 線の種類
lineNum =0
for addr,  values in df4plot.iteritems():
    if addr in dispAddr :
        # 移動平均 (空白があるとだめ)
        #ax.plot_date(values['x'], values['y'], line_style[lineNum], label=addr) # 生データ
        values.rolling('10s')
        #print values
        #values.plot(y='rssi', style=[line_style[lineNum]], ax=ax, label=addr)  # 移動平均データ
        ax.plot(values.rssi, label=addr) # rollingが効かない
        lineNum+=1

# 凡例表示
ax.legend()
# x軸をdatetime用の表示にする
# gcaは get current axesの意味
seconds = mdates.SecondLocator(interval=1)                              # 副目盛の定義
ax.xaxis.set_minor_locator(seconds)                                     # 副目盛をセット 
#ax.xaxis.set_minor_formatter(mdates.DateFormatter('%S'))               # 副目盛に文字を入れる
ax.xaxis.set_major_formatter(mdates.DateFormatter('%M:%S'))             # 主目盛のセット
ax.set_xlabel("time (min:sec)")
ax.set_ylabel("rssi")
plt.show()

過去一定区間の最大値を表示する

とりあえずバージョンである。

rssi強度データと時間(カウント数)幅を与えて、その幅内の最大値の配列を返す関数 maxPrevSequenceを追加し、描画プロットの部分で移動平均の代わりに使うだけでよい。 幅はrolling_widthで設定する。

In [ ]:
# coding:utf-8
'''
BLEデータの読み込み、プロット
過去の一定幅(prevWidth)の最高値をプロットする
'''
import csv
from datetime import datetime, timedelta
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import time

'''
dtypeがdatetimeのnp.arrayを受け取って、移動平均を返す
'''
def  rolling_datetime(data, window) :
    x = np.array([])
    for i in range(data.size) :
        x = np.append(x, time.mktime(data[i].timetuple()))
    rollingData =pd.rolling_mean(x, window)
    x = np.array([])
    for i in range(rollingData.size) :
        if rollingData[i] == rollingData[i] :
            x = np.append(x, datetime.fromtimestamp(rollingData[i]))
        else:
            x = np.append(x, np.nan)
    return x

def maxPrevSequence(data, width):
    dat = []
    for i in range(width-1):
        dat = np.append(dat, data[0])
    dat = np.append(dat, data)
    dat = dat.astype(int)
    result = np.zeros(data.size)
    for i in range(data.size):
        result[i] = np.max(dat[i:i+width-1])
    return result

'''
パラメータ
'''
# 読み込むファイル名
datafile = open('./20170202/raspi2_ble20170202p1.csv', 'r')
# 最大値を取る間隔
rolling_width = 4
# 表示するアドレス一覧
dispAddr = ["fe:ee:81:c1:35:9f", "c0:1c:4d:47:ff:61","ec:e0:9c:03:00:5e"]
# c0:1c:4d:47:ff:61, fe:ee:81:c1:35:9f,  4f:74:da:59:11:9b, 75:a3:00:83:89:74,  ec:e0:9c:03:00:5e

'''
以下計算
'''

data = {}
#時刻の読み込み
csvObject = csv.reader(datafile)
for row in csvObject:
    addr = row[1].strip()
    if not addr in data.keys():
        data[addr] = {'x': np.array([]), 'y': np.array([])}
        # print addr  # 1回だけアドレスを出力
    data[addr]['x'] = np.append(data[addr]['x'], datetime.strptime("20170202 "+row[0],'%Y%m%d %H:%M:%S')) # 文字列をdatetimeオブジェクトに変換
    data[addr]['y'] = np.append(data[addr]['y'],row[2])


# 穴がないようにデータを作成する
plotData = {}
for addr, values in data.iteritems():
    if not addr in dispAddr :
        continue
    plotData[addr] = {'x': np.array([values["x"][0]]), 'y': np.array([values["y"][0]])}
    for i in range(1,values["x"].size) :
        rec_time =values["x"][i-1] + timedelta(seconds=1)
        while values["x"][i] > rec_time :
            plotData[addr]['x'] = np.append(plotData[addr]['x'],rec_time)
            plotData[addr]['y'] = np.append(plotData[addr]['y'], "-110")
            rec_time += timedelta(seconds=1)
        plotData[addr]['x'] = np.append(plotData[addr]['x'], values["x"][i])
        plotData[addr]['y'] = np.append(plotData[addr]['y'], values["y"][i])

# 描画
ax = plt.figure(figsize=(12,4), dpi=100).add_subplot(111)    # 描画ウィンドウの確保
line_style = ["-o", "-s", "-^", "." ,":", "<", ">"] # 線の種類
lineNum =0
for addr,  values in plotData.iteritems():
    if addr in dispAddr :
        #ax.plot_date(values['x'], values['y'], line_style[lineNum], label=addr) # 生データ
        # y = pd.rolling_mean(values["y"],rolling_width)
        # x = rolling_datetime(values["x"],rolling_width)
        x = values["x"]
        y = maxPrevSequence(values["y"], rolling_width) # 過去rolloing_width間の最大値をyデータ配列とする
        ax.plot_date(x, y, "-", label=addr)   # 加工データのプロット
        lineNum+=1

# 凡例表示
ax.legend()
# x軸をdatetime用の表示にする
# gcaは get current axesの意味
seconds = mdates.SecondLocator(interval=1)                              # 副目盛の定義
ax.xaxis.set_minor_locator(seconds)                                     # 副目盛をセット 
#ax.xaxis.set_minor_formatter(mdates.DateFormatter('%S'))               # 副目盛に文字を入れる
ax.xaxis.set_major_formatter(mdates.DateFormatter('%M:%S'))             # 主目盛のセット
ax.set_xlabel("time (min:sec)", fontsize=14)
ax.set_ylabel("rssi",fontsize=14)
plt.show()