Pages

ラベル R の投稿を表示しています。 すべての投稿を表示
ラベル R の投稿を表示しています。 すべての投稿を表示

2012年9月17日月曜日

Mongodbを使いTwitterデータをRで解析する

「Twitterから取得したデータを解析し、mongodbに格納し、Rで分析する」Node.jsアプリケーションを開発するための実験。プロトタイピング。Pragmatic Programmer流儀であれば、曳光弾プログラムでしょうか。

実験の環境

  • OSX 10.8.1 Mountain Lion
  • node.js 0.8.7
  • R 2.13.2
  • mongodb 2.0.7

内容

  • Twitter開発者登録
  • Streaming APIの利用
  • P.O.S parts of speech 単語解析
  • mongodbへのデータ登録
  • Rからmongodbを利用

twitterの開発者登録

TwitterAPIの利用は、OAuthが推奨されている(Basic認証でも今のところは大丈夫だが、いずれダメになる)ので、OAuth認証を行うため、twitterの開発者登録を行い、アプリを登録しなければならない。

Twitter開発サイト Streaming API パラメータの扱い

Twitter開発者アカウント登録の手順

http://ratememo.blog17.fc2.com/blog-entry-967.html を参考にしました。

  1. Twitterアカウントを用意する (mailアカウントが必要、既に登録済みのものはダメ)
  2. http://dev.twitter.com/ に取得したTwitterアカウントで、ログインする
  3. 「My Applicationの登録」リンクがあるhttps://dev.twitter.com/apps/new ので、その画面に移動しアプリを登録
  4. access level を "read" から "read & write"に変更
  5. Access Token (Access Token key と Access Token Secret)を取得し、控えておく

ntwitterライブラリを使いstreaming APIへのアクセスをテスト

OAuthのサンプルのほとんどが、ユーザサイドから利用するWebアプリの認証に関するもので混乱。(OAuthの構造を良く理解していない自分が悪い)

今にして思へば、Twitterの開発者登録の時に、取得した"Access Token"は、「自分自身(自分のアプリ)がtwitterAPIにアクセスするための暗号キー」で、アプリを利用するユーザ(consumer)が使用するものではないのです。随分と混乱してます。

結局、ntwitterライブラリのサンプルソースを使う。

twitter API利用Node.jsライブラリ ntwitter

エラーイベントが起きる。調査

前回のサンプルを作った時にはエラー発生しなかったのに。なぜだろう。
events.js:68
        throw new Error("Uncaught, unspecified 'error' event.");

https://github.com/AvianFlu/ntwitter/issues/57

とりあえず、エラーイベントを回避するため、サンプルコードに以下を追加。

    scream.on("error", function(error){
        console.log(arguments);
    });

再度、何度か実行してみると、成功する場合とエラーが返ってくる場合(argumentsを表示している)があるが、理由は不明。Twitter側の問題だろうか?

失敗した場合

$ node sample/ntwitter_sample.js 
{ '0': 'http', '1': 401 }

P.O.S moduleを組み込んで実行してみた結果

英語専用の形態要素分析 "part-of-speach POS Tagging"のライブラリpos-jsを使い、単語の分解と品詞のタグ付けを試みる。POS Taggingとpos-jsライブラリについては、以前 記事を書いたので見ていただけますと幸いです。

http://tech-baker.blogspot.jp/2012/06/part-of-speech-tagging.html

ここまでのプログラムソースは、以下の通り。

var twitter = require('ntwitter');    // twitter API用ライブラリ

// Twitter開発者登録の結果
var twit = new twitter({
    consumer_key        : 'xxxxxxxxxxxxxxxxxxxx',
    consumer_secret     : 'xxxxxxxxxxxxxxxxxxxx',
    access_token_key    : 'xxxxxxxxxxxxxxxxxxxx',
    access_token_secret : 'xxxxxxxxxxxxxxxxxxxx'
});

var pos = require('pos');             // POS Tagging用ライブラリ  

// 検索キーは、とりあえず appleで :-p
twit.stream('statuses/filter', {'track':'apple'}, function(stream) {
    stream.on('data', function(data) {
        // この行は日本語を省くため 結構いい加減
        if(data.text.toLowerCase().indexOf('apple') > 0){
            console.log(data.text);                        // オリジナルを表示
            var words = new pos.Lexer().lex(data.text);    // 単語分解
            var taggedWords = new pos.Tagger().tag(words); // 品詞のタグ付け jsonで返す
            for(i in taggedWords){
                var taggedWord = taggedWords[i];
                var word = taggedWord[0];
                var tag  = taggedWord[1];
                console.log(word + " : " + tag);
            }
        }
    });

    stream.on('end', function(response){
        //console.log('');
    });

    stream.on('destroy', function(response){
        //console.log('');
    });

    // エラーイベント対処
    stream.on('error', function(error){
        console.log(arguments);
    });

    // 5000ミリ秒で終了イベントを発生させた。これをしないと何時までのTwitterからのpushを受け続ける
    setTimeout(stream.destroy, 5000);
});

実行してみると

$ node sample/s_api_with_pos.js 
(途中省略)
@WhipeeDip One fifty and Apple will hand you a new phone. Though that thing is probably pushing “catastrophic damage ineligible for swap.”
@WhipeeDip : NN
One : NN
fifty : NN
and : CC
Apple : NNP
will : MD
hand : NN
you : PRP
a : DT
new : JJ
phone : NN
. : .
Though : IN
that : IN
thing : VBG
is : VBZ
probably : RB
pushing : VBG
“catastrophic : NN
damage : NN
ineligible : JJ
for : IN
swap : NN
. : .
” : NN


形態要素分析結果をmongodbに保存する

Node.js用のmongodb接続ライブラリ mongooseをインストールする。 # npm install mongoose 一発。

mongooseの使い方については、いずれまとめたいと思います。以下のサイトを参考にしました。

mongoose 本家 github

node.js + mongoose + mongodbで遊ぶ

上記のプログラムを改造して、mongodbに登録を行うように改造してみる。出来たプログラムを動かしてみると?

  • 終了しない。プログラムが。
  • でも、保存されたもよう。

mongodbのクライアントアプリ mongoで直接データを覗いてみます。

$ mongo
MongoDB shell version: 2.0.7
connecting to: test
> show dbs
local   (empty)
posdb   0.203125GB
test    (empty)
> use posdb
switched to db posdb
> show collections
postags
system.indexes
> db.postags.find()
{ "word" : "@PissWizardCunt", "tag" : "NN", "_id" : ObjectId("50557db5da8a0357b8000001"), "__v" : 0 }
{ "word" : "not", "tag" : "RB", "_id" : ObjectId("50557db5da8a0357b8000006"), "__v" : 0 }
(途中省略)
{ "word" : "5", "tag" : "CD", "_id" : ObjectId("50557db5da8a0357b800005b"), "__v" : 0 }
{ "word" : ".", "tag" : ".", "_id" : ObjectId("50557db5da8a0357b8000060"), "__v" : 0 }
has more
> exit
bye

has more が表示されているけど、残りのデータは、どのように表示させたら良いのかな?

// (途中まで同じ)

var pos = require('pos');            // POS Taggingライブラリを呼び出す
var mongoose = require('mongoose');  // mongodb接続用ライブラリを呼び出す

// スキーマの定義 mongooseは、ORMのように振る舞う。modelを定義する訳ですね。 
var Schema = mongoose.Schema;
var PosSchema = new Schema({
    tag: String,
    word: String
});
mongoose.model('PosTag', PosSchema);
// 接続
mongoose.connect('mongodb://localhost/posdb');
var PosTag = mongoose.model('PosTag');

twit.stream('statuses/filter', {'track':'apple'}, function(stream) {
    // TwitterからTweetを取得し
    stream.on('data', function(data) {
        if(data.text.toLowerCase().indexOf('apple') > 0){   // 日本語は省き
            var words = new pos.Lexer().lex(data.text);     // 単語に分解し
            var taggedWords = new pos.Tagger().tag(words);  // 品詞タグをつけ
            for(i in taggedWords){
                var postag = new PosTag();                  // 新規レコードを用意?
                postag.tag = taggedWord[1];                 // 値をセット
                postag.word = taggedWord[0];                // 値をセット
                postag.save(function(err){                  // 登録エラー処理
                    if(err){ console.log(err);}
                });
            }
        }
    });

// (後半省略)

});

統計解析環境Rからmongodbのデータを呼び出す

OSXはパッケージを本家からダウンロードして使っている。

mongodb接続ライブラリrmongodbをインストールするため、

  1. メニューのパッケージインストーラを起動
  2. ダウンロード先のミラーサイトを指定し
  3. rmongodb を検索し、インストール


 httpdヘルプサーバーを起動...   完了 
 URL 'http://cran.md.tsukuba.ac.jp/bin/macosx/leopard/contrib/2.13/rmongodb_1.0.2.tgz' を試しています 
Content type 'application/x-gzip' length 733855 bytes (716 Kb)
 開かれた URL 
==================================================
downloaded 716 Kb
 
ダウンロードされたパッケージは、以下にあります /var/folders/cw/qznw_j_1313d3stpq5zc34240000gn/T//RtmpAbXBrm/downloaded_packages
console画面から、rmongodbを読み込んでみて、テストしてみる。
> library(rmongodb)
rmongodb package (mongo-r-driver) loaded
Use 'help("mongo")' to get started.

うまく、rmongodbライブラリはインストールされたので、恐る恐るtwitterデータを確認してみる。

保存されているデータの構成(私の現在のdb, collection構成)

  ---------     ------------  
  database      collections
  ---------     ------------

  posdb ------> postags
  test (empty)
  local(empty)


$ r

R version 2.13.2 (2011-09-30)
Copyright (C) 2011 The R Foundation for Statistical Computing
ISBN 3-900051-07-0
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)
(途中省略)

> library(rmongodb)
rmongodb package (mongo-r-driver) loaded
Use 'help("mongo")' to get started.

> db <- mongo.create()
> mongo.find.one(db,'posdb.postags')
    word : 2     @PissWizardCunt
    tag : 2      NN
    _id : 7      50557db5da8a0357b8000001
    __v : 16     0
> mongo.find.one(db, 'posdb.postags', list(tag='NN'))
    word : 2     @PissWizardCunt
    tag : 2      NN
    _id : 7      50557db5da8a0357b8000001
    __v : 16     0
> mongo.find.one(db, 'posdb.postags', list(tag='CD'))
    word : 2     13
    tag : 2      CD
    _id : 7      50557db5da8a0357b800001a
    __v : 16     0
> mongo.find.one(db, 'posdb.postags', list(tag='RB'))
    word : 2     not
    tag : 2      RB
    _id : 7      50557db5da8a0357b8000006
    __v : 16     0
> quit()
Save workspace image? [y/n/c]: n

無事、データをRで処理することが出来るところまで、たどり着きました。今回の実験は終了。

2012年6月3日日曜日

Webな統計分析環境 RStudio とMongodb

さくらVPS に、NoSQLデータベース Mongodbをデータの取得・保存先に使い、統計分析を行う為の環境を用意した。
  • 統計解析ツール R
  • データベース Mongodb
  • Rとmongodbを連携させるためのRライブラリ rmongodb
  • R利用のためのユーザインターフェース R Studio
最終的に目指しているのは、データを自動で定期的にmongodbにため込み、RStudioで適宜分析する環境だ。
インターネットのデータ -- (Spidering Tool) -- mongodb -- rmongodb -- R -- R Studio

R & R Studioのインストール

既に拡張パッケージ EPEL(Extra Packages for Enterprise Linux)を利用して入れば、Rのインストールはyumを使うだめの簡単作業だ。
http://fedoraproject.org/wiki/EPEL
依存しているパッケージが20ぐらいあったような。
R Studioは、統計解析ソフトRのためのIDE(統合開発環境)だ。Rの為のEclipseやVisualStudioと言ったところか。サーバ版をインストールした。
http://rstudio.org/
# Rをインストールする
# EPELを使っていることが前提  
  $ sudo yum install R
# RStudioのrpmパッケージを取得し、インストール
  $ wget http://download2.rstudio.org/rstudio-server-0.96.228-x86_64.rpm
  $ sudo rpm -Uvh rstudio-server-0.96.228-x86_64.rpm 
インストールすると、サーバが起動し、起動スクリプトもセットされる。RStudioは、初期設定ポートが8787なので、8787ポートは開けてあげること。 http://yoursite.com:8787/ にアクセスするとログイン画面が表示されるはず。ユーザ情報は、Linuxユーザ情報をそのまま利用している。




rmongodb

Mongodbのインストールは、随分前にインストールしていたので、省略。yumでインストール出来たはず。コンパイルとか、随分とインストールに時間が掛かったような記憶がある。
rmongodbは、Rのライブラリで、Rに、mongodbへの接続機能を追加するものだ。R本体はデータの取得先に対する機能は充実しておらず、その当たりはライブラリで頑張ってくださいと言う姿勢らしい。
githubで公開しているrmongodbは、上手くコンパイル出来なかった。CRAN(the Comprehensive R Archive Network)から取得すると上手くらしい。
# mongodbとRを接続するためのライブラリ rmongodbをインストールする
# githubに配布されているものは、なぜかインストールに失敗するので、CRANから取得しインストール
 $ wget http://cran.r-project.org/src/contrib/rmongodb_1.0.3.tar.gz 
 $ sudo R CMD INSTALL rmongodb_1.0.3.tar.gz 

R Studioを実行してみる

まず、コマンドラインからrmongodbが動作するか確認してみた。
> R                 # コマンドラインからRを起動
(途中省略)
> library(rmongodb) # rmongodbをロードする
rmongodb package (mongo-r-driver) loaded
Use 'help("mongo")' to get started.

R Studioから、同じ事をやってみる。
  1. http://yoursite.com:8787/ にアクセス
  2. linuxユーザでログイン
  3. 右下のペインのPackages タブを選択
  4. rmongodb にチェックを入れる
すると、右のコンソールに上記のコマンドラインと同じメッセージが表示される。(当たり前だけど)
なかなか、使い勝手がよさそう。




Mongodbへのデータ登録と取得

サンプルソースをみて、Rからmongodbを利用する方法など確認してみた。
# insert
mongo <- mongo.create()                            # 接続
if (mongo.is.connected(mongo)) {                   # 接続確認
    buf <- mongo.bson.buffer.create()              # bson用の1レコードバッファをR内に用意
    mongo.bson.buffer.append(buf, "name", "baker") # レコードバッファに属性と値をセット
    mongo.bson.buffer.append(buf, "age", 50L)
    b <- mongo.bson.from.buffer(buf)               # レコードバッファをbson形式に変更
    mongo.insert(mongo, "test.people", b)          # db:test, collection:peopleに追加
}

# select
mongo <- mongo.create()
if (mongo.is.connected(mongo)) {
    buf <- mongo.bson.buffer.create()
    mongo.bson.buffer.append(buf, "age", 18L)
    query <- mongo.bson.from.buffer(buf)

    # Find the first 100 records
    #    in collection people of database test where age == 18
    # queryの内容は、{age:18L}
    # レコードコレクションに対するカーソルが提供される
    cursor <- mongo.find(mongo, "test.people", query, limit=100L)
    # Step though the matching records and display them
    # nextメソッドで、カーソルを順に動かしていく
    while (mongo.cursor.next(cursor))
        print(mongo.cursor.value(cursor))
    mongo.cursor.destroy(cursor)                  # カーソルの開放
    # 現在、100件も入っていないので表示されませんが :-p
}
実際にMongodbにデータが格納されていることを、mongodbのクライアントソフトmongoで確認してみる。
# mongo (クライアントソフトから実行結果を確認してみる)
> show dbs     # データベース一覧をみる
admin
error_logger
local
test
> use test     # データベース test に移動
switched to db test
> show collections   # 現在のデータベース内のコレクションをみる
foo
people
system.indexes
users
> db.people.find()   # test.people を全件検索してみる
{ "_id" : ObjectId("4fcb042f3eee4d39039e1b87"), "name" : "baker", "age" : 50 }
> 

2011年10月30日日曜日

統計計算言語 R を試す

統計処理を扱う言語としては、商用のS、S-Plusが有名だが、オープンソースのRも負けていない。先日も丸善の数学コーナに顔を出したが、本棚の二段分はR関連の本が並んでいます。非常に多機能だ。まだ、チュートリアルを試してみただけだが、その可能性に驚いています。

R ホームページ
http://www.R-project.org/

テキストからのデータの取込み、データ構造として、ベクトル、マトリックス、リスト、複数のデータ形式を扱うデータフレームなどのデータ構造を扱うことができる。各種のグラフの出力も可能。手続きはインタラクティブに操作できるほか、もちろん、バッチ処理可能。Windows, Linux , MacOSXのパッケージが存在しています。

普通の人がデータ処理を扱うソフトとしては、ExcelやAccessが有名だが、Excelは、ドキュメント作成ソフトに成り下がっており、Accessは、アプリケーション開発環境に自分の行く末を決めたようだ。大量の企業データを黙々と自動的に処理し、有意義な結果を導き出すツールとして、Rは非常に好都合なのだ。

さて、インストール

OSXへのインストールは簡単。以下のサイトからダウンロードして、インストール・パッケージを実行。もちろん、他のOSもある。
http://cran.md.tsukuba.ac.jp/
すでに、Lionでの検証も済んでいるようで、すばやいなあ。

実行は、一文字 r と打てばOK
baker$ r

R version 2.13.2 (2011-09-30)
Copyright (C) 2011 The R Foundation for Statistical Computing
ISBN 3-900051-07-0
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)
Rは、自由なソフトウェアであり、「完全に無保証」です。 
一定の条件に従えば、自由にこれを再配布することができます。 
配布条件の詳細に関しては、'license()'あるいは'licence()'と入力してください。 

Rは多くの貢献者による共同プロジェクトです。 
詳しくは'contributors()'と入力してください。 
また、RやRのパッケージを出版物で引用する際の形式については 
'citation()'と入力してください。 

'demo()'と入力すればデモをみることができます。 
'help()'とすればオンラインヘルプが出ます。 
'help.start()'でHTMLブラウザによるヘルプがみられます。 
'q()'と入力すればRを終了します。 

データファイルの取込から演算まで

総務省・統計局のダウンロードから、「平成17年の福岡県人口統計」を入手。
http://www.stat.go.jp/data/guide/download/index.htm

本当は時系列データのほうが、面白そうなのだけど、この手のデータはワークシート毎に年度でまとめられている。とりあえず、平成17年の市町村別人口統計を使ってみる。

Appleご用達のNumberで整形。あまり、使っていないので使い方が判らん。最終的に、一行目をヘッダー(項目名)として、二行目以降をデータ部とする典型的なデータファイルにした。

なお、一行目の項目名は、以下のようにした。(日本語でも問題ないのだが、コンソールで操作するので、日本語変換は面倒だったため)

  • No -- 市町村番号(らしい)
  • Name -- 市町村名
  • m.u15 , f.o15 -- 男性15歳未満、女性15歳未満
  • m15.65 , f15.65 -- 15歳以上65歳以内 それぞれ男性、女性
  • m.o65, f.o65 -- 65歳以上。それぞれ、男性、女性

# 変数 fukuoka に、csvファイル "fukuoka-h17-x.csv"を読み込む read.csv()
> fukuoka <- read.csv("fukuoka-h17-x.csv")

fukuokaは、マトリックス型で登録されています。マトリックス型は、行列です。

> fukuoka
      NO     Name m.u15 m15.64 m.o65 f.u15 f15.64 f.o65
1  40101   門司区  6618  31509 11515  6288  34673 18044
2  40103   若松区  6081  26608  8181  5876  28214 12357
3  40105   戸畑区  4044  21171  5714  3896  20130  8743
4  40106 小倉北区 10798  58329 15939 10461  63276 24305
5  40107 小倉南区 16805  68123 17238 15836  73155 23348
6  40108 八幡東区  4154  22722  8345  3959  23557 13077
(省略)

このあたり、ちょっと疑問。チュートリアルでは、ベクトルもマトリックスも、その要素は全て同じ型です。数字と文字を混ぜることができません。なので、それを可能にするためにデータフレームがあるのですが、上記のように変数fukuokaには、一見 文字と数字が混在しているようにみえます。

実は、この取込は一度失敗しています。取込ファイルの各要素が”double quotation”で囲まれていたため、全て文字として扱われてしまい、演算ができなくなりました。もしかすると、上記の状態はマトリックス型なのかもしれません。

要素の名称を調べてみます。

> dimnames(fukuoka)
[[1]]
 [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13" "14" "15"
[16] "16" "17" "18" "19" "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" "30"
[31] "31" "32" "33" "34" "35" "36" "37" "38" "39" "40" "41" "42" "43" "44" "45"
[46] "46" "47" "48" "49" "50" "51" "52" "53" "54" "55" "56" "57" "58" "59" "60"
[61] "61" "62" "63" "64" "65" "66" "67" "68" "69" "70" "71" "72" "73" "74" "75"
[76] "76" "77" "78" "79" "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" "90"
[91] "91" "92" "93" "94" "95" "96" "97"

[[2]]
[1] "NO"     "Name"   "m.u15"  "m15.64" "m.o65"  "f.u15"  "f15.64" "f.o65" 

[[1]]は、縦の要素をしめしています。特に指定していないため、登録順に番号が夫番されているようです。例えば、fukuoka[18,]と指定すると、18番目の行のデータすべてが表示されます。
[[2]]は、列要素の名前。csvファイルの一行目が指定されています。
名前要素は、例えば fukuoka$Name とすると、Name列の値一覧を表示しますし、Name列のみの行列(1列、x行の行列)として取り扱うことができます。
ただ、いちいち変数名を指定するのも面倒なので、attach()してやると、直接列名を使うことができます。つまり、fukuoka$f.u15 + fukuoka$m.u15は、 f.u15 + m.u15 と書けます。

以下は、attach()して、15歳以上65歳以下の比率を計算し、変数 rate.workersに格納しています。

> attach(fukuoka)
> workers <- m15.64 + f15.64
> total <- m.u15 + f.u15 + m.o65 + f.o65 + workers
> rate.workers<-workers/total

そうそう、Rの変数名は、.が使えますが、-+/*はつかえません。これらは、演算として処理されます。csvで取り込んだ時には、ご丁寧に、-は、.に置換されていました。(ヘッダー行のみ)

グラフをプロットする

簡単にできます。OSXでは、X11アプリケーションが起動し、表示します。

> hist(rate.workers , main="Histgram Rate of workers")



バッチ処理

処理を記述したファイルを実行することができる。他の言語などと組み合わせて利用すると、とても面白いことができそうだ。

以下のスクリプトは、パラメータを受け取って、上記の計算を実行した後、グラフはpdfに出力するようになっている。注意しなければならないのは、引数の扱い。引数は、ベクトルargsにセットするが、その添え字は、プログラム本体を1番目して順に格納されることだ。なんか、--argsオプションが意味ないような気がするが、このオプションのお陰で、それ以降の値は、何らかの意味を持たない(例えばオプションではない)値としてプログラムには無視されるのだろう。また、引数は文字列型なので、数字で扱いたい場合は、変換する必要がある。

# file name test.R
# パラメータを受け取る
args <- commandArgs()
# なぜ五番目なのかは、あとのコマンドをみてほしい
file <- args[5]
# pdfファイルを用意する。この時点でグラフの出力先は変更される。
pdf(file)

fukuoka <- read.csv("fukuoka-h17-x.csv")
attach(fukuoka)
workers <-m15.64 + f15.64
total <- m.u15 + f.u15 + m.o65 + f.o65 + workers
rate.workers<-workers/total
hist(rate.workers , main="Histgram Rate of workers")

# たぶん、出力デバイスの終了 pdfファイルのクローズか。
dev.off()

以下のように実行します。vanillaオプションはバッチモードを指定し、quietオプションは標準出力の制御、argsオプション以降にパラメータを書きます。Rは、基本インタラクティブなプログラムらしく、標準入力にスクリプトを食わせます。

> R --vanilla --quiet --args test1.pdf < test.R 

Rは、歴史もあり、機能も豊富で、多くのノウハウも公開されています。これからの勉強や仕事への応用がとても楽しいことになりそうです。