2016年12月22日木曜日

対応分析

昨夜からずっと対応分析について考えていましたがどうにもよくわかりません。クラスター分析はサンプル間の「距離」を計算して類似度を求め,クラスターに分割する手法なので直感的にわかりやすいのですが。
そもそも対応分析の結果に示される座標系はいったい何なんでしょう?そこからすでにわかりません。そこで,いろいろなサイトを調べてみました。わかりやすいかどうかは別にして,参考になりそうなものをいくつかリストします。
  1. コレスポンデンス分析とは 
  2. コレスポンデンス分析/カテゴリー間の関係を視覚化する分析
  3. 対応分析法・数量化法III類の考え方
  4. 対応分析入門 原理から応用まで 解説◆Rで検算しながら理解する
  5. 【連載】記述的多変量解析 - 対応分析(コレスポンデンス・アナリシス) 
これらを読んでいると,どうやら対応分析は主成分分析に似た分析手法のようです。主成分分析の場合は量的データ(四則演算可能なデータ)を扱いますが,対応分析の場合はカテゴリカルなデータを扱うとのことです。いまいちよくわかりません。

考え方が間違っていました

KH Coderについて書いた昨日のブログは間違っていました。昨日は,単語がサンプルで,その特徴量が文だと書きましたが,これは反対ではないでしょうか。つまり,文がサンプルで,特徴量が単語です。単語はその分を特徴づけているわけですからこの考え方は自然です。たとえば野球について書かれた文の中にはピッチャーやストライク,ホームランといった単語が出てくるでしょう。それに対してサッカーについて書かれた文にはペナルティキックやロスタイム,ドリブルといった単語が出てくるでしょう。それらの単語が文(の意味やテーマ)を特徴づけているのですから。
私たちの研究で考えるなら,サンプルは疾患について書かれた文(以降,これを疾患定義文と呼ぶことにします)で,特徴量は症状です。つまり,疾患定義文は症状によって特徴づけられているわけです。特徴ベクトルの要素として,疾患定義文に対応する症状が登場するかしないかでそれぞれ1, 0の値をとるような症状ベクトルを考えると,各疾患は症状ベクトルによって特徴づけられ,それらが近接している場合は類似した疾患と分類されるでしょう。これはまさに理にかなっています。おそらくクラスター分析もこのような考え方で行っているのではないでしょうか。
ところで,各々の疾患定義文が含む症状の数はまちまちです。ですから,文の長さによる違いを除去するために何らかの方法で正規化する必要があるかもしれません。また,同じ症状が複数回登場することもあるでしょう。そのような症状はその疾患について重要な特徴であることを示しています。さらに,登場回数は少なくてもすべての疾患定義文における登場回数が少ない症状は,その疾患の特徴を表す重要な症状と言えるでしょう。ですからこれらも考慮に入れるべきです。そう考えると,症状の有無や登場回数だけで特徴ベクトルを表現する方法は好ましくありません。そこで,特徴ベクトルの各要素にtfidfを利用することが考えられます。tfはterm frequencyのことで,ある一つの疾患定義文に登場する症状tの出現回数です。そして,dfはdocument frequencyのことで,これは疾患定義文全体に登場するtの出現回数です。tfidfのiはinverseのことで,tfをdfで割ることを意味しています。こうして先に述べた問題点を解消した特徴ベクトルを作ることができます。

2016年12月21日水曜日

KH Coder

K君が実に興味深いフリーソフトを探してきてくれました。KH Coderです。これは,テキストデータを統計的に分析するフリーソフトウェアで,いわゆるテキストマイニングって奴ですね。これを使うと今やっている研究にかなり役立ちそうです。しかし,ざっとサイトを眺めただけでは使い方や結果の解釈の仕方がよくわかりません。そこで,このサイトで紹介されている参考書「社会調査のための計量テキスト分析」を購入することにしました。KH Coderのリファレンスマニュアルも掲載されているということなので役立ちそうです。
もっとも興味深いのは,「Rによる多変量解析と可視化」です。 ここでは,「階層的クラスター分析」をはじめ,様々な解析手法が紹介されており,とても興味深いです。ただし,このサイトからだけではそれぞれの解析手法がどのような仕組みで作られているかや,得られた結果をどのように見ていけばよいのかがわかりません。これについては注文した本が届くのを待つしかなさそうです。

クラスター分析

クラスター分析について解説されたこのサイトを見ると,サンプル間の距離を求めてそれを使ってクラスターに分けているようです。では,距離とは何か?
このサイトには「都道府県の特徴データ」を使った距離の定義方法が紹介されています。いろいろなやり方があるみたいですね。ここで重要なのは「特徴量」ベクトルです。「都道府県・・・」の場合は人口や降雪量,漁獲量,・・・といったデータを特徴量に使っていました。では,テキストマイニングの場合はどのように考えたらよいのでしょうか?
まず,サンプルとは何でしょうか?おそらくテキストから抽出した単語でしょうね。KH Coderの「抽出語・クラスター分析」を見ると単語をクラスターに分けていることがわかります。では,そのサンプル(つまり単語)の「特徴量」とは何でしょうか?
これについては明確な記載がありません。これは想像ですが,文(センテンス)が特徴量ではないでしょうか?例えば,解析対象の文がN個あるとして,それをD1, D2, ..., DNとします。そして,これらの文から抽出したある単語Wiがそれらの文に含まれるか(1)含まれないか(0)を要素とするベクトル(1, 0, ..., 1)を考えます。こうして作られた「特徴量」ベクトルが類似していると(たとえば頂点間のユークリッド距離やベクトルの内積,あるいは余弦などを使って類似度を計算),サンプル間の距離は小さいと判断するなどです。
この考え方があっているかどうかはわかりませんが,いずれにせよ,テキストから抽出した単語が個々のサンプルであることには間違いないでしょう。そして,各サンプルには何らかの特徴量が付随しているはずです。わたしは,それを単語を抽出した文(センテンス)と考えました。
では,なぜ文(正確には文に含まれる・含まれないという変量)が特徴量なのでしょう?
個々の文に個性がなければ特徴量とは言えません。しかし,もし,個々の文が何らかのカテゴリを持っていたら?例えば,この文は「子宮筋腫」について説明した文書,この文は「子宮内膜症」について説明した文,・・・といった具合に。そうすれば,ある単語がそれらの文(=カテゴリ)に登場するか否かを要素とするベクトルは立派な特徴量ベクトルと言えるでしょう。2つの単語(サンプル)の距離が近いということは疾患を説明する単語として類似している語と言えるのではないでしょうか。この方法で分析された単語は,疾患を説明する単語という観点から分析されていることになります。
例えばクラスター分析であれば,同一クラスターに分類された単語は疾患を説明する上で近い概念を表していると言えるかもしれません。共起ネットワークであれば,同じ疾患の説明文書に共起することが多い単語がネットワークで結ばれるでしょう。

今日のゼミで決まったこと

今日のゼミでは疾患と症状の区別がつきにくいということで,まず,みんなで手分けして疾患リストを作成しようということになりました。婦人病に関する疾患を紹介しているサイトや病院や診療所のサイトから婦人科疾患を収集します。その後,収集した疾患をマージし,最終的に今回の研究の対象となる疾患を絞ります。

2016年12月20日火曜日

テキスト解析

Yahooデベロッパーネットワークというサイトにテキスト解析というのがあります。今日はその中から「キーフレーズ抽出」というWebAPIを使ってアプリを作ってみます。この「キーフレーズ解析」というのは,「日本語文を解析し、特徴的な表現(キーフレーズ)を抽出」するということです。たとえば次の分からキーフレーズを抽出してみます。
基本的な症状は「痛み」です。 内膜症のある場所、大きさ、癒着の程度などによって症状はさまざまですが、主に生理痛や下腹部痛、腰痛、性交痛、排便痛、慢性骨盤痛などが現れます。 痛み以外では、不正出血が見られたり、経血量が多かったり、レバー状の塊が出ることもあります。
その結果,このWebAPIは,次のようなXML形式でキーフレーズを返してきます。
<?xml version="1.0" encoding="UTF-8"?>
<ResultSet xmlns="urn:yahoo:jp:jlp:KeyphraseService" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:yahoo:jp:jlp:KeyphraseService http://jlp.yahooapis.jp/KeyphraseService/V1/extract.xsd">
  <Result>
    <Keyphrase>慢性骨盤痛</Keyphrase>
    <Score>100</Score>
  </Result>
  <Result>
    <Keyphrase>下腹部痛</Keyphrase>
    <Score>99</Score>
  </Result>
  <Result>
    <Keyphrase>症状</Keyphrase>
    <Score>98</Score>
  </Result>
  <Result>
    <Keyphrase>生理痛</Keyphrase>
    <Score>97</Score>
  </Result>
  <Result>
    <Keyphrase>不正出血</Keyphrase>
    <Score>95</Score>
  </Result>
  <Result>
    <Keyphrase>内膜症</Keyphrase>
    <Score>95</Score>
  </Result>
  <Result>
    <Keyphrase>性交痛</Keyphrase>
    <Score>93</Score>
  </Result>
  <Result>
    <Keyphrase>排便痛</Keyphrase>
    <Score>91</Score>
  </Result>
  <Result>
    <Keyphrase>経血量</Keyphrase>
    <Score>89</Score>
  </Result>
  <Result>
    <Keyphrase>癒着</Keyphrase>
    <Score>89</Score>
  </Result>
  <Result>
    <Keyphrase>レバー状</Keyphrase>
    <Score>88</Score>
  </Result>
  <Result>
    <Keyphrase>腰痛</Keyphrase>
    <Score>64</Score>
  </Result>
  <Result>
    <Keyphrase>塊</Keyphrase>
    <Score>50</Score>
  </Result>
  <Result>
    <Keyphrase>程度</Keyphrase>
    <Score>47</Score>
  </Result>
  <Result>
    <Keyphrase>以外</Keyphrase>
    <Score>43</Score>
  </Result>
  <Result>
    <Keyphrase>場所</Keyphrase>
    <Score>39</Score>
  </Result>
</ResultSet>
作成するアプリのスクリーンショットは以下のようになります。
初画面(id="page1")文章を入力する画面

キーフレーズを抽出して一覧表示する画面(id="page2")
では,アプリを作成しましょう。 そのためには,まず,このWebAPIを使うためにはアプリケーションIDを取得する必要があります。ここから登録することができます。
メインとなるindex.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は次のようになります。
<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="keyphrase-button">キーフレーズ抽出</ons-button>
    </div>
</ons-page>
キーフレーズ一覧画面は次のようになります。
<ons-page id="page2">
    <ons-toolbar>
        <div class="left"><ons-back-button>Back</ons-back-button></div>
        <div class="center">キーフレーズ</div>
    </ons-toolbar>

    <div style="text-align: center">
        <ons-list id="keyphrase-list">
        </ons-list>
    </div>
</ons-page>
最後にapp.jsです。
var gAPPID = 'ここにAPPIDを書く';

$(document).on('pageinit', '#page1', function(){
    $('#sentence').val('基本的な症状は「痛み」です。 内膜症のある場所、大きさ、癒着の程度などによって症状はさまざまですが、主に生理痛や下腹部痛、腰痛、性交痛、排便痛、慢性骨盤痛などが現れます。 痛み以外では、不正出血が見られたり、経血量が多かったり、レバー状の塊が出ることもあります。');
});
    
$(document).on('click', '#keyphrase-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/KeyphraseService/V1/extract';
    $.ajax({
        url: url,
        type: 'POST',
        data: {
            appid: gAPPID,
            sentence: sentence
        }
    }).done(function(xml) {
        listKeyphrase(xml);
    }).fail(function(XMLHttpRequest, textStatus, errorThrown) {
        alert("error");
    });
});

function listKeyphrase(xml) {
    var html = '';
    $(xml).find('Result').each(function(){
        var Keyphrase = $(this).find('Keyphrase').text();
        console.log(Keyphrase);
        html += '<ons-list-item modifier="chevron" class="keyphrase">' + Keyphrase + '</ons-list-item>';
    });
    $("#keyphrase-list").html(html);
    var content = $("#keyphrase-list").get(0);
    ons.compile(content);
}

2016年12月14日水曜日

ゼミのテーマが決まりました

ゼミのテーマが決まりました。症状から疾患を推定するアプリの開発です。手法としてはナイーブベイズ分類器を用います。仮題を次のようにしておきましょう。
ナイーブベイズ分類器を用いた症状から疾患を推定するアプリの開発と評価
まず予備実験をやってこの研究を前に進めてよいかどうかを判断することとします。そのために以下の材料を集めます。
  1. 推定対象となる疾患
  2. 疾患定義データ(疾患を定義する文書を各疾患について10個ずつくらい,疾患の数だけ集める)
  3. 症状を集めた医学辞書
  4. 形態素解析ツール
上記1についてはすでに64疾患が挙がっています。上記2は,疾患を定義したテキストデータです。上記3はテキストデータに含まれる症状を抽出するための辞書です。上記4は,テキストデータを形態素解析するためのツールです。「茶筌」を考えています。

2016年12月13日火曜日

症状から病名を診断すること

症状から受診すべき診療科を決めることはどのくらい難しいのだろうか。今回の卒業研究報告会に人工知能を使ってそれを実現するという内容の発表があった。しかし,どこで人工知能を使っているのか,また,その人工知能の手法(機械学習のアルゴリズムなど)については何の説明もなかった。ただ,IBMのWatosonを使っているとだけ言っていた。発表だけからは詳しいことはわからないが,①医学書を用いて各々の疾患にはどのような症状が出るかを調べた,②病院のホームページを調べて診療科リストとそれぞれの診療科で扱う疾患を調べた,③そして患者の症状から①によって疾患を求め,②によって診療科を決定するという内容のように理解した。しかし,症状から疾患は一意に決まるのだろうか。仮に一意でない場合,どうやって疾患を絞り込むのだろう。たとえば「発熱」は多くの疾患の症状として現れるだろう。また,診療科によって扱う疾患は決まるのだろうか。同じ疾患であっても外科的なアプローチと内科的なアプローチはあるだろう。

この手法の問題点は,症状→疾患→診療科という三段論法で診療科が決まるというあまりに非現実的で安易なモデル設定にある。現実には,症状から疾患を特定することはできない。できることは候補を確率をつけてリストアップすることくらいだろう。今はやりの人工知能がやっているのもそれに過ぎない。どんなに情報を集めても確定診断などできるはずがない。

では,どうやって症状から確率付きで疾患を割り出せばよいのか。それこそがWatsonなどの人工知能がやっていることである。人工知能は客観的な事実を収集し,得られた情報から結論を確率的に導く。Watsonは深層学習というものを使っているらしい。

学習アルゴリズムには数々の手法が提案されているが,もっとも直感的で実装が簡単な割にそこそこの精度があるナイーブベイズという手法がある。これは収集した情報から事前確率を計算し,手元にある事実から事後確率(つまり,条件付確率)を計算し,それを使って意思決定を行うというものである。

ナイーブベイズの理論的な土台はベイズの定理である。ベイズの定理は次のように定式化される。

$ P(Y|X)=\frac{P(Y)P(X|Y)}{P(X)} $

これは,事象$X$,$Y$が起きる確率をそれぞれ$P(X)$,$P(Y)$とし,事象$Y$のもとで事象$X$が起きる確率(これを尤度という)を$P(X|Y)$とした場合,事象$X$のもとで事象$Y$が起きる確率$P(Y|X)$を求める公式である。たとえば,$X$を症状,$Y$を疾患と考えた場合,この式は罹患率$P(Y)$と疾患$Y$を発症したときに症状$X$が現れる確率$P(X|Y)$から,症状$X$が出たときに疾患$Y$である確率$P(Y|X)$を求める計算式となる。

これは,見方を変えれば,事前確率から事後確率を求める計算式でもある。事前確率は,何も情報を持ち合わせていないときの確率で,ベイズの定理は,入手した事実によって事前確率を更新し,事後確率を得るという枠組みを示している。

つまり,患者の症状を知らない状況ではその患者がどのような疾患にかかっているかを判定するには罹患率$P(Y)$しかない。けれども,その患者の症状がわかれば,尤度$P(X|Y)$を使って症状を織り込み済みの罹患率(これが$P(Y|X)$)が得られるのである。こうして,症状織り込み済みの罹患率(事後確率)の大きい順に疾患をリストすればその患者の疾患候補を確度の高い順に並べることができるのである。

さて,では, $P(Y)$や$P(X|Y)$はどのようにして計算すればよいのだろう。これを医学的に(以後,医学モデルと呼ぶ)求めるのは医学の知識がない者には無理である。そこで,知識を医学文書(あるいは文献)に求めることを考える(以後,これを文書モデルと呼ぶ)。すなわち,入手可能な文献から疾患$Y$と症状$X$の関係性を求めるというやり方である。文献も一種の知識であるから,これはもっともな手法と考えてよい。

そこで,症状と疾患について書かれた文書$D_{i}$を収集する。たとえば「疾患$Y$には$X$の症状が現れる」といった文書である(このような記述は医学事典に豊富にあるだろう)。 次に,事前確率$P(Y)$と尤度$P(X|Y)$を求める。事前確率$P(Y)$は,全文書中の疾患$Y$について書かれた文書の割合とする。本来,事前確率$P(Y)$は罹患率であるが,文書モデルではその疾患についての文書がどのくらい頻繁にみられるかという程度を示している。しかし,ここに問題がある。文書の収集は恣意的である。必ずしも母集団から無作為に選んでいるわけではないだろう。言ってしまえば集めやすいものを集めているだけである。であれば,各々の疾患についての文書は等しく集めるのが無難だろう。そうすれば事前確率$P(Y)$は考えなくてよくなる。

次に尤度$P(X|Y)$であるが,これはその疾患について記述した文書の中で,その症状がどのくらい出やすいかを示すもので,疾患$Y$の文書に出てくる症状$X$の回数を,その疾患の文書に出てくる症状の総数で割ることにより求められる。

実際には症状は$X_{1},X_{2},...$と複数ある。したがって求める尤度は

$\prod_{i}P(X_{i}|Y)$

である。事前確率$P(Y)$は等しくなるようにデータを収集するので,求める事後確率$P(Y|X)$は上記の尤度に比例する。

具体例を考えよう。いま,「腰痛」「排便通」という症状からこれが「子宮内膜症」なのか「子宮筋腫」かを判別する問題を考える。「子宮内膜症」について書かれた文書には全部で症状が100個あり,そのうち,「腰痛」,「排便通」の出現数はそれぞれ30個,20個とする。同様に,「子宮筋腫」の場合は全部で80個の症状が書かれてあり,そのうち「腰痛」,「排便通」の出現数はそれぞれ10個,40個とする。すると, 「子宮内膜症」である事後確率は$0.3\times0.2=0.06$,「子宮筋腫」である事後確率は$\frac{10}{80}\times\frac{40}{80}=0.0625$となって,「子宮筋腫」であると判定できる。

  1. ナイーブベイズを用いたテキスト分類
  2. ナイーブベイズ分類器でTwitterの男女判別!
  3. MathJax

2016年12月11日日曜日

プライバシー情報を扱うスマホアプリから情報を収集する方法について

プライバシー情報を扱うスマホアプリで問題となるのがその情報をどこに保存するかです。スマホに保存すればネットを介した情報漏えいのリスクは回避できます。しかし,それではアプリに入力した情報を関係者間で共有することができません。したがって情報共有を目的としたスマホアプリではこのプライバシーの問題が大きな壁となって立ちはだかります。一方,入力された情報を集めて統計分析するなどしてデータの再利用をするアプリでは必ずしもその限りではありません。なぜならその場合は個人情報が必要ないからです。しかし,この場合であってもデータから個人が特定される危険性があります。例えばアプリに入力したある属性(例えば病名)を収集する必要がある場合,その属性が非常に稀なものであれば,そしてある個人がその属性を持っていることが周知されていたら,かなりの確度でその個人を特定できるかもしれません。この場合,問題なのは属性から個人が特定され,それに紐づけられたほかの情報が漏えいすることです。であれば,属性と他の属性を紐づけて送信しなければ問題はないように思えます。つまり,収集した情報を使って何らかの統計処理を行いたい場合,その統計処理に必要な情報に加工して送信するのです。例えば属性1と属性2の関係性を調べたい場合,属性1そのものではなく属性1に対応した識別子(これはその属性が名義尺度であれば非公開のローカルなコードである必要があります)と属性2に対応した識別子(〃)をペアにして送信します。他の属性とは紐づけません。そうすればたとえ情報が漏えいしたとしても意味すらわかないでしょう。解析する側は属性をローカルコードのまま分析し,結果が出たらローカルコードから属性を復元してその意味を解釈すればよいのです。そうすれば,少なくともシステム上には一切の個人情報も個人情報を特定されそうな情報も残りません。

2016年12月7日水曜日

SmartWatchを用いた患者呼び出しシステム 「」

現状分析と外部設計

アナログをデジタルに!!
今日は附属病院の実習の経験をもとに、詰所で行っている患者呼び出しシステムの分析(図1)と外部設計(図2)を行いました。
現状は電話が来たらノートに記載している。そのシナリオを図に起こしました。
デジタル化するにあたり、入力する項目と何を勉強するのか、通信方法を調べるなど課題が見つかりました。出た課題をもとに、実際に現場で働いている方(クラーク、Ns)にインタビューを行うなどし、解決していきたいと考えています。



図1

図2
図3

2016年12月6日火曜日

メンタルヘルス

インターネットでメンタルチェックサイトを調べてみた。
@メンタルヘルス
無料で自分自身のこころの状態を知るための診断チェック。「メンタルヘルスの悩み相談と無料のメンタルヘルス診断」を謳っている。うつ病,アスペルガー症候群,社会不安障害,サイコパスなど,全部で15の診断ができる。そのほか同じ悩みを持つ話し相手を探すための「話し相手募集」という掲示板や自分の気持ちを自由に呟ける「つぶやき」など,利用者のコミュニケーションを促進する仕掛けがある。
気になるのはどこが運営しているかとビジネスモデル。サイトにはいろいろ広告が出ているので広告収入か?
運営者は株式会社 studio C というところ。事業内容はウェブサイト企画・設計など。精神医療とは無縁っぽい。「どの診断チェックも実際に精神科、心療内科で使用されている信頼性があるものをベースに作成しておりますので、ご自身のこころの状態を確認するためにお役立て下さい」と書いているが出典がないので確認のしようがない。
他にもこのようなサイトがある。
ストレス度診断チェックテスト
「全国約120万人のデータをもとに、あなたのストレス度を判定いたします。ストレスレベルに応じて対処法ページへリンクしています」と書いてあるが・・・。制作しているのは株式会社メンティグループという会社。業務内容は「コンテンツ提供」。「ストレス対応,メンタルヘルス対応」などのコンサルティングや研修なども業務内容に入っている。また,代表者はメンタルヘルス関係の著書もある。「主にマネジメントの観点から、官公庁・企業などでストレス・メンタルヘルス対策、パワハラ対策のコンサルティングや研修・講演などを行ってきた」とあるのでまがい物ではなさそう。少し掘り下げて調べてみる価値はあるかもしれない。
自分で出来るストレスチェック
株式会社フィスメックという会社がやっている。事業内容は「メンタルヘルス事業,医療関連事業」取引先も警視庁など堅実なところが多い。「現在、多くの医療機関や施設で、ストレス状態を把握するためにさまざまなチェックリストが使用されています。このページでは比較的容易に利用できるセルフチェックリストを、順次紹介していきます」とあるがどのようなチェックリストがあるのか知りたいものだ。
うつ病のチェック診断「脳ナビ」
これは辻メンタルクリニックという医療機関がやっているサイト。専門機関だから信用できるだろう。しかし,この診断の出典が記載されていないのは気になる。

こうして見ていくと,設問数と言い,設問内容と言い,似通ってはいるが全く同じではない。それぞれ何を根拠としているのだろうか。また,臨床試験は行っているのだろうか。原典に当たりたい。

これはどうだ?
うつ度チェック 簡易抑うつ症状尺度(QIDS-J) 
これはきちんとした根拠に基づいているようだ。
「簡易抑うつ症状尺度(Quick Inventory of Depressive Symptomatology:QIDS-J)は、16項目の自己記入式の評価尺度で、うつ病の重症度を評価できるほか、アメリカ精神医学会の診断基準DSM-IVの大うつ病性障害(中核的なうつ病)の診断基準に対応しているという特長を持っています。世界的に知られた精神科医John Rush先生によって開発され、世界10カ国以上で使用されています」
うつ病に使われる心理検査。ハミルトンうつ病評価尺度(HAM-D)とは?
こういうのもある。これも「せせらぎメンタルクリニック」という医療機関がやっているので信頼できるだろう。
心理検査
代表的な検査手法が紹介されている。これは役に立ちそう。

2016年12月1日木曜日

覚悟

専門知識に裏付けられない、単なる思い付きと現実を見ない試みは、素人のたわごとと相手にされない可能性が大です。しかし、専門知識はなくとも事実に基づく研究であれば、データの集め方と分析の手法さえ間違わなければ堂々と渡り合うことができます。そこで、次のようなことを考えてみました。
まず、メンタルなコンディションを表す変数をY、それに影響を与えるだろうと思われる複数の要因を(X1, X2, ..., Xn)とします。アンケート調査等により、Yと(X1, X2, ..., Xn)の組を集め、それらを分析して(X1, X2, ..., Xn)とYの関係を求めます。これはいわゆる多変量解析ですが、得られた結果は間違いなく事実に基づいています。ですから、その結果を用いてやりたいことを実現させれば十分に研究として成立すると思います。たとえばスマホから(X1, X2, ..., Xn)を入力してもらい、Yを推測し、アラートを出すアプリを開発するなど。さらに、このアプリに学習機能を加え、ユーザの気持ちとアプリの結果の差異を学習させればより精度を上げることも可能かもしれません。今はやりの人工知能です。
まず、分析についてですが、例えば次のようなことがあるでしょう。
  • XiとXjの相関を求め、それをもとにネットワーク図を描いて要因Xのクラスタリングを行う。これによってメンタルコンディションYに与える要因をグループ化することができます。
  • いわゆる重回帰分析を行ってXからYを推定する(各要因の寄与を調べる)
  • 同じことを決定木を使って行い、どのようなロジックで判定が行われているかを調べる
  • ベイズ学習によって推定の精度を上げる
他にもいろいろあると思いますが、最終的にはこれらの分析によって得られた知見を基にメンタルサポートアプリを開発します。そのアプリを何人かの被験者に使ってもらい、学習効果によって精度がどのように向上したかを分析するのも面白いと思います。特に、最初にアンケートで収集したデータに基づく分析結果と学習後の分析結果を比較すればアプリの重要性を主張できるのではないかと思います。それにこの手のメンタルコンディションを推定するアプリで学習機能を持ったものなどないと思います。


2016年11月30日水曜日

一歩進んでRESTful API

これまでWebAPIを使ったスマホアプリを作ってきたけれど、単なるWebAPIではなく、いま主流(?)のRESTful APIを使ったアプリを考える。まず、RESTful APIとは何か知る必要がある。
RESTful APIとは何なのか
RESTful Webサービスの基本
このあたりから読んでみよう。さらに、「RESTful APIとは何なのか」からリンクが張られている「Web API」とは何なのか」、「リソース指向アーキテクチャ(ROA)とは何なのか」などにも目を通してほしい。



先行研究を調べることの大切さ

これまでの2か月間のゼミ活動で、如何に限られた狭い知識では新しい発想が出てこないかを経験したのではないでしょうか。人間誰でも似たようなことを考えるもの。何か思いついたとしても、それは既に出尽くしたアイデアの一つにすぎず、しかも独りよがりなものです。
やはり先行研究を当たり、これまでその分野でどのようなことが行われてきたかを知るべきです。また、標準的に利用されている様々な技術や概念についても知っておかないと何かやるにしてもどうしたらいいかわかりません。
学会で何か発表する以上、ひととおりの文献調査は必要です。しかし、来春には診療情報管理士の試験も控えており、卒研ばかりに全精力をつぎ込むわけにはいきません。難しいですね。
いずれにしても自分の力を知ることは大切です。

2016年11月28日月曜日

FHIR

最近注目されている(?)技術にFHIRというものがあります。オリジナルサイトは
https://www.hl7.org/fhir/index.html
ですが、全部英語で分かりづらいです。日本語の資料を見つけたのでリンクを貼っておきます。
http://www.hl7.jp/docs/49seminar_2_HL7.pdf
しかし、正直なところ、わかりやすいとは言えません。結局FHIRって何なんだ、と叫びたくなるような内容でした。
そんなとき、こんな論文を見つけました。
Using FHIR to develop a healthcare mobile application
FHIRだけでなくHL7の開発経緯なども分かってなかなか興味深い論文です。英語も比較的平易です。


天気予報アプリ

前回はAjaxを使って外部サーバと通信する技術を修得し、RSSリーダを作りました。今回も引き続きAjaxを利用して外部サーバが提供するWebAPIを利用して情報を取得するアプリを開発します。開発するのは10章の第2節「外部サーバと通信する」で扱う天気予報アプリです。このアプリは、下記のOpenWeatherMapというサイトから天気情報を取得します。

http://openweathermap.org/api
このWebAPIを利用するにはAPI key (APPID)が必要になります。API key を取得するにはユーザ登録が必要です。この画面から登録を行ってAPI key を入手してください。
入手したAPI key はHttpリクエストの際、パラメタとしてサーバへ送ります。テキストP306のプログラムは少し古いようで、そのあたりが抜けています。パラメタ config を以下のように修正してください。

            var config = {
              params: { 
                q: 'Tokyo,jp',
                id: '[ユーザID]',
                APPID: '[API key]'
              }
            }

このAPIはJavascriptオブジェクトとしてレスポンスを返してきます。したがって、レスポンスデータはそのままJavascriptオブジェクトとして利用することができます。たとえば、次のようなフォーマットです。
{
 "coord":{
  "lon":139.69,
  "lat":35.69
 },
 "weather":[
  {
   "id":803,
   "main":"Clouds",
   "description":"broken clouds",
   "icon":"04n"
  }
 ],
 "base":"stations",
 "main":{
  "temp":275.93,
  "pressure":1030,
  "humidity":52,
  "temp_min":271.15,
  "temp_max":281.15
 },
 "visibility":10000,
 "wind":{
  "speed":5.7,
  "deg":10
 },
 "clouds":{
  "all":75
 },
 "dt":1480449600,
 "sys":{
  "type":1,
  "id":7619,
  "message":0.008,
  "country":"JP",
  "sunrise":1480368684,
  "sunset":1480404486
 },
 "id":1850147,
 "name":"Tokyo",
 "cod":200
}

2016年11月16日水曜日

オープンデータ

国がオープンデータ戦略を推進しているようだ。以下は総務省のサイト。
オープンデータ戦略の推進
e-Stat API機能

また,動画による紹介もある。
統計によるオープンデータの高度化
これらを使って何ができるか考えてみるのもよいかもしれない。

2016年11月8日火曜日

MonacaでRSSリーダ

今回MonacaでRSSリーダアプリを作成します。
HTML5モバイルアプリ開発講座 RSSリーダー編  
このアプリではAjaxを用いて外部のサーバから情報を取得する技術を学びます。
まず、下記サイトからRSSリーダのプロジェクトZIPファイルをダウンロードします。
http://s3.asial.co.jp/~monaca/rss.zip
ダウンロードしたプロジェクトをMonacaでインポートしようとすると
Internal Server Error (500)
Neither project_info.json nor config.xml was found. Maybe an old project?
というエラーメッセージが出ます。どうも古いようです。しかたがありません。チュートリアルを見ながらダウンロードしたソースコードを入力(コピペ)するしかありません。

その前にAjaxを学ぶサンプルが紹介されています。 スライドのP7~16までです。スライドにしたがってやってみましょう。IDEで「新規プロジェクト」を選んでテンプレートから「Onsen UI V1 Minimum」を選びます。これが「最小限のテンプレート」です。必ずV1を選んでください。あとはスライドにしたがって作業をしていきます。
    <script>
    $.ajax({
        url: 'http://s3.asial.co.jp/~monaca/rss/blog.xml',
        dataType: 'xml',
    })
    .done(function(data){
        $(data).find('item').each(function(){
            var title = $(this).find('title').text();
            alert(title);
        });
    })
    .fail(function(err){
        alert(JSON.stringify(err));
    })
    </script>
このサンプルプログラムは以下のサイトから情報を取得しています。
http://s3.asial.co.jp/~monaca/rss/blog.xml
実は、これがRSSフィードです。ソースを見てください。XMLで情報が記述されれていることがわかるでしょう。

次に、RSSリーダアプリを作っていきます。スライドのP17から始まります。P22でindex.htmlをアップロードするように書いてありますが、どうも、index.htmlは上書きできないようです。ですから、コピペでMonacaのindex.htmlの内容を書き替えてください。

ところで、このチュートリアル、困ったことにサンプルコード集が見当たりません。ですから、スライドP23の「RSSリーダ STEP①」を実装できません。そこで、多分そうだろうと見当をつけてコーディングしてみました。それがこれです。
        // アプリ起動時の処理
        $(function(){
            $.ajax({
                url: 'http://s3.asial.co.jp/~monaca/rss/blog.xml',
                dataType: 'xml',
            })
            .done(function(data){
                var html = '';
                $(data).find('item').each(function(){
                    var pubDate = $(this).find('pubDate').text();
                    var title = $(this).find('title').text();
                    var link = $(this).find('link').text();
                    var description = $(this).find('description').text();
                    html += createArticleNode(pubDate, link, title, description);
                })
                $('#articleList').html(html);
            })
            .fail(function(){
                alert(JSON.stringify(err));
            })
        });

これは、アプリ起動時の処理です。Ajaxを使ってRSSフィード'http://s3.asial.co.jp/~monaca/rss/blog.xml'を読み込んで更新日 pubDate、タイトル title、記事URL link、そして本文 descriptionを取り出してcreateArticleNode関数を使ってhtmlタグ(li要素)を作っています。これをリストの数だけ作ってくっつけてそれをセレクタ'#articleList'で示されるulタグ内に挿入しています。
このプログラムではXMLデータの処理の仕方やHTMLの生成方法など参考になるTipsがたくさん出てきているのでしっかり身につけてください。
次はcreateArticleNode関数の中身です。
    // 1件分の記事を表示するli要素を生成
    function createArticleNode(pubDate, link, title, description) {
        var date = new Date(pubDate);
        var li = 
            '<li class="btn">' +
                '<a href="' + link + '">' +
                    '<time>' +
                        '<span class="date">' + date.getFullYear() + '</span>' +
                        '<span class="month">' + (date.getMonth() + 1) + '</span>' +
                        '<span class="year">' + date.getDate() + '</span>' +
                    '</time>' +
                    '<div>' +
                        '<h2>' + title + '</h2>' +
                        '<p>' + description + '</p>' +
                    '</div>' +
                '</a>' +
            '</li>';
        return li;
    }

RSS提供サイト一覧
これらをajaxに渡すリクエストの送信先(url)に設定してどのように変わるかを確かめてください。

はてなブックマークはRSS1.0でpubDateの代わりにdc:dateになっている。ところが、XMLのタグ名がdc:dateだとjQueryのfindメソッドでは取ってこれないようだ。そこで、その場合はDOMを使って以下のようにしてdc:date要素を取ってくる。
$(this).children().each(function(){
    if($(this)[0].tagName == "dc:date") {
        pubDate = $(this).text();
    }
})

2016年11月1日火曜日

パーソナル・ヘルス・レコード

こんなサイトを見つけました。
PHRを活用したヘルスケアサービス事例
これは、第2回クラウド時代の医療ICTの在り方に関する懇談会の資料のようです。電子母子健康手帳の開発でよく目にする株式会社エムティーアイが作成した資料です。
T君やKさんの研究の参考になるのではないかと掲載しました。
この株式会社エムティーアイという会社は千葉県の柏市と共同でスマホを使った健康サービスを展開している企業で、私の調べた限り、電子母子健康手帳に関しては一番アクティブに活動し一歩先を行く企業です。
実は私も電子母子健康手帳の開発を行っていて、情報を収集していました。私の印象では数年前(2012~2014年頃)までは盛んに行われていたけれど最近はめっきり情報が少なくなっています。その点、これは昨年(2015年)の資料なので比較的最近の動向を知るにはうってつけです。
また、最近の動向としては、AMED(国立研究開発法人日本医療研究開発機構)が募集した研究事業「パーソナル・ヘルス・レコード(PHR)利活用研究事業」に「妊娠・出産・子育て支援PHRモデルに関する研究」という研究課題があり、1.8億円で前橋工科大学が獲得しています。これは、今年の7月から始まった事業なので今のところ何も情報は出てきていませんが、これから注目していく必要があると思います。
新しい情報としては、総務省が「総務省における医療等分野のICT利活用について」という資料を平成28年10月に出しています。この中でも妊娠・出産・子育て支援について触れています。目新しい概念としてPLR(Personal Life Repository)とか分散PDS(Personal Data Storage)という言葉が躍っていました。どんどん新しい概念が登場します。ついていくようアンテナを張っておかないと・・・。

2016年10月11日火曜日

カメラアプリを作ろう!

Cordovaを使ってカメラアプリを作成します。
以下の手順にしたがって進めてください。
  1. Monacaで新規プロジェクトを作成する
  2. テンプレートとして Onsen UI V1 Minimum を選択する
  3. 「設定」→「JS/CSSコンポーネントの追加と削除」でjQuery (Monaca Version)をインストールする
  4. index.htmlを以下のように修正する。

list.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">
    <meta http-equiv="Content-Security-Policy" content="default-src * data:; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
    <script src="components/loader.js"></script>
    <link rel="stylesheet" href="components/loader.css">
    <link rel="stylesheet" href="css/style.css">
    <script>
        ons.bootstrap();

        var images = null;
        // ギャラリーから画像のパスを取得する
        var getPictureFromGallery = function(onSuccess) {
            var options = {
                quality: 50,
                sourceType : Camera.PictureSourceType.PHOTOLIBRARY,
                destinationType: Camera.DestinationType.FILE_URI
            };
            
            navigator.camera.getPicture(function(imageURI) {
                onSuccess(imageURI);
            }, onFail, options);
        };
          
        // 写真を撮影して保存する
        var getPictureFromCamera = function(onSuccess) {
        
            // デバイスのカメラアプリを利用して撮影し保存
            var options = {
                sourceType : Camera.PictureSourceType.CAMERA,
                saveToPhotoAlbum: true,
                correctOrientation:true,
                destinationType: Camera.DestinationType.FILE_URI 
            };
            
            // カメラアプリを起動し、撮影して保存
            navigator.camera.getPicture(function(imageURI) {
                onSuccess(imageURI);
            }, onFail, options);
        };
        
        function onFail() {
            console.log("写真を取得できませんでした");
        }
        
        $(document).on('pageinit', '#main-page', function() {
            var onSuccess = function(pictureUrl) {
                images = [];
                var $img = $('<img>');
                $img.attr('src', pictureUrl);
                images.push({
                    element: $img,
                    label: '無加工'
                });
                myNavi.pushPage('image.html');
            }
            
            $(this).on('click', '.take-from-camera', function() {
                getPictureFromCamera(onSuccess);
            });
            $(this).on('click', '.take-from-gallery', function() {
                getPictureFromGallery(onSuccess);
            });
        });
        
        $(document).on('pageinit', '#image-page', function() {
            var elements = images.map(function(image) {
                var element = $('<ons-carousel-item><div class="converted-image-label-wrapper"><div class="converted-image-label"></div></div></ons-carousel-item>');
                $(image.element).addClass('converted-image');
                element.prepend(image.element);
                element.find('.converted-image-label').text(image.label);
                return element[0];
            });
            elements.forEach(function(element) {
                $('ons-carousel').append(element);
            });

            setTimeout(function() {
                imageCarousel.refresh();
            }, 100);
        });
            
    </script>
</head>
<body>
    <ons-navigator var="myNavi">
        <ons-page id="main-page">
            <ons-toolbar>
                <div class="center">画像変換アプリ</div>
            </ons-toolbar>
            <br>
            <ons-list>
                <ons-list-item modifier="chevron" class="take-from-gallery">
                    <ons-icon icon="ion-images"></ons-icon> ギャラリーから選ぶ
                </ons-list-item>
                <ons-list-item modifier="chevron" class="take-from-camera">
                    <ons-icon icon="ion-camera"></ons-icon> カメラで撮影
                </ons-list-item>
            </ons-list>
            <br>
        </ons-page>
    </ons-navigator> 
    <ons-template id="image.html">
        <ons-page id="image-page">
            <ons-toolbar>
                <div class="left">
                    <ons-back-button ons-if-platform="ios">Back</ons-back-button>
                </div>
                <div class="center">画像</div>
            </ons-toolbar>
            <ons-carousel var="imageCarousel" class="image-carousel" fullscreen auto-scroll overscrollable>
            </ons-carousel>
        </ons-page>
    </ons-template>
</body>
</html>

ons.bootstrap()

11行目のons.bootstrap()は,Onsen UIを読み込み,Onsen UIの機能をアプリで利用できるようにする。 javascriptの一番最初に実行する。 もし,これを書かなければOnsen UIを利用した処理は動かない。
なお,この関数は Onsen UI V1 Minimum (Onsen UI Version 1 最小限のプロジェクト)でなければ動かないみたい。既に Onsen UI V2 は出ているみたいだけれど動かなかった。

getPictureFromGallery()

15行目のgetPictureFromGalleryは,ギャラリーから画像のパスを取得する関数。 引数の onSuccess は成功したときに実行するコールバック関数で,実際には23行目で呼び出され,49~58行目で定義したものが実行される。 実行時,23行目に書いてあるように,引数として imageURI が渡される。 imageURIは,ギャラリーから画像のパスを取得するCordovaのAPIである navigator.camera.getPicture が成功したときに呼び出されるコールバック関数で,オプションの destinationType に Camera.DestinationType.FILE_URI を指定した場合,このコールバック関数の引数に画像ファイルへのパスが返される。

getPictureFromCamera()

28行目のgetPictureFromCameraは,写真を撮影して保存する関数。 CordovaのAPIである navigator.camera.getPicture を呼び出すときのオプション sourceType が Camera.PictureSourceType.CAMERA になっているだけで,他はgetPictureFromGalleryと同じである。

onFail()

CordovaのAPIである navigator.camera.getPicture が何らかの原因で失敗したときに呼び出される関数。

メインページが表示されたときのイベントハンドラ

48~66行目はメインページが表示されたときのイベントハンドラ。 前述したように49~58行目にgetPictureFromGalleryやgetPictureFromCameraの引数に指定するコールバック関数が定義されている。 この関数では,引数に渡された画像ファイルのURIをsrc属性に指定したimgタグを作り,'無加工'というラベルとともにimages配列へ追加している。 ただ,このとき,50行目でこの関数に入る都度images配列を初期化しているため,何回この関数が呼び出されても配列imagesには1枚の画像しか格納されない。なぜそうなっているのかは不明。 50行目を取り除き,13行目でimagesを定義するとき空の配列 [] で初期化すれば複数の画像が配列に格納される。 60~62行目,63~65行目は,それぞれボタン「カメラで撮影」,「ギャラリーから選ぶ」がクリックされたときの呼ばれるイベントハンドラである。 ここで $(this) は,メインページを表す。一般に,イベントハンドラ内では$(this)はそのイベントを取得したオブジェクトを表す。 したがって,メインページ内でセレクタが '.take-from-camera' のオブジェクト(classプロパティにtake-from-cameraが設定されているタグ)がクリックされたとき関数getPictureFromCamera()が, '.take-from-gallery' のオブジェクトがクリックされたとき関数getPictureFromGallery()が呼び出されるようにイベントハンドラが登録される。

画像ページが表示されたときのイベントハンドラ


68~83行目は画像を表示するためのページ(image-page) が表示されたときに呼び出されるイベントハンドラの定義である。
まず,69行目のimages.mapであるが,これは配列(この場合はimages)の各々の要素に対してある処理を行って,その結果から得られるデータを要素とする配列を返す関数である。
具体的には,ギャラリーから取得した,あるいは,カメラで撮影した画像を指定して50~56行目で作成したimg要素配列の各々に対して,70~73行目で画像をカルーセル表示するためのons-carousel-item要素を作成し,それを要素とする配列を変数elementsに格納している。
そうして作成した配列elementsの各々の要素を,76~78行目でons-carousel要素の子要素として加えている。
最後に80~82行目でカルーセルを0.1秒後にリフレッシュしている。ここで,81行目のimageCarouselは115行目でons-carousel要素につけた変数名である。
なお,ons-carousel要素はテキストのP161に説明があるが,左右や上下に項目を並べてスワイプで移動できるようにしたものである。詳細はオンラインマニュアルを参照のこと。

プリプロード

後で表示する画像を前もって読み込んでおき,表示するときにスムーズにすることをプリプロードという。 51~56行目でこのプリプロードを行っている。プリプロードを行うにはまずimgタグ要素を作成する(51行目)。jQueryでhtml要素を作成するには$()内にセレクタでなくhtmlタグ(この場合は<img>)を書く。このとき返される値はjQuery要素である。こうして作成されたhtmlタグ要素にattrメソッドでsrc属性に画像のurlを設定する(52行目)。

2016年10月5日水曜日

ゼミの初日

今日はゼミの初日。ガイダンスとスケジュールの確認,ゼミの進め方を説明し,早速Monacaでスマホアプリの作り方を練習しました。
以下は秋学期のスケジュール。


1限目はMonacaを使ったスマホアプリ技術の修得。2限目は各自の研究テーマの文献調査。
今日の話し合いで暫定的なテーマが決まりました。次回はそれについての調査報告。
  • K君:スマホによる質問紙によってメンタルな問題を抱えている人の心の状態を定量化し,それと他の様々な要因(歩数など運動量,睡眠時間,食欲,天候,・・・)との相関を経時的にグラフ表示して見える化し,心が不安定になる兆候を自分自身で把握し予防できるようなスマホアプリを開発する。
  • T君:育児日記の電子化。ただ電子化するだけならブログと同じ。そこで,育児日記をつける際赤ちゃんの夜泣きの数,飲んだおっぱいの量,睡眠時間,・・・といった値を記録し,産後うつの指標(エジンバラ産後うつ病自己評価票)との相関を経時的にグラフ表示して見える化し,うつになる兆候を自分自身で把握し予防できるようなスマホアプリを開発する。
  • Kさん:月経時の頭痛などの体調不良は適切な時間にピルを服用することにより予防できる。しかし,ピルの服用には種類によってタイミングがあり,時期を逸すると効果があまり期待できない。そこで,前回の月経,体温,食欲などのデータをスマホアプリで管理し,ピルを服用すべき適切な時期をアラートするスマホアプリを開発する。
各自のテーマについての先行研究や事例研究を文献調査して毎回のゼミで報告する。

2016年10月4日火曜日

明日からゼミが始まります!

いよいよ明日から3年生の新しいゼミが始まります。これから1年間半ゼミ活動を続けていくことになりますが,明日はその初日ということでいろいろな準備を行います。
  • Googleアカウントの取得
  • Googleドライブの共有設定
  • ブログの共有設定
  • Monacaのアカウント登録
次に「Monacaで学ぶ初めてのプログラミング」というオンラインブックを読みます。ただし,この本は無料で読むことのできるのは27ページまでです。それ以降を読みたければ購入しなければなりません。
また,Monacaには公式ガイドブックがあります。 ただし,この本は昨年出版されたもので,その後Monacaは進化を遂げているため,多少記述が古く,書いてある通りに操作ができないという難点があります。それでもMonacaでアプリを開発する際には常に携帯しておく価値のある本です。
また,Monacaのホームページには「はじめてのHTML5モバイルアプリ時開発講座」というのがあり,これにしたがって学習していけばおおよそのMonacaでのアプリ開発手法を学ぶことができます。