Magicode logo
Magicode
O
3

Python Tutorial - 心音解析 -

25 min read

心音データの解析をしながら、以下のことを学んでいきます。

Pythonの簡単な文法

Google Colabの使い方

Google Colabノートブックは複数のセルで構成されます。

セルにはテキストセルコードセルの2種類があります。

テキストセル(この文章が書かれている場所)にはテキストのみを,コードセルにはPythonなどのプログラムを記述することができます・

コードセルでは、記述したプログラムにおける,最後に書いた値が表示されます。

コードセルを実行するには,実行したいセルの左端の再生ボタンを押します。

@@@@@


'全国医療AIコンテスト'

標準入出力

標準入力とは,キーボードからの入力のことで,標準出力とは,ディスプレイへの出力のことです。


あなたの名前を入れてください→ name name は全国医療AIコンテストに参加しています

変数

変数は、値を保持する「箱」のようなものです。

変数には、数値や文字列など、どんな値でも入れることができます。

変数に値を入れることを「代入」と言います。

変数に値を代入するには、「=」を使います。

変数名には、アルファベット・数字・アンダーバーの組み合わせが使えますが、数字から始まる変数名は無効です。


演算

演算には、以下のように様々な種類があります。

  • +:足し算
  • - :引き算
  • *:掛け算
  • /:割り算の商
  • %:割り算の剰余
  • **:累乗

15 16 2.4 2.4 2 125 100 What is Artificial Intelligence 123Artificial Intelligence

条件分岐

値に応じてプログラムの処理を変更したいときは、if 文を使って条件分岐を行います。

if文の書式


if文の中で実行したい処理には、インデント(字下げ)することに注意しましょう。 インデントするには、tabキーを押します。

if文で使える条件式

  • ==(等号):右辺と左辺が等しいときのみ真
  • !=(ノットイコール):右辺と左辺が等しくないときのみ真
  • <(小なり):左辺が右辺より小さいとき真
  • <=(小なりイコール):左辺が右辺以下であるとき真
  • >(大なり):左辺が右辺より大きいとき真
  • >=(大なりイコール):左辺が右辺以上であるとき真

血圧をいれてください。140 あなたは高血圧です

リスト

リストは,数値や文字列,様々な値を複数持つことができるデータ構造です。

例えば,リストは以下のように表されます。


リストの中のn番目の要素にアクセスするには,リストを格納した変数の後ろに [n] を付けて表します。ここで、要素番号は0から始まるので、左から数えて4番目の要素のインデックスは 41=34-1=3 になります。


リストのリストを定義することもできます。「リストの入れ子」「多次元リスト」とも呼びます。


繰り返し(for文)

for文の書式を以下に示します。


ループ変数には、リストの要素が頭から順番に代入されていきます。 そしてそのたびに繰り返したい処理が繰り返されます。

for文の中で繰り返したい処理には、インデント(字下げ)することに注意しましょう。


sternocleidmastoid sternocleidmastoid sternocleidmastoid sternocleidmastoid sternocleidmastoid

関数

プログラミングでは、まとまった同じ処理を複数行うことがよくあります。

そのとき、それらの処理を全部書いているとプログラムの行数が増えて読みにくくなってしまいます。

そこで、「処理のまとまり」を表現する書式が生まれました。それが関数です。

関数を定義する書式を以下に示します。


関数を実行する書式を以下に示します。


それではカレーを作ってみましょう。


カレーを作ります ↓ まず肉を炒めます ↓ その後野菜をいためます。 ↓ ルーを入れて煮込むと完成です。 ↓ 肉と野菜のカレーの完成です

次に,引数と戻り値付きの関数を定義する書式を以下に示します。


関数を実行して、戻り値を変数xxに代入する書式を以下に示します。


戻り値とは,関数によって生成され,関数の外のプログラムに渡す値のことです。

それでは、カレーを作った後に、足し算を行う関数addを定義して実行してみます。


カレーを作ります ↓ まず肉を炒めます ↓ その後野菜をいためます。 ↓ ルーを入れて煮込むと完成です。 ↓ 肉と野菜のカレーの完成です

12

さてPythonの文法がわかったところで、実際の医療データ解析をしてみましょう。

公開医療データのダウンロード

今回は 公開されている The PASCAL Classifying Heart Sounds Challenge 2011(CHSC2011) という心音データをダウンロードしてみましょう。このデータは心音のS1領域・S2領域を特定するというタスクと、正常か異常かを分類するタスクの2つを解かせるために公開されています。

まず linuxコマンド を使って 公開されているデータをダウンロードしましょう。下のセルでは heartbeat というディレクトリをデフォルトのディレクトリ /content の下に作成し、そこに関連データをダウンロードしています。

コマンドとはコンピュータに計算やファイルの操作を行うように指示できるツールです。今回は詳しく触れませんが、colabのようなノートブックではpythonとコマンドを混ぜて使えるのが一つの強みです。

/content/heartbeat
--2021-05-18 04:24:23--  http://www.peterjbentley.com/heartchallenge/Atraining_normal_seg.csv
Resolving www.peterjbentley.com (www.peterjbentley.com)... 209.151.22.160
Connecting to www.peterjbentley.com (www.peterjbentley.com)|209.151.22.160|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3412 (3.3K) [text/csv]
Saving to: ‘Atraining_normal_seg.csv’

Atraining_normal_se 100%[===================>]   3.33K  --.-KB/s    in 0s      

2021-05-18 04:24:23 (432 MB/s) - ‘Atraining_normal_seg.csv’ saved [3412/3412]

--2021-05-18 04:24:23--  http://www.peterjbentley.com/heartchallenge/Atraining_normal.zip
Resolving www.peterjbentley.com (www.peterjbentley.com)... 209.151.22.160
Connecting to www.peterjbentley.com (www.peterjbentley.com)|209.151.22.160|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 13876165 (13M) [application/zip]
Saving to: ‘Atraining_normal.zip’

Atraining_normal.zi 100%[===================>]  13.23M  6.31MB/s    in 2.1s    

2021-05-18 04:24:26 (6.31 MB/s) - ‘Atraining_normal.zip’ saved [13876165/13876165]

Archive:  Atraining_normal.zip
   creating: Atraining_normal/
  inflating: Atraining_normal/.DS_Store  
   creating: __MACOSX/
   creating: __MACOSX/Atraining_normal/
  inflating: __MACOSX/Atraining_normal/._.DS_Store  
  inflating: Atraining_normal/201101070538.aif  
  inflating: __MACOSX/Atraining_normal/._201101070538.aif  
  inflating: Atraining_normal/201101151127.aif  
  inflating: __MACOSX/Atraining_normal/._201101151127.aif  
  inflating: Atraining_normal/201102081152.aif  
  inflating: __MACOSX/Atraining_normal/._201102081152.aif  
  inflating: Atraining_normal/201102081321.aif  
  inflating: __MACOSX/Atraining_normal/._201102081321.aif  
  inflating: Atraining_normal/201102201230.aif  
  inflating: __MACOSX/Atraining_normal/._201102201230.aif  
  inflating: Atraining_normal/201102260502.aif  
  inflating: __MACOSX/Atraining_normal/._201102260502.aif  
  inflating: Atraining_normal/201102270940.aif  
  inflating: __MACOSX/Atraining_normal/._201102270940.aif  
  inflating: Atraining_normal/201103090635.aif  
  inflating: __MACOSX/Atraining_normal/._201103090635.aif  
  inflating: Atraining_normal/201103101140.aif  
  inflating: __MACOSX/Atraining_normal/._201103101140.aif  
  inflating: Atraining_normal/201103140132.aif  
  inflating: __MACOSX/Atraining_normal/._201103140132.aif  
  inflating: Atraining_normal/201103140135.aif  
  inflating: __MACOSX/Atraining_normal/._201103140135.aif  
  inflating: Atraining_normal/201103140822.aif  
  inflating: __MACOSX/Atraining_normal/._201103140822.aif  
  inflating: Atraining_normal/201103151912.aif  
  inflating: __MACOSX/Atraining_normal/._201103151912.aif  
  inflating: Atraining_normal/201103170121.aif  
  inflating: __MACOSX/Atraining_normal/._201103170121.aif  
  inflating: Atraining_normal/201103221214.aif  
  inflating: __MACOSX/Atraining_normal/._201103221214.aif  
  inflating: Atraining_normal/201104122156.aif  
  inflating: __MACOSX/Atraining_normal/._201104122156.aif  
  inflating: Atraining_normal/201104141251.aif  
  inflating: __MACOSX/Atraining_normal/._201104141251.aif  
  inflating: Atraining_normal/201105011626.aif  
  inflating: __MACOSX/Atraining_normal/._201105011626.aif  
  inflating: Atraining_normal/201105021654.aif  
  inflating: __MACOSX/Atraining_normal/._201105021654.aif  
  inflating: Atraining_normal/201105021804.aif  
  inflating: __MACOSX/Atraining_normal/._201105021804.aif  
  inflating: Atraining_normal/201105151450.aif  
  inflating: __MACOSX/Atraining_normal/._201105151450.aif  
  inflating: Atraining_normal/201106111136.aif  
  inflating: __MACOSX/Atraining_normal/._201106111136.aif  
  inflating: Atraining_normal/201106141148.aif  
  inflating: __MACOSX/Atraining_normal/._201106141148.aif  
  inflating: Atraining_normal/201106151236.aif  
  inflating: __MACOSX/Atraining_normal/._201106151236.aif  
  inflating: Atraining_normal/201106210943.aif  
  inflating: __MACOSX/Atraining_normal/._201106210943.aif  
  inflating: Atraining_normal/201106221418.aif  
  inflating: __MACOSX/Atraining_normal/._201106221418.aif  
  inflating: Atraining_normal/201106221450.aif  
  inflating: __MACOSX/Atraining_normal/._201106221450.aif  
  inflating: Atraining_normal/201108011112.aif  
  inflating: __MACOSX/Atraining_normal/._201108011112.aif  
  inflating: Atraining_normal/201108011114.aif  
  inflating: __MACOSX/Atraining_normal/._201108011114.aif  
  inflating: Atraining_normal/201108011115.aif  
  inflating: __MACOSX/Atraining_normal/._201108011115.aif  
  inflating: Atraining_normal/201108011118.aif  
  inflating: __MACOSX/Atraining_normal/._201108011118.aif  
Atraining_normal  Atraining_normal_seg.csv  Atraining_normal.zip  __MACOSX
/content

ダウンロードされたファイルは左のバーのフォルダーマークを押すとみることができます。

今回はいろんなPythonモジュールを使って、一つの心音データを図示することを目標にしましょう。

モジュールとは

あらかじめ定義された関数群をモジュールといいます。

科学技術計算や機械学習、アプリケーション開発では、同じ複雑な処理を複数回行うことがあるので、先人達がすでに有用な関数を実装してくれています。

モジュールを使うには、import文を使います。

import文を使うことで、そのモジュールが、今自分の書いているプログラムの中で定義されているように使用することができます。


まずはデータがどんな構造をしているのかみていくために、pandasというモジュールを使っていきましょう。

Pandasの使い方

Pandasとは、テーブルデータの処理に特化したライブラリです。データフレームと呼ばれる形式のデータを加工することができ、データの結合や切り取り、表計算、時系列データの取り扱いなどが可能です。また、簡単なグラフ化も同時にできます。

Pandasは、データサイエンスの分野でよく使われるライブラリで、機械学習を行う前にデータの整形を行う前処理のツールとして使用することが多いです。

csvデータの読み込み

まずはCHSCデータのS1とS2の位置情報がまとまっているcsvファイルの中身をみてみましょう。pandas は慣例的にpdとしてimportします。 pd.read_csv(ファイル名)でデータを読み込めます。


データフレームの情報

df.head()で先頭の5行をみることができます。


.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}

各行は一つ一つの心音データに関する情報を表してます。それぞれデータが記録されているファイル名とS1、S2それぞれの位置情報を表していると考えられます。

df.shapeでデータのサイズをみることができます。


(21, 40)

21件のデータがあることがわかります。

データの抽出

特定の列を取り出したい時は、df[列の名前]あるいはdf.列の名前で取り出せます。列が全部取ってこられるので、特定の行を取り出したい時はそのインデックスを指定する必要があります。

Unnamed: 0という列のインデックスが1の要素の中身をみてみましょう。


'201102260502.aif'

波形のデータが記録されているファイル名は、Unnamed: 0に書かれています。

少し発展してS1とS2に関する情報をそれぞれ抽出してみましょう。


[nan 27941 58163 88955 122028 152821.0 197869.0 230942.0 265168.0 nan nan nan nan nan nan nan nan nan nan] [11526 42197 71278 102641 134573 184753.0 216686.0 251211.0 nan nan nan nan nan nan nan nan nan nan nan]

次は取得したFILEにどのようなデータが入っているのかみてみましょう。

オーディオデータの構造

今回は心音データが aif というファイル形式を扱うので、それに特化した aifc というモジュールを使用します。今回のように知らないモジュールを使う場面に沢山出くわしますが、全ての使い方を覚えなければいけないというわけではありません。知らない人でも使えるように、モジュールを作った人たちが使い方の解説を書いてくれています。色んなサイトで使い方の記事がありますが、基本的には公式のドキュメントが一番正確です。今回の aifc というモジュールはPythonにもともと入っている標準ライブラリで、こちらのPython公式ドキュメントに使い方が書いてあるので、参考にしながら使ってみます。

まずは先程 pandas で抽出したデータの保存先ファイル名から読み込んでどんなデータなのかみてみましょう。


_aifc_params(nchannels=1, sampwidth=2, framerate=44100, nframes=277130, comptype=b'NONE', compname=b'not compressed')

公式ドキュメントによると、

オーディオファイルには、オーディオデータについて記述したパラメータがたくさん含まれています。サンプリングレートあるいはフレームレートは、1秒あたりのオーディオサンプル数です。チャンネル数は、モノラル、ステレオ、4チャンネルかどうかを示します。フレームはそれぞれ、チャンネルごとに一つのサンプルからなります。サンプルサイズは、一つのサンプルの大きさをバイト数で示したものです。したがって、一つのフレームは nchannels * samplesize バイト からなり、1秒間では nchannels * samplesize * framerate バイトで構成されます。

ということなので、上のparamsで返されたメタ情報から今回の音声ファイルは

チャンネル数 = 1 (モノラル)
サンプルサイズ = 2 [byte]
サンプリングレート = 44100 [Hz]

で一つのフレームは 1 * 2 = 2 [byte] 、1秒間では 1 * 2 * 44100 = 88200 [byte] であることがわかります。


6.284126984126984

長さは 6.28 [s] であることもわかります。このようにオーディオデータはフレームという単位に情報が格納されています。

次に全てのフレームのデータを抽出しましょう。


<class 'bytes'>

Pythonでは扱うデータをオブジェクトと呼びますが、オブジェクトの型は type(object) でみることができます。フレームデータはバイト型であることがわかりました。

開いたファイルは閉じてあげます。


それでは心音データをnumpy配列として変換してみましょう。データをnumpy配列として扱うことは機械学習において後々楽になることが多いです。

Numpyの使い方

numpy は行列の処理に非常に長けたモジュールです。

慣例的にnpでimportします。


Numpy配列

次に、Numpyで定義されたリストの拡張である「Numpy配列」を使います。

Numpy配列は,以下のように使います。


次に使用例を示します。


array([1, 2, 4])

Numpy配列には、多次元リスト(リストのリスト)を渡すこともできます。

多次元リストをNumpy配列に渡すと,多次元配列と呼ばれます。

このようにして、行列やベクトルを表現できます。


array([[1, 2, 3], [2, 3, 4], [3, 4, 5]])


array([[1], [2], [3]])

Numpy配列の形を調べたいときは、np.array.shapeを参照します。


(3, 3)

行列の演算

Pythonのリストよりも、Numpy配列の方が線形代数演算に向いています。

ベクトルや行列を含む計算には、積極的にNumpy配列を使います。

例えばこのように、+記号でベクトル同士の和を表現できたり、*記号で定数倍を表現できます。


array([[3], [7], [9]])


array([[3], [6], [9]])

@演算を使って行列同士の積や行列とベクトルの積を表現できます。


array([[14], [20], [26]])

Note

実は行列演算に特化している点とは別にNumpyを使う大きなメリットがあります。それは実行時間

です。Numpyの内部ではC言語によって記述されていて、また行列演算にはBLAS APIという高性能の行列演算ライブラリを用いているのでPythonとは比べ物にならないくらい高速となります。

フレームデータの変換

それではバイト型のフレームデータを numpy配列 に変換してみましょう。np.frombuffer(bytes file, dtype = "型を指定") で変換することができます。

また今回は1フレームで2 [byte] = 16 [bit]なのでファイル規格から dtype = "int16" を指定することも大事です。データの構造も知っている必要はなくて、わからなかったことは調べたら出てきます。


array([ 12542, 13822, 8446, ..., -29441, -29697, -28161], dtype=int16)

S1、S2データの整形


array([nan, 27941, 58163, 88955, 122028, 152821.0, 197869.0, 230942.0, 265168.0, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan], dtype=object)

配列の中身をみるとわかりますが、nan が混ざっていたり、小数点がついていたりついてなかったりするので、整形しましょう。



ValueError                                Traceback (most recent call last)

<ipython-input-29-77bdc267fed2> in <module>()
----> 1 s1data = s1data.astype(np.int32)


ValueError: cannot convert float NaN to integer

[ nan 27941. 58163. 88955. 122028. 152821. 197869. 230942. 265168. nan nan nan nan nan nan nan nan nan nan] [ 11526. 42197. 71278. 102641. 134573. 184753. 216686. 251211. nan nan nan nan nan nan nan nan nan nan nan]

次に nan 以外の値だけ抽出しましょう。


[ 27941. 58163. 88955. 122028. 152821. 197869. 230942. 265168.] [ 11526. 42197. 71278. 102641. 134573. 184753. 216686. 251211.]

出力からわかるように、それぞれ整数で各S1、S2のフレーム位置を表していると推測できます。ということはフレームの番号をインデックスにしたら対応する心音データの値をとってこれることになります。インデックスとして使いたいので、ここでは整数に変換しましょう。


[ 27941 58163 88955 122028 152821 197869 230942 265168] [ 11526 42197 71278 102641 134573 184753 216686 251211]

それでは最後に抽出した II誘導 の波形データを図示してみましょう。

Matplotlibの使い方

import周りの注意点

データの理解において可視化は非常に重要です。

Matplotlibでは、ほとんどの描画機能がpyplot.機能名で提供されています。慣例的にimport matplotlib.pyplot as pltでimportし、plt.機能名でさまざまな描画機能を使えるようにします。

%matplotlib inlineは、Notebook上にグラフを表示するためのマジックコマンドです。忘れやすいので、注意しましょう。


心音データの図示

散布図や円グラフ、棒グラフ、線グラフなどいろんなグラフの種類をこのmatplotlibを使って描画することができます。

今回は心音を図示したいので、点と点を連続的につなげる線グラフを描画します。この点と点を繋げてプロットするには、plt.plot(x, y)を使います。100個の点を図示したかったらそれぞれのx座標とy座標の配列を入れてあげます。配列の形式はリストでもnumpy配列でもどちらでも大丈夫です。

またグラフのスタイルを調節する機能もたくさんあるので、実際に使う時は色々試してみましょう。


png

おまけの宿題

解答はこちらのgithubからみることができます。

問題1

index = 1 のデータのプロットに重ねて、S1とS2それぞれのフレーム位置を垂直線でマーキングしてみましょう。


問題2

ある index の心音データの波形を図示する関数を作ってみましょう。



問題3

ある index の心音データのプロットに重ねてS1とS2のフレーム位置を垂直線でマーキングする関数を作ってみましょう。



Discussion

コメントにはログインが必要です。