8-1.全食品について、条件にあったピークを集める - nsaku/xrepo-api-tutorial GitHub Wiki
やること
・APIを組み合わせて大規模な検索をする。 ・選んだピーク情報だけ、別の配列に保持する。
前項までで、forやifを使って、大量のピークデータから、条件に合ったピークを選別する方法を学びました。 ただし、一つのサンプルについての実施例だけを見てきました。
APIの書式を見ると、サンプルのピーク情報を得るには、「食品番号」と「posかnegか」を指定すればよいです。
私たちはすでに、サンプル一覧を取得するAPIを知っているので、全食品の食品番号のリストを得ることができます。
両者を組み合わせれば、全食品について条件にあうピークを選ぶことができそうです。
(前準備)条件にあったピークを覚えておく
前項まででは、条件にあったピークが見つかった場合、それを直接画面に出力しました。 条件に合ったピークだけを、後で再利用できるように、別の配列変数に覚えさせる方法を見てみましょう。
test9.pyにコードを書いてゆきます。ここでは、MS2スペクトルでのFlavonoidSearchのスコアが0.3以上だったものを選んでみましょう。
import requests
url = "http://metabolites.in/foods/api/peaklist/05018_1/pos"
peaks = requests.get( url ).json()
peaks_fs2 = [] # -- 選んだピークを覚えておくための新たな配列
for peak in peaks:
if( 0.3 < peak["scoreFs2"] ):
peaks_fs2.append( peak ) # -- 条件に合ったものを配列に追加
print( len( peaks_fs2 ) ) # -- 配列の要素数(選ばれたピーク数)を表示
結果は以下になったと思います。
2
>>>>
プログラムの解説
選んだピークを覚えておくために、新たな配列「peaks_fs2」を準備しています。 新たな配列を作るときは、要素がない空の配列([])を代入して、
peaks_fs2 = []
と書きます。
元のデータも配列だったので、選んだピークの情報(オブジェクト)だけを、 新しい配列に付け加えてゆきます。これは、配列が持つappend()を使って、
peaks_fs2.append( peak )
と書きます。
プログラムの最後では、追加されたピークの数を知るために、len()関数を使って、 配列要素の数を書き出しています。
print ( len( peaks_fs2) )
数を数えるやり方にはこのような方法もあります。
覚えておく情報をもう少し拡張する(if, elif, else)
せっかくなので、ほかの条件に合うピークを取得することも考えてみましょう。 MS2だけでなく、MS3でFlavonoidSearchのヒットスコアが高いもの、などを選んでみましょう。
ifでは、ifの条件に合わなかった場合に、別の条件に合う場合の処理(elif)や、代わりに行う処理(else)を 記述することができます。
import requests
url = "http://metabolites.in/foods/api/peaklist/05018_1/pos"
peaks = requests.get( url ).json()
peaks_fs2 = []
peaks_fs3 = []
peaks_other = []
for peak in peaks:
if( 0.3 < peak["scoreFs2"] ):
peaks_fs2.append( peak )
elif( 0.3 < peak["scoreFs3"] ):
peaks_fs3.append( peak )
else:
peaks_other.append( peak )
print( len( peaks_fs2 ) )
print( len( peaks_fs3 ) )
print( len( peaks_other ) )
結果は以下のようになりました。
2
15
7943
>>>
if()の条件に合わない時ときに、elif()の条件が評価されます。それにも合わない場合、elseの処理が行われます。 elseには()の条件が必要ないことに注目してください。 elif()は、上の例では一つだけですが、複数個設けることもでき、上から順番に条件に合うまで評価されてゆきます。
結果を見るとほとんどがOtherに分類されるようですね。
元のピークの数とあっているかどうか、コードの最後に以下を追加して確認してみましょう。
print( len( peaks ) )
sum = len( peaks_fs2 ) + len( peaks_fs3) + len( peaks_other)
print( sum )
peaks_fs2、peaks_fs3、peaks_otherの合計を計算しています。
2
15
7943
7960 # -- peaksの数
7960 # -- peaks_fs2, peaks_fs3, peaks_otherの合計 = sum
>>>
全食品で該当するピークを洗い出す
データの覚えさせ方が分かったので、全食品について条件に合うピークを見つけることを考えてみましょう。
ピーク一覧を取得するには「食品番号」が必要ですが、食品番号の一覧は、(*)で確認したように、別のAPIで取得できます。
そこで、以下のようにforを入れ子にすることで、この処理は実現できそうです。
test10.pyにコードを書いてゆきましょう。
ただし、注意点があります。
まだ実行しないでください!
import requests
url_samples = "http://metabolites.in/foods/api/samples"
samples = requests.get( url_samples ).json() # 1)
for sample in samples:
fid = sample["fid"] # 2)
url_peaks = "http://metabolites.in/foods/api/peaklist/" + fid + "/pos" # 3)
peaks = requests.get( url_peaks ).json() # 4)
for peak in peaks:
if( 0.3 < peak["scoreFs2"] ):
# to do something
if ( 0.3 < peak["scoreFs3"] ): # 5)
# to do something
プログラムの解説
-
- 食品一覧を取得するAPIで、食品のデータ(samples)を取得しています。
-
- forを使って各サンプルについて、その食品番号(sample["fid"])を取得し、
-
- それを、ピーク一覧を取得するAPIのURLとして使っています。FlavonoidSearchの結果はポジティブモードにしか 付与されていないため、"pos"の部分は決め打ちしています。
-
- そのサンプルのピーク一覧の情報を取得し、forを使って各ピークを評価し、ifを使って条件に合ったピークに 対する処理を書いてゆきます(今はまだ何の処理も書いていません)
-
- MS2で条件にマッチし、かつMS3で条件にマッチするものをあるかもしれません。そのためここでは、elifではなく、 独立したifを設けることにしました。ifはこのような使い方もできます。
プログラムをテストする
さて、これで「# to do something」のところに処理を書いてプログラムを実行すれば、目的が果たせそうですが、 ちょっと気になることがあります。
今まで1食品についてプログラムを実行したときも、それなりに数秒程度の待ち時間がありました。 ピーク一覧を取得するAPIを使うと、1サンプルついて数千、数万という膨大なピーク情報を転送するので、 どうしても時間がかかってしまいます。
これを、食レポに登録されている200以上の食品について全部行うと、きっと、恐ろしく時間がかかることになります。 ですから、実際に全サンプルへの処理を行うのは、プログラムがうまく動くことをテストしてからがよさそうです。
ということで、次のような方針でテストすることを考えてみましょう。
200の食品全部を行うのではなく、5個くらいから動作を確認してみることにします。 そこで、プログラムを以下のように書き直します。
import requests
url_samples = "http://metabolites.in/foods/api/samples"
samples = requests.get( url_samples ).json()
count_loop = 0 # 1) 追加
for sample in samples:
count_loop = count_loop + 1 # 2) 追加
if( 5 < count_loop ): # 3) 追加
break # 4) 追加
fid = sample["fid"]
print( str( count_loop ) + ". processing: " + fid ) # 5) 追加
url_peaks = "http://metabolites.in/foods/api/peaklist/" + fid + "/pos"
peaks = requests.get( url_peaks ).json()
for peak in peaks:
if( 0.3 < peak["scoreFs2"] ):
# to do something
continue # 6) 追加
if ( 0.3 < peak["scoreFs3"] ):
# to do something
continue # 6) 追加
-
- 繰り返し処理の回数を数えるcount_loopという変数を設けました。
-
- samplesが繰り返し処理されるたびに1ずつ足して行き、
-
- 5以上になったときに、
- 4)「break」というキーワードで、すぐ外側にある繰り返し処理(この場合は
for sample in samples:
)を強制終了しています。
さらに、今どこまで処理が進んでいるかを見るために、
-
- 現在処理している食品番号を書き出すことにしました。
forなどの繰り返し処理を、条件によって制御するのは、「break」だけではありません。「continue」というキーワードは、 そこに到達したときに、あとのコードを実行せずに、次の繰り返し処理を開始します。
-
- ifで行う処理が何も書かれていないと、pythonではエラーとなってしまうので、とりあえず、 continueで何もせずに次の繰り返しに回るようにしました。
テスト実行 (強制終了のしかた)
準備がよさそうなので、実行してみたいのですが、念には念を入れておきましょう。 もし何か手違えで繰り返しが終わらない状態になったときの、対処法を確認しておきます。
- python IDLEを使っている場合は、結果が表示されるウィンドウの右上の「×」ボタン(Windowsの場合)を押せば、 処理が強制終了されます。
- コマンドプロンプトで実行している場合は、「Ctrlキー + Cキー」を押すことで、強制終了できます。
よろしいでしょうか?
それでは、上記のプログラムを実行してみてください。
1. processing: 01026
2. processing: 01038
3. processing: 01069
4. processing: 01080
5. processing: 01083
>>>
無事、上記のような結果が得られたでしょうか?
おめでとうございます!
ここまでできれば、あとは実際にデータを処理するコードを書いてゆき、 テストして良さそうだったら、全食品を対象とした本番処理を行うだけです。