2017年1月24日火曜日

立ちはだかる文書収集の困難さ

予想していたことではあるが,疾患と症状の関係を抽出するための文書を収集することは大変な作業です。ネットの文書を集めることには次のような問題があります。
  • 信頼性
  • 画像や広告を除去して文書のみを抽出する難しさ
  • 関係のない部分の除去(例えば治療法とか原因とか)
では,医学書や教科書から収集するのはどうか。これにも次のような問題があります。
  • どうやって電子媒体にするのか?スキャン?どうしても誤スキャンは避けられない。それをどうやってチェックするのか?
  • 十分な文書を確保できるか?
どっちもどっちです。信頼性を確保しつつ大量の文書を収集するにはどうしたらよいのでしょうか?思いつくのは医中誌などの論文検索サービスを使って抄録付きの論文を検索してダウンロードすることくらいです。学術論文だからネットに比べると質は保てるでしょう。しかし,だからと言って目的の文書ばかりが検索されるとは限りません。また,学術雑誌なのだから疾患に関係する症状を抽出するには不向きな場合もあるでしょう。でも,現時点ではこれ以外に効率の良い方法は見当たりません。試しに子宮内膜症をキーワードにして抄録ありの原著論文と症例報告を検索すると1544件ヒットしました。同様に子宮筋腫で検索すると2000を超える文献がヒットしました。文書量としては十分のように思います。とりあえずここから始めていきましょう。
これは試しに検索してみた結果の1例です。

AB:子宮内膜症は良性のエストロゲン依存性疾患であり閉経後の発生頻度は低いと報告されている。しかし、今回、閉経後33年で偶発的に発見された子宮内膜症性嚢胞の一例を経験した。症例は88歳4経妊4経産。閉経55歳。意識消失発作の原因検索で施行した造影CTで10cm大の卵巣腫瘍を指摘され、当科紹介となった。経腟超音波、造影CT、MRIのいずれでも腫瘍壁に不整や充実性部分はなかった。CA125 145.5U/mlと腫瘍マーカーの上昇を認めており、年齢、腫瘍の大きさ、腫瘍マーカー高値から卵巣原発の悪性腫瘍を否定できず、腹式両側付属器摘出術と術中迅速病理診断を施行する方針とした。術中所見では腹水を認めず、右卵巣は長径10cmに腫大しており、表面平滑・多房性の腫瘤であった。左側の卵巣は萎縮していた。また、骨盤腔内に内膜症性腹膜病変を認めなかった。右付属器の術中迅速病理診断にて良性と診断され、両側付属器摘出のみで終了とした。最終病理結果でも子宮内膜症性嚢胞の診断であった。本症例は、閉経後非常に長期間経過しているにも関わらず発見された、稀有な卵巣子宮内膜症性嚢胞であり、今後さらなる症例の蓄積が必要である。(著者抄録)

AB:で始まる抄録部分のみを抜粋しました。症例報告ですね。医療者の立場から書かれた医学文書です。難解な専門用語や医学概念が多いため,ここから抽出した用語が使えるかどうか,甚だ心もとない感じです。



2017年1月18日水曜日

論文紹介を行っていきます

次週から論文を持ち回りで紹介することが決まりました。
以下参考ページ
LIFE SCIENCE DICTIONARY-活動実績

国立国語研究所-過去の研究成果

対応分析第二回目の結果

LIFE SCIENCE DICTIONARY

医学用語の選択に見られる特徴から見てLIFE SCIENCE DICTIONARYを使用して対応分析をした。その結果を下の図に示す。
頻度3以上

考察
専門用語が多く、スマホアプリで疾患を区別するのは難しいと思う。軸の解釈が不明。

前回のコーパスにデータを増やした場合

前回のコーパスに新たにデータを増やし、対応分析を再び行った。その結果を下の図に示す。

頻度2以上

頻度3以上
考察
どちらも軸の解釈が不明であり、読み取ることが難しい。
卵巣腫瘍について関係する語が極端に少ない。


2017年1月17日火曜日

対応分析による疾患と症状の関係分析

疾患と症状の関連性を調べるために対応分析を行ってみました。方法はこうです。まず,ネットから各疾患の症状を説明した文書を複数集め,それらをマージして疾患ラベルを付けました。これを疾患コーパスと呼ぶことにしましょう(下図)。今回は子宮筋腫,子宮内膜症,子宮頸がん,子宮体がん,卵巣腫瘍の5つの疾患をとりあげました。
<H1>子宮筋腫</H1>
子宮筋腫は良性の腫瘍ですから、それ自体が生命を脅かすものではありません。
しかし放置しておきますと10kgを超えるような大きさまでになることもあります。女性ホルモンによって筋腫が大きくなりますが、逆に閉経後には小さくなります。複数個できることが多く、数や大きさはさまざまです。
大きさやできた場所によって症状が違ってきます。できた場所によって、子宮の内側(粘膜下筋腫)、子宮の筋肉の中(筋層内筋腫)、子宮の外側(漿膜下筋腫)に分けられています。
代表的な症状は月経量が多くなることと月経痛です。その他の症状としては月経以外の出血、腰痛、頻尿(トイレが近い)等があります。症状は、できた場所によってまちまちですが、子宮の内側にできた筋腫は小さくても症状が強く、月経量が多くなります。逆に子宮の外側にできた筋腫は相当大きくなっても症状がでません。ですから、治療法もできた場所や症状によって異なってきます。若い人では妊娠しにくくなったり、流産しやすくなったりするのも大きな問題です。

<H1>子宮筋腫</H1>
子宮内膜またはそれに似た組織が何らかの原因で、本来あるべき子宮の内側以外の場所で発生し発育する疾患が子宮内膜症です。20~30代の女性で発症することが多く、そのピークは30~34歳にあるといわれています。
子宮内膜症は女性ホルモンの影響で月経周期に合わせて増殖し、月経時の血液が排出されずにプールされたり、周囲の組織と癒着をおこしてさまざまな痛みをもたらしたりします。また、不妊症の原因にもなります。
代表的なものは「痛み」と「不妊」です。痛みの中でも月経痛は子宮内膜症の患者さんの約90%にみられます。この他、月経時以外にも腰痛や下腹痛、排便痛、性交痛などがみられます。こうした症状は20~30歳代の女性に多く発症し、加齢による女性ホルモン分泌の減少を境におさまります。また、妊娠を希望する生殖年齢の女性では「不妊」が問題となります。妊娠の希望のある内膜症患者さんの約30%に不妊があると考えられています。

<H1>子宮頸がん</H1>
子宮下部の管状の部分を子宮頸部、子宮上部の袋状の部分を子宮体部と呼び、それぞれの部位に生じるがんを子宮頸がん、子宮体がんといいます。
子宮頸がんは子宮がんのうち約7割程度を占めます。以前は発症のピークが40~50歳代でしたが、最近は20~30歳代の若い女性にも増えてきており、30歳代後半がピークとなっています。
子宮頸がんは通常、早期にはほとんど自覚症状がありませんが進行するに従って異常なおりもの、月経以外の出血(不正出血)、性行為の際の出血、下腹部の痛みなどが現れてきます。

<H1>子宮体がん</H1>
子宮は妊娠した時に胎児を育てる部分と分娩の時に産道の一部となる部分に分けることができ、それぞれを子宮体部、子宮頸部といいます。子宮体部に発生するがんが子宮体がんで、最近我が国の成人女性に増えてきているがんのひとつです。そのほとんどは、子宮体部の内側にあり卵巣から分泌される卵胞ホルモンの作用をうけて月経をおこす子宮内膜という組織から発生し、子宮内膜がんとも呼ばれています。
一番多い自覚症状は不正出血です。子宮頸がんに比べ、子宮体がんになる年代は比較的高齢ですから、閉経後あるいは更年期での不正出血がある時には特に注意が必要です。閉経前であっても、月経不順、乳がんを患ったことがあるなどということがあればやはり注意が必要です。

<H1>卵巣腫瘍</H1>
卵巣は子宮の左右に一つずつあり、通常では2~3cmぐらいの大きさです。ここに発生した腫瘍が卵巣腫瘍であり、大きいものでは30cmを超えることもあります。卵巣腫瘍には様々な種類がありますが、その発生起源から表層上皮性・間質性腫瘍、性索間質性腫瘍、胚細胞腫瘍などに大別され、それぞれに、良性腫瘍、境界悪性腫瘍、悪性腫瘍があります。
卵巣腫瘍の症状には腹部膨満感(お腹が張って苦しい)、下腹部痛、頻尿などがありますが、小さいうちは無症状で経過することが多く、大きくなったり腹水がたまったりしてから症状が出現することが多いのです。時に腫瘍が破裂したり、茎捻転といって腫瘍がお腹の中でねじれてしまうと突然の強い下腹部痛が出現することもあります。

次にこのコーパスをKH-Coderに読み込みました。その結果,コーパスは全部で42文,14段落,H1ラベル(疾患)を付けた文書が5つあることがわかりました。次に文書から用語を抽出するために[前処理]→[複合語の検出]→[TermExtractを利用]を選択しました。これは,形態素解析だけだと語が分割され過ぎて,例えば「子宮内膜症」が「子宮」「内」「膜」「症」に分割され意味のない語になってしまうため,互いに関連する語はくっつけるためです。
 これによって,子宮頸がん,子宮内膜症,子宮体がん,子宮体部,子宮内膜,卵巣腫瘍,子宮頸部,子宮筋腫など,全部で50個の関連語が抽出されました。これをファイルに保存して,[前処理]→[語の取捨選択]を選び,[強制的に抽出する語の設定]の[ファイルから読み込み]のファイルに設定しました。その後,[前処理]→[前処理の実行]を行いました。これで準備が整ったのでいよいよ対応分析です。
対応分析は,[ツール]→[抽出語]→[対応分析]を選択します。オプション画面が表示されますが,とりあえずデフォルトで実行しました。その結果が下の図です。
疾患と症状の関連性を対応分析した図

図から子宮内膜症は不妊,女性ホルモン,痛み,腰痛,月経痛といった症状との関連性が認められました。同様に子宮筋腫は筋腫,月経量といった用語,卵巣腫瘍は主要,下腹部痛など,そして,子宮がんと子宮体がんは不正出血,子宮体部,子宮頸部といった語との関連性が示されました。
また,図の縦軸については下へいくほど腫瘍やがんといった新生物に関連する疾患が付置される傾向が見られものの,横軸については明確な傾向が見られません。 そこで分析に用いる語の出現頻度が全体を通じて3以上のものに限定すると次のような結果が得られました。
分析に使用する語の出現頻度を3以上に設定したもの
すると先ほどに比べて横軸の特徴が少し見えてきました。どうやら,左へいくほど大きい,小さい,多いといった形容詞が見られるようです。これはいったい何を意味しているのでしょうか?卵巣腫瘍や子宮筋腫など左側に付置される疾患は形容詞を用いて説明されることが多いということでしょうか。それに対して子宮内膜症や子宮頸がん,子宮体がんは形容詞ではなく名詞をつかって説明されることが多いということでしょうか。しかし,これはあまり本質的なこととは思われません。そもそも,症状を疾患と関連づけようとこの分析を始めたのですから,形容詞には用はありません。これらの問題はコーパスに起因するものと思われます,もっとたくさん文書を集めないと疾患と症状の関連性は見つけられないということでしょうか。
 今回分析対象とした5つの疾患はどれも似たような疾患なので用語で特徴づけるのが難しいのかもしれません。全く種類の違う疾患を対象にした場合はもっと明確な違いを見出せるのではないかと感じています。

課題と対策

  1. 疾患が十分に分離できていない(子宮頸がんと子宮体がん)
  2. 被修飾語がない(例:大きい,小さい)
  3. (症状とは)関係のない語が抽出されている(例:分ける,それぞれ,最近,呼ぶ,・・・)
  4. 症状が抽出されていない
  5. 疾患名がそのまま用語として出ている

 1と4についてはコーパスを充実させる。2は何らかの方法で修飾語と被修飾語を連結させる。3については強制的に除去する用語をファイルにする。5は4と同様に除去する。

また,対象とする疾患をどれとどれにするかについての検討も行う。さらに,どのくらいコーパスを準備すればよいのかを評価する基準についても検討を行う。

2017年1月8日日曜日

HAPI FHIR

HL7 PHIRのResourceを登録するテスト用サーバ(Repository)が無償で提供されています。それが HAPI PHIRです。HAPI FHIRの目的は,FHIRの啓蒙と普及だと思います。このサーバを利用すればRESTful APIを用いてPHIRの様々なREsourceを登録,参照,更新,削除を行うことができます。これからFHIRでアプリケーションを作ってみようと考えている人々にとって貴重なテストベッドになります。なぜならPHIRの仕様を自ら実装するには敷居が高すぎるからです。

では,早速,HAPI FHIR Serverを使って簡単なスマホアプリを作ってみましょう。扱うのResourceはPatientです。初画面でHAPI FHIRに登録されているPatientの一覧を表示し,タップすると選択されたPatientの内容を表示するアプリです。

スクリーンショット

Patient一覧
Patient詳細画面

上のPatient一覧画面から患者をタップすると下の詳細画面が表示されます。

index.html

まず,index.htmlはons-navigatorを使ってPatient一覧画面(page1.html)を表示します。

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <script src="components/loader.js"></script>
    <script src="js/app.js"></script>
    <link rel="stylesheet" href="components/loader.css">
    <link rel="stylesheet" href="css/style.css">
    <script>
        ons.bootstrap();
    </script>
</head>
<body>
    <ons-navigator var="myNavigator" page="page1.html"></ons-navigator> 
</body>
</html>

page1.html

page1.htmlにはons-listコンポーネントを用意し,プログラムでこの中に患者一覧を挿入します。

<ons-page id="page1">
    <ons-toolbar>
        <div class="center">HAPI FHIR Patients</div>
    </ons-toolbar>
    <br>
    <div>
        <ons-list id="patient-list">
            
        </ons-list>
    </div>
</ons-page>

page2.html

page2.htmlは患者の詳細画面です。divタグの中にプログラムで患者の詳細情報を挿入します。

<ons-page id="page2">
    <ons-toolbar>
        <div class="left"><ons-back-button>Back</ons-back-button></div>
        <div class="center">Hapi FHIR Patient</div>
    </ons-toolbar>

    <div id="patient-info">
    </div>
</ons-page>

app.js

page1が表示されたとき,FHIRで患者検索を行い,その結果を編集します。結果の編集はlistPatients関数で行います。なお,検索結果はJSON形式です。 listPatients関数ではhtmlを編集したのち,患者クリック時のイベントプロシージャを登録します。登録はセレクタ$('.patient', '#page1')に対して行います。'#page1'のところはthisを使いたいところですが,使えません。なぜならプログラムでons-list-item要素を動的に作成しているため,それにつけたclass="patient"はthisとは紐づけられていないからです(たぶん)。本来,ここでのthisと#page1は同じものを指しているはずですが,そうとは解釈されないようです。
患者がタップされたら患者IDをpage2へ渡して画面遷移を行います。page2が表示されるタイミングでその患者の情報をFHIR Serverに取りに行き,詳細情報を表示します。ここでは,resource.text.divをそのまま出力しています。

$(document).on('pageinit', '#page1', function(){
    var url = 'http://fhirtest.uhn.ca/baseDstu2/Patient';
    $.ajax({
        url: url,
        type: 'GET',
        data: {
            _pretty: true,
        }
    })
    .done(function(resource) {
        listPatients(resource);
    })
    .fail(function(XMLHttpRequest, textStatus, errorThrown) {
        var html = '';
        html += '<ons-row>' + 'XMLHttpRequest:' + XMLHttpRequest.status + '</ons-row>';
        html += '<ons-row>' + 'textStatus:' + textStatus + '</ons-row>';
        html += '<ons-row>' + 'errorThrown:' + errorThrown + '</ons-row>';
        $("#patient-list").html(html);
        var content = $("#patient-list").get(0);
        ons.compile(content);
    });
});

$(document).on('pageinit', '#page2', function(){
    var page = myNavigator.getCurrentPage();
    var patientId = page.options.patientId;
    var url = 'http://fhirtest.uhn.ca/baseDstu2/Patient/' + patientId;
    $.ajax({
        url: url,
        type: 'GET',
    })
    .done(function(resource) {
        $("#patient-info").html(resource.text.div);
    })
    .fail(function(XMLHttpRequest, textStatus, errorThrown) {
        var html = '';
        html += '<ons-row>' + 'XMLHttpRequest:' + XMLHttpRequest.status + '</ons-row>';
        html += '<ons-row>' + 'textStatus:' + textStatus + '</ons-row>';
        html += '<ons-row>' + 'errorThrown:' + errorThrown + '</ons-row>';
        $("#patient-list").html(html);
        var content = $("#patient-list").get(0);
        ons.compile(content);
    });
});

function listPatients(resource) {
    var html = '';
    $.each(resource.entry, function(index, entry){
        html += '<ons-list-item modifier="chevron" class="patient">';
        html += '<ons-row>';
        html += '<ons-col class="patient-id">' + entry.resource.id + '</ons-col>';
        html += '<ons-col>' + entry.resource.name[0].family[0] + ' ' + entry.resource.name[0].given[0] + '</ons-col>';
        html += '</ons-row>';
        html += '</ons-list-item>';
    });
    $("#patient-list").html(html);
    var content = $("#patient-list").get(0);
    ons.compile(content);
    
    $('.patient', '#page1').on('click', function(){
        var options = {
            patientId: $('.patient-id', this).text()
        };
        myNavigator.pushPage('page2.html', options);
    });
}

なお,Ajaxの成功時及び失敗時の処理の書き方は.done~,.fail~が正しいらしい。詳しくは「jQuerryのDefferredを用いたモダンなAjaxの書き方」を参照。

2017年1月7日土曜日

日本語形態素解析

新しい年を迎え,今年最初のテーマは日本語形態素解析です。プログラムは前にやったキーフレーズ抽出とほとんど同じなので,それをベースにして必要な修正を行っていくのが最も簡単でしょう。まず,index.htmlはそのまま使えるので何も変更はありません。page1.htmlもほとんど同じですが,タイトルを変えることとボタンのラベルとidを変更することくらいです。

page1.html

<ons-page id="page1">
    <ons-toolbar>
        <div class="center">日本語形態素解析</div>
    </ons-toolbar>
    <br>
    <div>
        <textarea class="textarea" id="sentence" style="width: 100%; height: 300px">
        </textarea>
    </div>
    <div>
        <ons-button modifier="large--cta" id="parse-button">解析</ons-button>
    </div>
</ons-page>

次に,page2.htmlですが,タイトルを修正し,形態素解析の結果を出力するdivタグにword-listというidを割り当て,divタグ内のons-itemタグは削除します。形態素解析の結果は1行に複数の項目(形態素,読み仮名,品詞,基本形)を表示するのでons-rowとons-colを組み合わせてグリッドレイアウトにするからです。

page2.html

<ons-page id="page2">
    <ons-toolbar>
        <div class="left"><ons-back-button>Back</ons-back-button></div>
        <div class="center">日本語形態素解析</div>
    </ons-toolbar>

    <div id="word-list">
    </div>
</ons-page>

最後は。app.jsです。これも基本的なプログラム構造はキーフレーズ抽出の時と同じです。

app.js

var gAPPID = '[APPID]';

$(document).on('pageinit', '#page1', function(){
    $('#sentence').val('基本的な症状は「痛み」です。 内膜症のある場所、大きさ、癒着の程度などによって症状はさまざまですが、主に生理痛や下腹部痛、腰痛、性交痛、排便痛、慢性骨盤痛などが現れます。 痛み以外では、不正出血が見られたり、経血量が多かったり、レバー状の塊が出ることもあります。');
});

$(document).on('click', '#parse-button', function(){
    var options = {
        sentence: $('#sentence').val()
    };
    myNavigator.pushPage('page2.html', options);
});

$(document).on('pageinit', '#page2', function(){
    var page = myNavigator.getCurrentPage();
    var sentence = page.options.sentence;
    
    var url = 'http://jlp.yahooapis.jp/MAService/V1/parse';
    $.ajax({
        url: url,
        type: 'POST',
        data: {
            appid: gAPPID,
            sentence: sentence,
            response: 'surface,reading,pos,baseform'
        }
    })
    .done(function(xml) {
        listWords(xml);
    })
    .fail(function(XMLHttpRequest, textStatus, errorThrown) {
        //alert("error:" + textStatus);
        var html = '';
        html += '<ons-row>' + 'XMLHttpRequest:' + XMLHttpRequest.status + '</ons-row>';
        html += '<ons-row>' + 'textStatus:' + textStatus + '</ons-row>';
        html += '<ons-row>' + 'errorThrown:' + errorThrown + '</ons-row>';
        $("#word-list").html(html);
        var content = $("#word-list").get(0);
        ons.compile(content);
    });
});

function listWords(xml) {
    var html = '';
    $(xml).find('word').each(function(){
        var surface = $(this).find('surface').text();
        var reading = $(this).find('reading').text();
        var pos = $(this).find('pos').text();
        var baseform = $(this).find('baseform').text();
        html += '<ons-row>';
        html += '<ons-col>' + surface + '</ons-col>';
        html += '<ons-col>' + reading + '</ons-col>';
        html += '<ons-col>' + pos + '</ons-col>';
        html += '<ons-col>' + baseform + '</ons-col>';
        html += '</ons-row>';
    });
    $("#word-list").html(html);
    var content = $("#word-list").get(0);
    ons.compile(content);
}

25行目のresponseには結果にどのような形態素情報を含めるかを設定します。注意すべきことはカンマで区切る際,間に空白を入れないようにすることです。空白を入れるとエラーになります。
今回,Ajaxのエラーハンドラでエラー詳細を画面に出力するようにしました。