パルスオキシメータからデータ抽出し、Excelグラフ化

販売元への問い合わせから

 私は、睡眠時無呼吸症候群を患って以来、自分の睡眠について見える化をしていきたいという思いが強い人です。マットレスや枕を替えたり、いびき防止のためのマウスピースを使ってみたり。CPAPも私には合わず、最終的に外科医にレーザー切除手術をしてもらって、やっと少し寝られるようになりました。それでも、睡眠薬は手放せず、熟睡感もあまりない日々です。

 職業病のなのかもしれませんが、手術後も、睡眠の見える化をしたい気持ちが強く、「パルスオキシメータを購入」の記事のように計測器を購入しました。ただ、この計測器には、データ閲覧用のAndroidアプリはあるものの、CSVエクスポートの機能がなく、生データを直接操作してデータを抽出するしか道はないと感じました。

 そこで販売元のアメリカ Bodimetrics社CSに問い合わせました。「睡眠時無呼吸症候群の状況を把握するために、生データから数値を抽出したいので方法を教えて欲しい。」とストレートに聞きました。何回もやり取りさせてもらって、先方のエンジニアから、「データの先頭80バイトはヘッダ情報で、その後に2秒毎に計測したデータを10バイト長で格納している。内訳は、最初の2バイトがSpO2値、次の4バイトが心拍数、次の2バイトがモーション検出値、残りの2バイトはカウンタだ。それぞれの数値は16進数で記録されている。」と教えてもらいました。ここまでしっかりと教えてもらえて感謝でした。

データ抽出

 データベースの並び方が分かったところで、抽出作業に移りたいのですが、データベースを開くところからしてよく分からなかったので、とりあえずビューワを探して開いてみることにしました。使ったのは、下に示す、DB Browser for SQLite、です。

about DB Browser for SQLite
about DB Browser for SQLite

 データベースの構造を開き、その中のdatabuf項目がデータが収められているであろうと推定しました。BLOBってなんだ?と思い調べたら、Binary Large OBjectの略だそうで。

database structure
database structure

 まず、SleepDataテーブルの中にあるmFileName項目で日付を確認し、

select hex(databuf) from sleepdata where mFileName = 20190310

みたいな感じで、下の図に示すようにデータ抽出しました(この構文、知人に教えてもらいました。感謝です。)。本当はpythonから直接できればスマートな気がしますが、それ勉強しだすとデータ解析ができる日を先延ばしにしてしまうので、今回はこのソフトのお世話になりました。右側のエクスポートでテキストデータとして保存し、抽出完了です。ここからはpythonの出番!

execution to extract information
execution to extract information

抜き出したテキストデータです。呪文のような文字列・・・。

でも、ぱっと見、規則性がありそうな感じです。さっそく、教えてもらったデータ構造をpythonでプログラムしました(さらっと書いているように思われるかもしれませんが、結構時間かかってます・・・)。

extracted information
extracted information

pythonスクリプト

 最近、使うのに慣れてきたpython + xlsxwriterの組み合わせで、呪文のような文字列から、見て分かるExcelワークシートに変換しグラフまで作成するようにしました。

# bodimetrics SpO2計のデータからSoP2値とHR値とを抽出してExcel化するスクリプト
#
# 2019年3月10日 初版

import xlsxwriter

# 処理するファイルを指定する
NameOfFile = "20190310_databuf.txt"

# ワークシート名
NameOfWorksheet = "2019.3.10"
workbook = xlsxwriter.Workbook("vibe" + NameOfWorksheet + ".xlsx")
worksheet = workbook.add_worksheet(NameOfWorksheet)

# Excelのセル座標変数を初期化
row = 0
col = 0

# 各列の項目を記述
worksheet.write(row, col, "time(s)")
worksheet.write(row, col + 1, "time(h)")
worksheet.write(row, col + 2, "SpO2")
worksheet.write(row, col + 3, "HR")
row += 1

# ファイルを読み込んでExcel化
with open(NameOfFile, mode = "r", encoding = "utf-8") as f:
for line in f:
length = len(line)
k = 0

for i in range(80, length-80 , 10):
worksheet.write(row, col, k)
worksheet.write(row, col + 1, "=$A$" + str(row + 1) + "/3600")
worksheet.write(row, col + 2, int(line[i:i+2],16))
worksheet.write(row, col + 3, int(line[i+2:i+4],16))
row += 1
k += 2

# SpO2のトレンドグラフを描画
chart_SpO2 = workbook.add_chart({"type":"scatter"})
chart_SpO2.set_x_axis({"name":"time /h",
"name_font":{"size":14, "bold":True},
"min":0,
"max":10,
"major_unit":1})
chart_SpO2.set_y_axis({"name":"SpO2(%)",
"name_font":{"size":14, "bold":True},
"min":80,
"max":100,
"major_unit":10})
chart_SpO2.add_series({"categories":"='"+NameOfWorksheet+"'!$B$2:$B$"+str(row),
"values":"='"+NameOfWorksheet+"'!$C$2:$C$"+str(row),
"name":"SpO2(%)",
"line":{"width":1.25, "color":"#0000ff"},
"marker":{"type":"none"}})
chart_SpO2.set_size({"width":800, "height":400})
chart_SpO2.set_title({"name":"SpO2 trend (" + NameOfWorksheet + ")"})
chart_SpO2.set_plotarea({"border":{"color":"#000000", "width":1.25, "dash_type":"solid"}})
worksheet.insert_chart("G7", chart_SpO2)


# 心拍数のトレンドグラフを描画
chart_HR = workbook.add_chart({"type":"scatter"})
chart_HR.set_x_axis({"name":"time /h",
"name_font":{"size":14, "Bold":True},
"min":0,
"max":10,
"major_unit":1})
chart_HR.set_y_axis({"name":"Heart Rate /bpm",
"name_font":{"size":14, "Bold":True},
"min":40,
"max":80,
"major_unit":10})
chart_HR.add_series({"categories":"='" + NameOfWorksheet + "'!$B$2:$B$" + str(row),
"values":"='"+NameOfWorksheet+"'!$D$2:$D$"+str(row),
"name":"Heart Rate /bpm",
"line":{"width":1.25, "color":"#ff0000"},
"marker":{"type":"none"}})
chart_HR.set_size({"width":800, "height":400})
chart_HR.set_title({"name":"Heart Rate Trend (" + NameOfWorksheet + ")"})
chart_HR.set_plotarea({"border":{"color":"#000000", "width":1.25, "dash_type":"solid"}})
worksheet.insert_chart("J7", chart_HR)

workbook.close()

SpO2値のグラフの比較

 アプリのグラフとpythonでExcel化したデータから描画したグラフとを並べると、ばっちり合ってます。横軸の時間スケールも合ってます。

心拍数のグラフの比較

 心拍数のグラフもバッチリ合ってそうです。

おわりに

 やっと生データからテキストを抽出して、自由に使えるテキストデータを手にすることができました。プログラムを作成する時は頭をウンウン唸らせて考え込みますが、それだけに、目的のアウトプットが出た時の嬉しさは大きな達成感を味あわせてくれます。今度は、Excel化だけではなくて、Numpy / Pandas / Matplotlibを使ったグラフ化にも挑戦してもようと思います。

関連記事

パルスオキシメータを購入

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です