久々の Pythonネタ

Bokehという Pythonのライブラリの勉強を少しだけしました APIとかの変更が行われており、解説Blogでも 2016年のものではもう動作しません

最新のマニュアルを参照せねばなりません その結果こんなのできました

from bokeh.plotting import figure, output_file, show
x = []
import numpy as np
x = np.arange(0.1, 3.0, 0.1)

y0 = [i**2 for i in x]
y1 = [10**i for i in x]
y2 = [10**(i**2) for i in x]

# output to static HTML file
output_file("log_lines.html")

# create a new plot
p = figure(
   tools="pan,box_zoom,reset,save",
   y_axis_type="log", y_range=[0.001, 10**11], title="log axis example",
   x_axis_label='sections', y_axis_label='particles'
)

# add some renderers
p.line(x, x, legend="y=x")
p.circle(x, x, legend="y=x", fill_color="white", size=8)
p.line(x, y0, legend="y=x^2", line_width=3)
p.line(x, y1, legend="y=10^x", line_color="red")
p.circle(x, y1, legend="y=10^x", fill_color="red", line_color="red", size=6)
p.line(x, y2, legend="y=10^x^2", line_color="orange", line_dash="4 4")

# show the results
show(p)

これでできるグラフは下記のようになります

グラフ

しかもこのグラフ動くのです すごいですねえ

Python BootCamp in 茅ヶ崎 に参加してきました

茅ヶ崎駅から歩いてPython BootCamp in 茅ヶ崎会場まで行きました 案内では駅から徒歩5分ぐらいとあったのですが、スマホで検索してもなかなかわからず、何回か行き過ぎて15分ぐらい歩き回ってようやく会場に到着しました

今回の参加者は 12名でした そして講師の先生方か 5名と、とても手厚い講義でした

内容的には Python言語の基礎に始まり、簡単なプログラム作成、そして Scrapingを行いました 僕が改変して使用した Codeは以下の通りです

import requests
from bs4 import BeautifulSoup


def main():
    url = 'http://www.kamakuraheart.org'
    res = requests.get(url)
    content = res.content
    soup = BeautifulSoup(content, 'html.parser')
    columns = soup.find_all('div', class_='col-md-4')
    for column in columns:
        name = column.h2.text
        explain = column.p.text
        print('***項目***', name, "---内容---", explain)


if __name__ == '__main__':
    main()

これで結果は以下の通り

***項目*** TAVI/TAVR (経カテーテル的大動脈弁植え込み術/置換術)および僧帽弁閉鎖不全に対するカテーテル治療(TMVR: Transcatheter Mitral Valve Repair = 経カテーテル的僧帽弁修復術) ---内容--- ここ数年間で循環術です。当科は、この分野でも日本国内数少ない治験施設として、その実施に当たってきました。今でも重症大動脈弁狭窄症に対する治療法として外科的大動脈弁置換術しか治療法を提示されていませんでしょうか? 今や、TAVIという画期的な治療法を受けることができるのです。
***項目*** PCI (経皮的冠動脈インターベンション) ---内容--- 経皮的冠動脈インターベンション (PCI: かつては経皮的冠動脈形成術 PTCA とも呼ばれていました)は 1977年に当時チューリッヒ在住であった Dr. Andreas G治療が行われました。この時は、単純な風船の拡張により、冠動脈狭窄病変を拡大し成功裏に治療が行われました。この時より、既に40年間が経過し、その間に様々な治療法の改良と医学的知識の蓄積が行われ、現在では薬物界的にも先駆者であり、実際最初に齋藤 滋がPCIによる治療を行ったのは、昭和56年 (1981年)のことでした。それ以来、日本核内のみならず全世界での治療経験を有しています。
***項目*** カテーテル・アブレーション ---内容--- 頻拍性不整脈に対して、カテーテルから主として高周波電流通電を局所異常回路に対して行い、不整脈を根治する治療法です。伝統的な対象疾患は、発作性上室性頻拍症、れてきた高周波通電アブレーションのみならず、マイナス70度まで異常電流回路組織を冷却し、これにより異常回路を遮断するクライオ・アブレーション(Cryo Ablation)も多数の患者さんに対して行っており、患者さんに対する負担軽減に寄与しています。
***項目*** EVT (血管内治療) ---内容--- 以前は PTA (Percutaneous Transluminal Angioplasty: 経皮的経管的血管形成術)という呼び名が主流でしたが、必ずしも血管形成のみが治療の方法論ではないことから、最近では は、冠動脈に対する治療や、TAVI/TAVRも排除できませんが、一般的に EVTとは PAD (Peripheral Arterial Disease: 末梢動脈疾患)に対するものに限定されます。主な対象疾患は、閉塞性下肢動脈硬化症 特に、重症虚血肢 (Critical Limb Ischemia)、腎血管性高血圧症、内頚動脈狭窄症による一過性脳虚血性発作/脳梗塞症が挙げられます。
***項目*** 現在当科で行っている医療機器治験 ---内容--- 医療機器、特に循環器病分野の医療機器は急速に進歩しています。これにより以前は胸を開いての開心術でしか、あるいはそれ以前には薬物療法でだましだまし何と切り開くことなく)にカテーテルを用いて治療することが可能となってきました。この急速な医療機器の進歩をもたらしているのが、治験なのです。当科では、冠動脈内薬剤溶出性ステント、冠動脈内生体吸収性薬剤溶出性スキカテーテル左心耳閉鎖術など多数の最先端治験を実施しています。
***項目*** ペースメーカーなどの植え込み術 ---内容--- 現在では年間 200例を超える患者さんに対して、ペースメーカー植え込み術、ICD/CRT/CRT-D植え込み術を行っております。以前に体内式ペースメーカー植え込み行わえ込み電極が感染した場合に、電極を抜去して新しい電極を植えねばなりません。しかし、植え込みが長年経過していると、電極は心筋に強く接合しており、その抜去にはレーザーで癒着組織を剥離する必要があります。これを行うのが、エキシマレーザーを用いた植え込み電極レーザー抜去術でありますが、当院では神奈川県下でも数施設しか無い、施行の認定を受けており、他の施設からも抜去依頼を多数受けております。

要するに http://www.kamakuraheart.org/index.html を読み込み、その中からある特定の tagがついた部分を切り抜いたのです

でも一番参考となったのは、venvによる Python仮想環境の作成でした この過程では TA (Technical Assistance)の方々にお世話になりました

勉強会は 13:00に開始し、17:00で終了 その後小一時間会場で色々な雑談を行い、それから茅ヶ崎駅から近い居酒屋で二時間ぐらい歓談しました とても楽しかったです でもやっぱり僕がダントツにおじいちゃんでしたよ 一番若い参加者は現在大学生の 20歳ぐらいの方で、アルバイトでプログラムを書いている、という本格的な IT engineerでした 参加者の平均年齢は 35歳ぐらいでしょうか 以前参加したこのような会よりも少し年齢が高い印象でした

それにても茅ヶ崎駅に以前行ったのは うーん 20年ぐらい前でしょうか 随分と発展しているのに驚きました それでも人の流れが随分と少なくなった、ということでした

申し込みをして、実際に行くまで「参加するか」「キャンセルするか」と思い逡巡しました 昨日の会参加直前にも迷いに迷いました 一番恐れたのは「自分が参加して皆の足をひっぱるのでは?」という恐れでした しかしビルの屋上から飛び降りるような覚悟で参加してみてとても良かったです 決して知り合うことは無いであろう方々にも知り合うことが出来ました これからも思い切って参加したいと思います

 

AsiaPCR – Singaporeより戻ってきて

今年も引き続き、AsiaPCR -Singaporeに招聘され参加してきました このライブデモンストレーションは、昔 Singapore Live –> SinLiveとも呼ばれていたものですが、その後 EuroPCR groupに入り、今の名前となっています

という訳でこれまで Singaporeで開催されてきたのですが、色々な経済状況が背景としてあるのでしょうが、来年からは名前もまた代わり、場所も必ずしも Singaporeとは限らないかも知れません

今回は1月24日水曜日朝羽田発のSQ便で飛びました

去年もライブデモンストレーションのOperatorをしましたが、今年もさせて頂きました いやあ 今年の症例は戦略とか非常に難しい症例でしたが、最終的にはうまく奇麗に仕上げ、患者さんも僕に対して「ありがとう」と日本語で挨拶されました

どんな症例だったかと言えば、三枝病変で今回の標的は、左冠動脈主幹部分岐部および左前下行枝分岐部の病変です そもそも穿刺開始前より血圧が60mmHgぐらいの低値となり、カテーテル挿入も困難でした 当初、Powerpointでの選択的冠動脈造影を見る限りは、LAD-diagonal, OM-LCX, LMT-LCXと3つの bifurcationと考えられました そこで今回はLAD-diagonalには手を付けず OM-LCXとLMT-LCXのみ手を付け、それにはもちろん modified jailed balloon techniqueを用いる、そのような方針でした

しかし、LCX, OM, LADにガイドワイヤーを通過させ、OMの90%超の強い狭窄を2.0mm balloonで拡張したあと、非常に深い spider viewで撮影すると、何と LMT/OM(実はHigh Lateral Branchでした)/LAD/LCXの完全な trifurcation病変 + LAD/Diagonal bifurcationというものだったのです

えーっっっ、話が違うよ〜ーーー どうしよう これは臨床的にはCTOなんかよりもはるかに困難な症例です それをほぼぶっつけでライブデモンストレーションで行うのです うわーーー なんてことには僕の強い精神ではなりません むしろ「おおそうか それでは頭脳を働かして治療を楽しませて頂きましょう」そんな感じとなるのです

そして、まずはLCXに挿入していた一本のガイドワイヤーをDiagonalに入れ直し、 LAD-Diagonal bifurcationに対して、3.0mm DES + 1.5mm balloonで modified jailed balloonを行いここを完璧に仕上げました そして引き続き、3.0mm balloonをLMT – LADに置きながら、HLに対して 2.25mm DESを植え込みましたそして最後にLMT – LADに対して 3.5mm DESを入れ、4.5mm balloonで POTを行い、その後 IVUSで良好なステント拡張を確認し終了しました

会場からは modified jailed balloon techniqueの詳細についてものすごくたくさんの質問が飛び交い、満席の大会場は大変に盛況だったそうです

全ての手技を放映時間前に終了し、さあ その後はラーメンを食べに Singapore General Hospital/Singapore Heart Centerを後にしました

そのライブデモンストレーション以外にもたくさんの rolesが割り当てられ忙しかったのですね そして昨日26日金曜日 22:45 Changi空港発のSQ便羽田行きで今朝、6:10AMに羽田空港に到着しました

そのまま仮眠も取らずに今このブログを書いています 本日 13:00からは茅ヶ崎で Python Boot Campというのが開催され、Pythonのお勉強に昼から出かけてきます 多分僕以外の参加者は皆20歳台でしょうから環境に馴染めるかなあ それが心配です まあ顛末はまた報告させて頂きましょう

勇気を持って

あーー いや うーん でも いや 勇気をもって

ということで、逡巡した挙句にこれに参加申し込みしました 茅ヶ崎ですので近い、ということもあります でも勇気がいるのです (1) 例によって参加者の中では一番の高齢、皆がきっと「何だこのジジイは? そもそもキー打てるのかよ? Bashのコマンド知ってるのかよ?」なんて言葉が廻りから聞こえそうです (2) そもそも皆参加者はプロか、あるいはその方面を勉強している学生さんに決まってる 対して僕はインタベの医者ですよっ (3) そもそも身なりだって恥ずかしいし・・・

しかもですよ、この日は AsiaPCR Singapore Liveでライブの Operatorやってから、夜行便での帰国です 帰国した後、多分それは 6:00AM頃に羽田だから 13:00までまったりと時間を過ごさねばならないし・・・

でもこれが人生最後のチャンスかもしれないし、今後の僕の生き方を決める大きなことかも知れないし、勇気を振り絞り参加することにしました

1月27日

Python Boot Camp in 神奈川

主催 : 一般社団法人 PyCon JP

ハッシュタグ :#pycamp
募集内容

参加者2000円(前払い) 先着順  9/10

前払いについて

前払いについての連絡先:

(参加者にのみ公開されます)

キャンセル・参加費用の払い戻しについて主催者からの説明:

* キャンセルされた場合、参加費用の払い戻しは出来ません。
* キャンセル操作をせずに、ご自身で他の参加者に権利を譲る等の対処をお願いいたします。
(受付番号とconnpassのIDが記載された参加票を譲渡先にお渡しください)
* もしイベントが中止となった場合は、全額返金いたします。

失望の土曜日

先週の土曜日 午前中の外来診療を終え、午後にいそいそと自転車で「栄和堂」に向かいました ところが、その日は午後店じまいだったのです どうやらその夜に「栄和堂」の中で小規模な演奏会が開催されるための準備でお店を閉じていたのです がっかり とぼとぼと帰宅しました

ところで、何時も忘れてしますますので記録しておきます

$### pipそのものを upgradeする

$pip install --upgrade pip


$### pipでインストールした package全てを upgradeする

$pip-review --auto

何時も忘れてしまうので記録しておきます

 

本日の Python勉強

どのようにオブジェクトを下位関数に渡すか悩みました まず JPEG Tagの定義ファイル defTag.pyです

# defTag.py

jpegTag = {
    'TAG': '0xFF',
    'DHT': '0xFFC4',
    'SOI': '0xFFD8',
    'EOI': '0xFFD9',
    'SOF': '0xFFC3',
    'SOS': '0xFFDA'

}

if __name__ == '__main__':
    print(jpegTag)
    print(jpegTag['TAG'], jpegTag['DHT'])

つぎに、これを読み込んで JPEG Tagの DHT, SOI, EOIアドレスを探すルーチンです searchJPEGTAGS.pyです

import sys
from bitstring import ConstBitStream
from defTag import jpegTag

def searchDHTs(file_obj):
    DHTs = list(file_obj.findall(jpegTag['DHT'], bytealigned=True))
    return DHTs

def searchSOIs(file_obj):
    SOIs = list(file_obj.findall(jpegTag['SOI'], bytealigned=True))
    return SOIs

def searchEOIs(file_obj):
    EOIs = list(file_obj.findall(jpegTag['EOI'], bytealigned=True))
    return EOIs

if __name__ == '__main__':
    xa = ConstBitStream(filename=sys.argv[1])
    print(searchDHTs(xa))
    print(searchSOIs(xa))
    print(searchEOIs(xa))

そして、ここに file objectを渡す上位ルーチンです ファイル名は test_searchJPEGTAGS.py です

from searchJPEGTAGS import searchDHTs, searchSOIs, searchEOIs
import sys
from bitstring import ConstBitStream

xa = ConstBitStream(filename='XA1.dcm')
print(searchDHTs(xa))
print(searchSOIs(xa))
print(searchEOIs(xa))

これを走らせると結果は 以下のようになります

[10352, 5398688, 6469600, 11807904, 17146912, 22482576, 27843824, 33224096, 38652544, 44104704, 49597104, 55103072, 60640800, 66181296, 71753056, 77315744, 82880880, 88431792, 93978432, 99509264, 105038752, 110572944, 116110208, 121646512, 127184304, 132726336, 138267056, 143811248]
[10336, 5398672, 6469584, 11807888, 17146896, 22482560, 27843808, 33224080, 38652528, 44104688, 49597088, 55103056, 60640784, 66181280, 71753040, 77315728, 82880864, 88431776, 93978416, 99509248, 105038736, 110572928, 116110192, 121646496, 127184288, 132726320, 138267040, 143811232]
[5398584, 6469504, 11807800, 17146808, 22482472, 27843728, 33224000, 38652440, 44104600, 49597008, 55102976, 60640696, 66181200, 71752952, 77315648, 82880776, 88431688, 93978328, 99509160, 105038656, 110572840, 116110104, 121646408, 127184208, 132726232, 138266952, 143811152, 149355088]

要するに XA1.dcmという DICOM XAファイルの中の JPEG Tagアドレスが検出されたのです もちろん XA fileのフレーム数分あります

Python関数引数に関する考察

どうしても訳が分からないことがありました 要するに Pythonでの関数の名前空間が分かっていなかったのです 特にリストを引数で渡す場合です ここには、「リストの場合関数内部でグローバル変数を変化させれば、関数から出てもその変化は引き継がれる」というように記載されています

ところがこのテストプログラムではそのようにならないのです

from bitstring import ConstBitStream
from defTag import jpegTag

DHTs = []
print("DHTs: {}".format(id(DHTs))) #A

def searchTags(DHTs):
    xa = ConstBitStream(filename='xa1.dcm') #B
    print("DHTs: {}".format(id(DHTs)))
    DHTs = list(xa.findall(jpegTag['DHT'], bytealigned=True))
    print("DHTs: {}".format(id(DHTs))) #C


if __name__ == '__main__':
    searchTags(DHTs)
    print("DHTs = {0}".format(DHTs))

この場合、出力はA, B, Cの値(アドレス)が出力され、最後に゜カラリスト[]が出力されます そして興味深いことには A == Bであるにもかかわらず B != Cなのです つまり関数内で新たに変数が作成された訳です これは意図する動作ではありません そこで調べるとここに詳細に記載がありました これによれば、関数内で変数に変更を行えば、関数内の変数とグローバル空間の変数は同じアドレスを参照するようです そこで以下のテストを書きました

from bitstring import ConstBitStream
from defTag import jpegTag

DHTs = []
print("#A: {}".format(id(DHTs))) #A

def searchTags(DHTs):
    xa = ConstBitStream(filename='xa1.dcm')
    print("#B: {}".format(id(DHTs))) #B
    DHTs.extend(list(xa.findall(jpegTag['DHT'], bytealigned=True)))
    print("#C: {}".format(id(DHTs))) #C


if __name__ == '__main__':
    searchTags(DHTs)
    print("DHTs = {0}".format(DHTs))

これで走らせてみると当方での出力は以下の通りでした

#A: 4441990408
#B: 4441990408
#C: 4441990408
DHTs = [10352, 5398688, 6469600, 11807904, 17146912, 22482576, 27843824, 33224096, 38652544, 44104704, 49597104, 55103072, 60640800, 66181296, 71753056, 77315744, 82880880, 88431792, 93978432, 99509264, 105038752, 110572944, 116110208, 121646512, 127184304, 132726336, 138267056, 143811248]

これで意図した通りとなりました 要するに関数内で値を受け渡したいリストに変更を加えることにより関数内変数とグローバル変数が同じアドレスを参照したことにより参照渡しが実現された訳です

やったあ

Pythonで DICOM XAに挑む編です

XA fileは動画シネ(シネには限らず USなんかもですが・・・)をコマ(= Frame)毎にパラパラ漫画のように連続して記録してあります

そして、それぞれの Frameは Huffman Code圧縮されていて、その圧縮のキーはそれぞれの Frame毎に設定されています これは圧縮効率を最大にするためなのです

そこで、これを解読するためには、Frame毎に設定されている Huffman定義テーブル = Define Huffman Table (DHT)を検出し、フレーム毎に分離する必要があります

このため、まず行っていることはフレーム毎に分離するため、DHTを検出することです これをやってみました

from defTag import jpegTag
from bitstring import ConstBitStream
from defTag import jpegTag
DHT_address = []

xa = ConstBitStream(filename='xa1.dcm')
print(list(xa.findall(jpegTag['DHT'], bytealigned=True)))

これで以下のようなリストが出力されました

10352, 5398688, 6469600, 11807904, 17146912, 22482576, 27843824, 33224096, 38652544, 44104704, 49597104, 55103072, 60640800, 66181296, 71753056, 77315744, 82880880, 88431792, 93978432, 99509264, 105038752, 110572944, 116110208, 121646512, 127184304, 132726336, 138267056, 143811248]

これは xa1.dcmという DICOM XA fileの中の DHTの位置を表していることになります これを用いて Frame毎にファイルを切り分けて暗号解読すれば良いということになりますね

ちなみに、defTag.pyは以下のように定義しました

jpegTag = {
    'TAG': '0xFF',
    'DHT': '0xFFC4',
    'SOI': '0xFFD8',
    'EOI': '0xFFD9',
    'SOF': '0xFFC3',
    'SOS': '0xFFDA'

}

if __name__ == '__main__':
    print(jpegTag)
    print(jpegTag['TAG'], jpegTag['DHT'])

何事もうまくいかないな

昨日はプックスペース栄和堂に行ってゆったりとした時間を過ごすつもりでした それで丁度 12:00頃 Cross Bikeで病院を出発し向かいました

そして店の前の自転車スペースで自転車に鍵をしてさあドアを開けようとすると何と Closedとなっていたのです

仕方なく諦め、自宅を通り過ぎて坂道の途中にあるのを認識していた別の Cafeに向かったのです 結構きつい坂道で、Cross Bikeのギアを思い切り lowにしたのですがその途端にチェーンが外れ、そのまま前ギアに挟み込み、いくら手で戻そうとしても戻りません 仕方なく、自転車を転がしながら自宅にトボトボと歩いて自転車を押して帰りました

そして自宅でドライバーなどを用いて何とか復旧しようとしたのですががっちりとギアにチェーンが挟まれ 何ともできず、修理に出さねばならない事態に陥りました この Cross Bikeは先日定期点検から戻ってきたばかりで、何となくギアに音がして、しかもチェーンの張りが問題ありそうでした 案の定こんなことになってしまいました

結果的に土曜日午後優雅にカフェで思索に耽る、その予定が全部駄目となりました 本当にこの世の中は思うようには行きません

それで、朝から MacBook Proで PyCharmを立ち上げ色々と設定していたのですが、内蔵する Terminal Windowを開くとどうも文字コードの関係なのでしょうか うまいこと動作しません 色々と探してみるとこのページに遭遇しました その通りに設定してみるとスイスイとターミナルが動作するようになりました ありがたい まあ思いどおりに行かないこともあれば 何とな解決することもありますね めげない めげない

こんなことも知らなかった

Ron先生に刺激され、少しというか一ヶ月間ぐらいサボっていた Pythonのプログラミングに本日取り組みました 朝から外来診療し、その後PCIでしたが、PCIは若者に任せ自分は Pythonでした

ツールは PyCharmと、VisualStudio Codeです なかなか VisualStudio Codeが使い勝手が良いです

  • やりたいこと
    • jpeg tagを使いまわしできるように一つのファイルにまとめ、使用する時に importする
  • ファイル1 = defineTag.py
    • これは importされるファイルであり、ここにjpeg tag定義をまとめる
  • ファイル2 = test_defineTag.py
    • これがメイン・ルーチンでありここから importする
# defineTag.py

jpegTag {
    'TAG': 0xFF,
    'DHT': 0xC4,
    'SOI': 0xD8,
    'EOI': 0xD9,
    'SOF': 0xC3,
    'SOS': 0xDA
}

if __name__ == '__main__':
    print(jpegTag)
    print(jpegTag['TAG'])

そしてこれを呼び出すメインです

#test_defineTag.py


from defTag import jpegTag

print(jpegTag)
print(jpegTag['TAG'])

これをターミナルから呼び出します

$ python test_defTag.py

そうすると、以下のように応答されます

{'TAG': 255, 'DHT': 196, 'SOI': 216, 'EOI': 217, 'SOF': 195, 'SOS': 218}
255

これで使いたい時に、jpegTagを呼び出せば良いということになります ここまで来るのに数時間必要でした