正規表示式 Regular Expression Python語言實戰-處理CGRD的report資料
處理CGRD的report資料 心臟超音波(M22-060) 讀出每一筆報告的LVEF (left ventricular ejection fraction) 它是一個1-99之間的數字
巨量資料 CGRD 5個資料夾 72個檔案 19.2G 上千萬筆 人工辨識
心內科報告 CGRD資料: ITEM = M22-060 2010~2017年:共621744筆 REPORT: 找出LVEF值 辨識度:70%→90%→95%→99%
LVEF = 32% LVEF(%) = 71 (M-mode)
EF by M-mode: XX % EF by 2-D method: XX%
LVEF M-mode(Teichholz) = XX %
LVEF M-mode(Teichholz) = XX-XX %, LVEF M-mode(Teichholz) = XX-XX %, Average = XX % 2D(M-Simpson) = XX-XX %, Average = XX %
LV SV/ EF (32-95ml/49-76%) OO / XX LV SV(ml)/ EF(%) (32-95/49-76) OO / XX
數值很長一串
沒有我們要判讀的LVEF值
EF by M-mode: XX % EF by 2-D method: XX% function SUBSTR CAT LOWCASE SCAN CATS UPCASE LEFT CATT PROPCASE RIGHT CATX FIND TRIM TRANWRD COMPRESS STRIP LENGTH COMPBL 前處理 大小寫轉換 空白處理 符號 找出pattern位置 FIND 計算字串長度 LENGTH 讀取2位數的LVEF SUBSTR
EF = XX%. LV SV/ EF (32-95ml/49-76%) OO / XX EF(%) = XX EF = XX% LV SV/ EF (32-95ml/49-76%) OO / XX EF(%) = XX LV SV(ml)/ EF(%)(32-95/49-76) OO / XX function SUBSTR CAT LOWCASE SCAN CATS UPCASE LEFT CATT PROPCASE RIGHT CATX FIND TRIM TRANWRD COMPRESS STRIP LENGTH COMPBL 前處理 大小寫轉換 空白處理 符號 找出pattern位置 FIND 計算字串長度 LENGTH 讀取2位數的LVEF SUBSTR
字串處理的大絕招 使用單個字串來描述pattern、找出符合一系列句法規則的字串str。 正規表示式最初是由Unix中的工具軟體(grep)普及開的。 許多程式語言都支援利用正規表示式進行字串操作。 Perl Python R SAS 9
描述pattern和子字串 在報告中找出特定的pattern、並且取得數值子字串 EF by M-mode: XX % EF by 2-D method: XX% EF = XX% EF(%) = XX LV SV/ EF (32-95ml/49-76%) OO / XX LV SV(ml)/ EF(%)(32-95/49-76) OO / XX
Python套件re import re re.sub re.search NewStr = re.sub (X pattern, Y str, OrgStr) #在OrgStr中,找出符合X pattern的字串、換成Y字串,結果存到NewStr re.search ans = re.search (X pattern, str, re.IGNORECASE) #re.IGNORECASE 忽略大小寫 #在str中找出符合X pattern的字串、結果存在ans ans[0] = 完整pattern ans.groups(n)子字串 # n為數字、第n個子字串 re.split #使用pattern分割字串,結果存到Nlist NList = re.split(r'[-,~_]', Str) #67-72-65,60-68-63 → ["67", "72", "65", "60", "68", "63"]
語法 特殊字元 貪婪 重複特定次數 字元集合 跳脫字元 子字串
特殊字元 特殊字元 說明 pattern . 符合除了\n之外的任何單個字元 ^ 符合輸入字串的開始位置 $ 符合輸入字串的結束位置 * 符合前面的子運算式零次或多次 EF:.* EF:開始到\n為止 + 符合前面的子運算式一次或多次 EF:.+ :後致少要有一個\n以外的字元 ? 符合前面的子運算式零次或一次 :? :可有可無 X|Y X正規表示式或Y正規表示式 a|baa a或baa (a|b)aa aaa或baa
貪婪 Greedy 儘可能多 vs non-greedy 儘可能少 str = xyz <a> b <c> pqr * pattern <.*> ans <a> b <c> *? pattern <.*?> ans <a> + +? ? ??
次數{} 次數 {m} 符合前面的子運算式m次 a{6} aaaaaa {m, n} 符合前面的子運算式m次到n次 a{3, 5} aaa 非貪婪 a{3, 5}? str = aaaaaa
字元集合 [] 匹配字元集合 [:=] 字元匹配:或= [a-z] -前後有字元:表示從某字元到某字元 所有小寫字母 [a\-z] [-a] [a-] 可使用跳脫符號來表示- 放在最前面 放在最後面 a或-或z -或a a或- [^] 放在最前面:表示排除 [^a-z] 排除小寫字母a-z [0-5][0-9] 第一個字元0-5;第二個字元0-9 [0-5][0-9]:[0-5][0-9] 分:秒 [0-9A-Fa-f] 16進位
\跳脫符號 跳脫符號 \ 使用限制字元 \* * \? ? \. . \\ \d [0-9] \d+ \d+\.\d+ 整數 浮點數 數字 \D [^0-9] \s [ \t\n\r\f\v] 任何空白字元 \S [^ \t\n\r\f\v] 非空白字元 \w [a-zA-Z0-9_] 字母數字底線 \W [^a-zA-Z0-9_] 非字母數字底線
子字串 ans = re.search (pattern, str) ans[0] 整串 ans.group(#)=(子字串) (…) Pattern = 開始評估:(\d+/\d+/\d+) Str = 結束分析 開始分析:2018/08/29 開始評估:2018/08/28 ans[0] = 開始評估:2018/08/28 ans.group(1) = 2018/08/28 (?:…) 不取得匹配子字串 不放進group \d+(\.\d+)? \d+(?:\.\d+)?
字串前處理 str = REPORT01 str = re.sub (" +", " ", str) #消除多的空白 str = re.sub ("\s+", " ", str) #\n str = re.sub(" ?, ?", ",", str) #去除,前後空白 str = re.sub ("\( ?", "(", str) #尋找pattern要\( \) str = re.sub (" ?\)", ")", str) #替換字串不需\
數字落落長 symbol = “[-,~]” #定義各種數字間的符號 num = "\d+(?:\.\d+)?" # number = num + "(?:" + symbol + num + ")*“ number = "_?\d+(?:\.\d+)?_?(?:[-,~]_?\d+(?:\.\d+)?_?)*" number = "(_?\d+(?:\.\d+)?_?(?:[-,~]_?\d+(?:\.\d+)?_?)*)" #子字串 numberx = "(?:_?\d+(?:\.\d+)?_?(?:[-,~]_?\d+(?:\.\d+)?_?)*)" #不取子字串
實戰例:pattern EF by M-mode: 76 % M mode 39% M-mode = 44% M mode: 27_% M[- ]mode ?[:=]? ?number ?% → Pattern = "M[- ]mode ?[:=]? ?" + number + " ?%" 2D(M-Simpson) = 10 % 2D(Msimpson) : 24 % 2D (Simpson)= 64.6% 2D ?\(M?-?Simpson\) ?[:=]? ?number ?% → Pattern = "2D ?\(M?-?Simpson\) ?[:=]? ?" + number + " ?%" LV-SV/EF (32-95ml/49-76%) 99_ / 97 LV SV/ EF (32-95ml/49-76%) 100 /40 LV[ -]SV ?/ ?EF ?\(32-95 ?ml ?/ ?49-76 ?%\) ?numberx ?/ ?number Pattern = "LV[ -]SV ?/ ?EF ?\(32-95 ?ml ?/ ?49-76 ?%\) ?" + numberx + " ?/ ? " + number
程式架構 pattern input re LVEF output 維護pattern.xlsx 從Excel中取得pattern list 讀取CGRD原始檔 19.2G / 上百萬筆 ITEM = M22-060 保留心臟超音波(M22-060) 2010~2017年:共621744筆 re ans = re.search (pattern, report) #完整字串 = ans[0] 子字串 = ans.groups(1) LVEF 取得優先權最高的pattern為最終唯一的LVEF值 如有需要的話計算平均 output pattern字串 + 子字串 + 唯一的LVEF值 將所有無法判讀的report輸出到 no
Strength 機器已經打造完畢 只需要維護pattern
Strength
Limitation Key in資料錯誤 字串在奇妙的地方換行
其他應用 CGRD心內科報告 MR : LA(mm) : Trello Export Excel ptt網軍帶風向辨識 網路爬虫+正規表示式
reference Python re - Regular expression operations https://docs.python.org/3/library/re.html An Introduction to Perl Regular Expressions in SAS 9 http://www2.sas.com/proceedings/sugi29/265-29.pdf
demo Pattern LVEF MR : LA(mm) :
Q & A
FAQ 程式最難處理的部份 要如何找出pattern 各醫師複雜的數字記錄方式才是最麻煩的地方 可以請醫師或是PI提供pattern 自己找會花比較多時間、甚致跟本不知道要找什麼 克里斯多夫只需要希特勒萬歲就可以打敗恩尼格瑪