d4i.log

作ったものなど

D3.js で Word Clouds

 Wordle ライクに Word Clouds を描きたいなーと思い、D3Word Cloud Layout を試してみました。

f:id:d4i:20130831013322p:plain

データの準備


 とりあえず CSV からデータを読み込むことを想定してサンプルを作ります。単語と頻度を記述してあれば何でもいいのですが、折角なので公開されているデータを利用してみます。
 今回使ったのはこちら ↓

FDA Adverse Event Reporting System (FAERS)

 米食品医薬品局 (FDA) が公開している医薬品の有害事象報告データです。簡単に言うと、「この薬を使ったらこんな副作用っぽい反応があった」的な情報の集積です。

 今回は 2012q4 のデータから医薬品と報告数を抽出してサンプルとします。下記の処理は Fedora 19 のシェルで行っていますが、他の Linux や Mac の場合でも似たような感じです。

1 - データのダウンロード

 FAERS から FAERS_ASCII_2012q4.zip をダウンロード・展開します。

$ mkdir faers && cd faers
$ wget http://www.fda.gov/downloads/Drugs/GuidanceComplianceRegulatoryInformation/Surveillance/AdverseDrugEffects/UCM364757.zip
$ unzip UCM364757.zip && rm UCM364757.zip 

 以下は展開したファイルの中身。

$ tree
.
├── FAQs.doc
├── README.doc
└── ascii
    ├── ASC_NTS.doc
    ├── demo12q4.pdf
    ├── demo12q4.txt
    ├── drug12q4.pdf
    ├── drug12q4.txt
    ├── indi12q4.pdf
    ├── indi12q4.txt
    ├── outc12q4.pdf
    ├── outc12q4.txt
    ├── reac12q4.pdf
    ├── reac12q4.txt
    ├── rpsr12q4.pdf
    ├── rpsr12q4.txt
    ├── ther12q4.pdf
    └── ther12q4.txt

 今回使うのは drug12q4.txt のみです。

$ head ascii/drug12q4.txt
primaryid$caseid$drug_seq$role_cod$drugname$val_vbm$route$dose_vbm$cum_dose_chr$cum_dose_unit$dechal$rechal$lot_nbr$exp_dt$nda_num$dose_amt$dose_unit$dose_form$dose_freq
34483284$3448328$1$PS$SUSTIVA$1$TRANSPLACENTAL$UNK UNK$$$$U$$$020972$$$$
34483284$3448328$2$SS$NEVIRAPINE$1$TRANSPLACENTAL$$$$$U$$$$$$$
34483284$3448328$3$SS$VIRACEPT$1$TRANSPLACENTAL$UNK UNK$$$$U$$$$$$$
34483284$3448328$4$SS$COMBIVIR$1$TRANSPLACENTAL$UNK UNK$$$$U$$$$$$$
34483284$3448328$5$SS$RETROVIR$1$TRANSPLACENTAL$UNK UNK$$$$U$$$$$$$
35676563$3567656$1$PS$EXELON$1$ORAL$1.5 MG, BID$59.714$MG$$U$$$020823$1.5$MG$$BID
36059074$3605907$1$PS$EXELON$1$ORAL$1.5 MG, BID$$$$U$$$020823$1.5$MG$Capsule$BID
36177452$3617745$1$PS$EXELON$1$ORAL$1.5 MG, BID$$$$U$$$020823$1.5$MG$Capsule$BID
36177452$3617745$2$SS$EXELON$1$ORAL$1.5 MG, ONCE/DIALY$$$$U$$$020823$1.5$MG$Capsule$X1

 1行目はヘッダー、2行目以降がレコード、 $ はフィールドの区切りです。
 drugname 毎の primaryid を集計して報告数とします。

2 - 医薬品と報告数の CSV 出力

 ヘッダーを作成。

$ echo 'count,word' > example.csv 

 drugname と primaryid のユニークなペアを抽出 → 集計 → 上位100剤を出力。

$ awk -F \$ '{ print $5 "$" $1 }' ascii/drug12q4.txt | \
 sort | uniq | awk -F \$ '{ print $1 }' | sort | uniq -c | \
 sort -r | sed -e 's/^ \+\([0-9]\+\) /\1,/g' | \
 head -100 >> example.csv

 example.csv がサンプルの CSV となります。
 集計処理は Gawk でちゃんと書けばここまでパイプ使わない気もしますが、楽だから気にしない!

Word Clouds 描画


 HTML で D3.jsd3.layout.cloud.js を読み込ませ、 SVG を描画する JavaScript を書いていきます。

<!DOCTYPE HTML>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>D3 Word Clouds</title>
    <meta name="author" content="d4i"/>
  </head>
  <body>
    <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script src="https://rawgithub.com/jasondavies/d3-cloud/master/d3.layout.cloud.js"></script>

    <script>
      var filename = 'example.csv';

      d3.csv(filename, function(data){
        data = data.splice(0, 100);

        var width = 1200,
        height = 600,
        fill = d3.scale.category20(),
        maxcount = d3.max(data, function(d){ return d.count; } ),
        wordcount = data.map(function(d) { return {text: d.word, size: d.count / maxcount * 10}; });

        d3.layout.cloud().size([width, height])
        .words(wordcount)
        .padding(5)
        .rotate(function() { return ~~(Math.random() * 2) * 90; })
        .font("Impact")
        .fontSize(function(d) { return d.size; })
        .on("end", draw)
        .start();

        function draw(words) {
          d3.select("body").append("svg")
          .attr({
            "width": width,
            "height": height
          })
          .append("g")
          .attr("transform", "translate(" + [ width >> 1, height >> 1 ] + ")")
          .selectAll("text")
          .data(words)
          .enter()
          .append("text")
          .style({
            "font-size": function(d) { return d.size + "px"; },
            "font-family": "Impact",
            "fill": function(d, i) { return fill(i); }
          })
          .attr({
            "text-anchor": "middle",
            "transform": function(d) { return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")"; }
          })
          .text(function(d) { return d.text; });
        }
      });
    </script>
  </body>
</html>

 作成した example.csv を同じディレクトリに置いてブラウザから描画される図を見ます。コードで細かく調整できます。
 Web から見るとこんな感じ
 (位置はランダムなのでリロードで形が変わります。)

感想

 描画の自由度が高くて良いですね。逆に調整が複雑になる印象はありますが。
 D3 は JavaScriptCSS で小回りが効くので、可視化を美しくするポテンシャルがあるなーと思います。

Getting Started With D3

Getting Started With D3