読者です 読者をやめる 読者になる 読者になる

TokyoR #45で「R for Cloud Computing」という本の紹介をしました。

LT にて、最近執筆を協力した「R for Cloud Computing」という本の紹介をしました。

スライドはこちらになります。

主催者の皆さん、会場を提供していただいた、VOYAGE GROUP さんどうもありがとうございました。

R for Cloud Computing: An Approach for Data Scientists

R for Cloud Computing: An Approach for Data Scientists

installr パッケージで R のバージョンアップ

installr パッケージWindows 版 R のバージョンアップや Rstudio や git などの外部アプリケーションのインストールが R から行える R パッケージで、R-bloggers の管理人でもある Tal Galili 氏によって開発されています。

また、R のアンインストール、OS のリブート、RGui と RStudio のどちらからから実行しているかの確認など、インストール関連の機能も有しています。

この記事では installr を使って R のバージョンを行う方法と installr パッケージで Rstudio をインストールする方法を紹介します。

installr パッケージをインストールし、ロードする

R を起動し、下記コマンドを実行します。

install.packages("installr")
library(installr)

R のバージョンアップ

updateR 関数にてバージョンアップを行えます。本例では下記のコマンドを実行しました。

updateR(browse_news=T, install_R=T, copy_packages=T, keep_old_packages=T, update_packagesv, start_new_R=T, quit_R=T)

各引数は以下を示します。

  • browse_news: ブラウザで最新版の R の NEWS (リリースノート) を表示するか。
  • install_R: R をインストールするか。
  • copy_packages: 古いバージョンから新しいバージョンへパッケージを引き継ぐかどうか。
  • update_packages: 新しいバージョンの R でパッケージのアップデートを行うかどうか。
  • keep_old_packages: 古いバージョンの R を残すかどうか。
  • start_new_R: 古いバージョンのRを終了した後、新しいバージョンの R を起動する (64bit 版 R を優先で起動する)
  • quit_R: インストール後に自動的に R を終了するかどうか。

全て引数なしで実行した場合はダイアログボックスにて尋ねられます。

updateR()

f:id:hiratake55:20140505182652p:plain

コマンド実行後、自動的にダウンロードが開始し、ダウンロード完了後、インストーラが起動します。

f:id:hiratake55:20140505182708p:plain

RStudio のインストール

RStudio のインストールは install.RStudio 関数を実行します。

install.RStudio()

ダウンロード後、RStudio のインストーラが自動的に起動します。

f:id:hiratake55:20140505182718p:plain

OS をロック状態にする

os.lock コマンドで OS を ロックし、Windows のログオン画面に遷移します。

os.lock()

まとめ

本記事では、installr パッケージで R のバージョンアップと RStudio のインストールを行う方法を概説しました。また、OSをロック状態にする方法を参考までに紹介しました。

installr パッケージは最新版の R や RStudio を毎回サイトからダウンロードし、インストールする手間が省けるだけでなく、installr でインストールできる外部アプリケーションには RStudio や git の他、Rtools, MikTex, ffmpegImageMagick などレポート作成などに必要なアプリケーションがあり、ワークステーションの環境構築やバージョンアップ時にとても有用なパッケージだと思われます。

参考情報

Force.com と R 言語でビッグデータ統計分析 – Salesforce.com のお客様の声をデータマイニング -

この記事では、統計分析環境 R と R パッケージの RForcecom を用いて、Salesforce.com に格納された顧客情報データからデータマイニングを行う手順についてサンプルケースを用いて説明します。

Force.com (Salesforce.com) とは

Salesforce.com とは、セールスフォース・ドット・コム社によって提供されている SaaS 型の CRM (顧客情報管理システム) です。類似の SaaS サービスでは、Microsoft 社の Dynamics CRM、ソフトウェアパッケージとしては、Oracle 社の Siebel、SAP 社の SAP CRM、OSS の SugerCRM などが有名です。

f:id:hiratake55:20131220013754p:plain Salesforce.com の画面イメージ

Force.com は、Salesforce.com から顧客管理に特化した機能を取り除いた機能削減バージョン (PaaS) で、Force.com と Salesforce.com は基本的な機能 (各種データ管理、Apex, VisualForce, 各種 API 等) が共通化されています。

統計分析環境 R とは

統計分析環境 R とは、データ操作と統計分析、データ可視化のためのオープンソースソフトウェアです。R では、R 言語によるスクリプト操作にて各種の分析を行います。類似の商用製品では SAS 社の SASIBM 社の SPSS などが挙げられます。

R の特徴の1つとして、有志によって作成されたアドオンパッケージが CRAN (The Comprehensive R Archive Network) レポジトリに 5,000 以上登録されており、非常に多くの種類の統計分析が行え、分析機能の網羅性に関しては数ある統計分析ソフトウェアの中でも群を抜いてトップといえます。

f:id:hiratake55:20131221133349p:plain R の統合開発環境 RStudio の画面イメージ

RForcecom とは

RForcecom は、R から Force.com (Salesforce.com) のデータを抽出したり、R 上からのデータの登録、更新、削除、アップサートが可能な R パッケージで、私が開発、メンテナンスを行っています。2012 年 4 月に初回バージョンをリリースして以来、バージョンアップを重ねて現在に至ります。

f:id:hiratake55:20131221141959p:plain RForcecom パッケージの概要

今回の分析ケース (Salesforce.com のお客様の声を統計分析)

分析の概要

今回の分析は、顧客管理システムに Salesforce.com を利用している企業で、コールセンターのオペレーターより登録された「お客様の声」を R で日本語形態素解析自然言語処理し、統計分析により頻出キーワード、急上昇したキーワードを抽出し可視化します。

f:id:hiratake55:20131221124405p:plain 今回の分析ケースのイメージ

今回使用するデータ

本例では、実際のデータを利用することは困難であるため、代替として、Twitter によるアクティブサポート (お客様からのお問い合わせ窓口として Twitter を活用) を実施している企業として、ソフトバンク社の Twitter アカウント (@SBCare) 宛のツイートログを一定期間クロールし、その内容を Salesforce.com に登録しました。

1. Salesforce.com に登録されたデータを確認

クロールしたデータは、以下のように Salesforce.com に登録されています。

f:id:hiratake55:20131221124521p:plain Salesforce.com のレコード一覧画面

f:id:hiratake55:20131221124537p:plain Salesforce.com のレコード詳細画面

2. RForcecom パッケージで Salesforce.com からデータを取得する

RForcecom パッケージの インストール

R から下記コードを実行し、RForcecom パッケージをインストール、ロードします。

# RForcecom パッケージのインストール・ロード
install.packages("RForcecom")
library(RForcecom)

Salesforce.com へのログイン

下記コードを実行し、Salesforce.com にログインします。パスワードの後ろにセキュリティトークンを忘れずに付与してください。

# ユーザ名
username <- "<YOUR_EMAIL_ADDRESS>"
# パスワード+セキュリティトークン
password <- "<PASSWORD><SECURITY_TOKEN>"
# インスタンスのホスト名 (ap0, ap1, na14 等)
instanceURL <- "https://<YOUR_SFDC_INSTANCE>.salesforce.com/"
# API のバージョン (2013/12 時点の最新版は 29.0)
apiVersion <- "29.0"
# ログインし、セッション ID を取得
session <- rforcecom.login(username, password, instanceURL, apiVersion)

データセットの取得

rforcecom.retrieve 関数にて、Salesforce.com のデータをロードします。

# CustomerVoice__c オブジェクトの "Tweet__c", "TweetDate__c" フィールドを取得
CustomerVoice <- rforcecom.retrieve(session,"CustomerVoice__c",c("Tweet__c","TweetDate__c"))

head 関数にて取得出来たことを確認します。

# 取得したデータの先頭 6 行を表示
head(CustomerVoice$Tweet__c)

f:id:hiratake55:20131221133350p:plain Salesforce.com より取得したデータ

データサイズによっては数分かかる場合があります。データサイズが大きすぎる場合は、抽出期間を設定してデータを絞り込むような工夫が必要かと思われます。

3. MeCab を利用して日本語形態素解析を行う

MeCab は工藤拓氏によって開発された日本語形態素解析を行うためのソフトウェアです。本例では、MeCab を用いて、文章から単語への品詞分解を行います。

MeCab のインストール

MeCab のサイトから、ページ中ほどの mecab-0.996.exe をダウンロード、インストールします。

f:id:hiratake55:20131220011513p:plain MeCab ダウンロードページ

RMeCab パッケージのインストール

続いて R から MeCab を実行する RMeCab パッケージ をインストールします。

# RMeCab パッケージのインストール・ロード
install.packages ("RMeCab", repos = "http://rmecab.jp/R")
library(RMeCab)

取得したデータセットから Twitter ID と URLを除去

前手順にて Salesforce.com から取得したデータセットには本文中に Twitter ID や URL が含まれていますが、本分析では不要ですので gsub 関数を利用して正規表現で除去します。

# Twitter ID を除去
CustomerVoice$Tweet__c <- gsub("@[0-9a-zA-Z_]+\\s*","",CustomerVoice$Tweet__c)
# 「RT」の文言を除去
CustomerVoice$Tweet__c <- gsub("RT\\s*:\\s+","",CustomerVoice$Tweet__c)
# 本文中の URL を除去
CustomerVoice$Tweet__c <- gsub("https?://t.co/[0-9a-zA-Z\\._]*","",CustomerVoice$Tweet__c)

データセットを一時ディレクトリに書き出し

RMeCab を利用するにはには、分析用のデータセットを一旦、一時ディレクトリに書き出す必要があります。

# 一時ファイル名を取得
filename <- tempfile("CustomerVoice")
# ファイルに書き出し
write.table(CustomerVoice$Tweet__c,
            file=filename,
            row.names=F,
            col.names=F,
            fileEncoding="shift-jis",
            quote=F)

形態素解析と頻出語の抽出

RMeCabFreq 関数を用いて品詞分解を行い、その出現頻度をカウントします。本例では、名詞に限定して品詞抽出を行いました。

# 品詞分解と単語の出現頻度をカウント
freq <- RMeCabFreq(filename)
# 名詞のみを抽出
freq <- freq[freq$Info1=="名詞",]

抽出結果は以下のようになりました。

# 抽出結果の先頭 12 行を表示
head(freq[order(freq$Freq, decreasing=T),], n=12)

f:id:hiratake55:20131221133351p:plain 抽出結果のイメージ

この状態では、記号や数字など無意味な単語が並んでおり分析に適さないため、データクレンジングを行い不要な単語を除去し意味のある単語のみを残します。

# 数名詞、非自立名詞、接尾名詞を除去
freq <- freq[!freq$Info2 %in% c("数","非自立","接尾"),]
# 2 文字以上の単語のみを抽出
freq <- freq[nchar(freq$Term)>2,]
# 異なる品詞、同じ単語を整理
freq.term<-unique(freq$Term)
freq.sum <- lapply(freq.term,function(x){sum(freq[freq$Term==x,]$Freq)})
# データフレームに格納
freq <- data.frame(Term=freq.term,Freq=unlist(freq.sum))

4. ワードクラウドとして特徴語を可視化する

wordcloud パッケージのインストール

ワードクラウドを出力する wordcloud パッケージ をインストール、ロードします。

# wordcloud パッケージのインストール・ロード
install.packages("wordcloud")
library(wordcloud)

ワードクラウドの出力

# フォントリストに Meiryo UI を追加
windowsFonts(MeiryoUI = "Meiryo UI")
# 頻度の高い順に並び替え
freq <- freq[order(freq$Freq, decreasing=T),]
# 頻度の上位 100 件を抽出
freq.head<-head(freq, n=100)
# カラースケールを Dark2 に設定
pal <- brewer.pal(8, "Dark2")
# wordcount 関数でプロット
wordcloud(freq.head$Term, freq.head$Freq, random.color=T, colors=pal, family="MeiryoUI")

Wordcloud の出力結果

以下のように頻出語が大きな文字で出力され一目で頻出語が把握できます、なお文字色はランダムで選択しているため意味は持ちません。

f:id:hiratake55:20131221133352p:plain

5. TF-IDF 法による特徴語抽出

TF-IDF 法とは文書分類や特徴語の抽出に用いられるアルゴリズムで、文書中の単語 i の出現頻度と単語 i を含む文書数の関係から、特定の文書に高い頻度で出現する単語を抽出します。TF-IDF の計算方法は以下サイトに詳しく書かれています。

tf–idf - Wikipedia, the free encyclopedia

本例では、2013-12-10 ~ 2013-12-16 の 1 週間分の Tweet ログを全ての文書、各日の Tweet ログを対象の文書として、各日に頻出したキーワードを抽出します。

各日のデータセットの取得

RForcecom パッケージの rforcecom.query 関数を利用して、SOQL クエリを実行しデータを取得します。

# 12/10 - 12/16 の各日のデータセットを取得
CustomerVoice.1210 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-10T00:00:00+09:00 and TweetDate__c < 2013-12-11T00:00:00+09:00")
CustomerVoice.1211 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-11T00:00:00+09:00 and TweetDate__c < 2013-12-12T00:00:00+09:00")
CustomerVoice.1212 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-12T00:00:00+09:00 and TweetDate__c < 2013-12-13T00:00:00+09:00")
CustomerVoice.1213 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-13T00:00:00+09:00 and TweetDate__c < 2013-12-14T00:00:00+09:00")
CustomerVoice.1214 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-14T00:00:00+09:00 and TweetDate__c < 2013-12-15T00:00:00+09:00")
CustomerVoice.1215 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-15T00:00:00+09:00 and TweetDate__c < 2013-12-16T00:00:00+09:00")
CustomerVoice.1216 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-16T00:00:00+09:00 and TweetDate__c < 2013-12-17T00:00:00+09:00")
# 7 日分のデータセットを結合したデータセットを作成
CustomerVoice.all <- rbind(CustomerVoice.1210,
                           CustomerVoice.1211,
                           CustomerVoice.1212,
                           CustomerVoice.1213,
                           CustomerVoice.1214,
                           CustomerVoice.1215,
                           CustomerVoice.1216)

単語の品詞分解と出現頻度のカウント

# 単語を品詞分解し、出現頻度を出力する関数
calc.wordfreq <- function(CustomerVoice){
  # Twitter ID を除去
  CustomerVoice$Tweet__c <- gsub("@[0-9a-zA-Z_]+\\s*","",CustomerVoice$Tweet__c)
  # 「RT」の文言を除去
  CustomerVoice$Tweet__c <- gsub("RT\\s*:\\s+","",CustomerVoice$Tweet__c)
  # 本文中の URL を除去
  CustomerVoice$Tweet__c <- gsub("https?://t.co/[0-9a-zA-Z\\._]*","",CustomerVoice$Tweet__c)
  # 一時ファイル名を取得
  filename <- tempfile("CustomerVoice")
  # ファイルに書き出し
  write.table(CustomerVoice$Tweet__c,
              file=filename,
              row.names=F,
              col.names=F,
              fileEncoding="shift-jis",
              quote=F)
  # 品詞分解と単語の出現頻度をカウント
  freq <- RMeCabFreq(filename)
  # 名詞のみを抽出
  freq <- freq[freq$Info1=="名詞",]
  # 数名詞、非自立名詞、接尾名詞を除去
  freq <- freq[!freq$Info2 %in% c("数","非自立","接尾"),]
  # 2 文字以上の単語のみを抽出
  freq <- freq[nchar(freq$Term)>2,]
  # 異なる品詞、同じ単語を整理
  freq.term<-unique(freq$Term)
  freq.sum <- lapply(freq.term,function(x){sum(freq[freq$Term==x,]$Freq)})  
  # データフレームに格納
 freq <- data.frame(Term=freq.term,Freq=unlist(freq.sum),stringsAsFactors=F)
  return(freq)
}
# 各日のデータフレームに対して単語の出現頻度を算出
freq.1210 <- calc.wordfreq(CustomerVoice.1210)
freq.1211 <- calc.wordfreq(CustomerVoice.1211)
freq.1212 <- calc.wordfreq(CustomerVoice.1212)
freq.1213 <- calc.wordfreq(CustomerVoice.1213)
freq.1214 <- calc.wordfreq(CustomerVoice.1214)
freq.1215 <- calc.wordfreq(CustomerVoice.1215)
freq.1216 <- calc.wordfreq(CustomerVoice.1216)
freq.all <- calc.wordfreq(CustomerVoice.all)

IDF 値を求める

# 各単語が出現する文書の数をカウント
IDF.documents <- sapply(freq.all$Term,function(x){
  sum(
    nrow(freq.1210[freq.1210$Term==x,])>0,
    nrow(freq.1211[freq.1211$Term==x,])>0,
    nrow(freq.1212[freq.1212$Term==x,])>0,
    nrow(freq.1213[freq.1213$Term==x,])>0,
    nrow(freq.1214[freq.1214$Term==x,])>0,
    nrow(freq.1215[freq.1215$Term==x,])>0,
    nrow(freq.1216[freq.1216$Term==x,])>0
  )
})
# データフレームに格納する
IDF<-data.frame(Term=freq.all$Term,IDF=log(7/IDF.documents))

TF-IDF 値を求める

# TF 値を求め、TF-IDF 値を計算して返す関数を作成
calc.tfidf <- function(freq){
  # TF 値を求める
  freq$TF <- freq$Freq/sum(freq$Freq)
  # TF-IDF 値を求める
  tfidf.val <- lapply(freq$Term,function(x){
    tf_i <- freq[freq$Term==x,]$TF
    idf_i <- IDF[IDF$Term==x,]$IDF
    return(tf_i * idf_i)
  })
  # 結果をデータフレームに格納する
  tdidf <- data.frame(Term=freq$Term, TFIDF=unlist(tfidf.val))
  return(tdidf)
}
# 各日ごとの TF-IDF 値を求める
tfidf.1210 <- calc.tfidf(freq.1210)
tfidf.1211 <- calc.tfidf(freq.1211)
tfidf.1212 <- calc.tfidf(freq.1212)
tfidf.1213 <- calc.tfidf(freq.1213)
tfidf.1214 <- calc.tfidf(freq.1214)
tfidf.1215 <- calc.tfidf(freq.1215)
tfidf.1216 <- calc.tfidf(freq.1216)

ワードクラウドを出力する

各日のワードクラウドを出力し、PNG 形式で保存します。

# フォントリストに Meiryo UI を追加
windowsFonts(MeiryoUI="Meiryo UI")
# Wordcloud を出力する関数を作成
draw.wordcloud <- function(tfidf,title=""){
  # ファイル名、出力サイズの指定
  png.filename <- paste("wordcloud-", title,".png", sep="")
  png(png.filename,width=7,height=7,units="in", res=600)
  # データセットの上位 100 件を抽出
  tfidf <- tfidf[order(tfidf$TFIDF, decreasing=T),]
  tfidf.head <- head(tfidf, n=200)
  # 上部の余白を設定
  par(oma = c(0, 1, 2, 1))
  # Wordcloud を出力
  wordcloud(tfidf.head$Term,tfidf.head$TFIDF,random.color=T,colors=pal,family="MeiryoUI",main=title)
  # 上部の余白を元に戻す
  par(oma = c(0, 0, 0, 0))
  # タイトルを付与
  title(title)
  # ファイルに保存
  dev.off()
}
# 各日の Wordcloud を描画
draw.wordcloud(tfidf.1210,title="2013-12-10")
draw.wordcloud(tfidf.1211,title="2013-12-11")
draw.wordcloud(tfidf.1212,title="2013-12-12")
draw.wordcloud(tfidf.1213,title="2013-12-13")
draw.wordcloud(tfidf.1214,title="2013-12-14")
draw.wordcloud(tfidf.1215,title="2013-12-15")
draw.wordcloud(tfidf.1216,title="2013-12-16")

ワードクラウド出力結果

ワードクラウドの出力結果は以下で、図からいくつかの各日ごとに特徴的な傾向があることが推測されます。

f:id:hiratake55:20131220121851p:plain:w480 12/10(火) は、「待ち時間」「一括払い」などのショップ窓口に関する相談が多かったことが推測されます。

f:id:hiratake55:20131220121852p:plain:w480 12/11(水) は、「フレッツ」「スティック(データ通信端末?)」などの固定インターネット、データ通信に関する相談が多かったことが推測されます

f:id:hiratake55:20131220121853p:plain:w480 12/12(木)は、「トンネル」、「ネットワーク」などの回線品質、通話エリアに関する相談が多かったことが推測されます

f:id:hiratake55:20131220121854p:plain:w480 12/13(金)は、「お子さま」など、キッズ向けケータイに関する相談が多かったことが推測されます

f:id:hiratake55:20131220121855p:plain:w480 12/14(土)は、「三ノ宮」、「出来島」、「新鎌ヶ谷」など特定のエリアまたはショップに関する相談が多かったことが推測されます。

f:id:hiratake55:20131220121856p:plain:w480 12/15(日)は、「スリープ」、「Wifi」など、スマートフォンの利用方法に関する相談が多かったことが推測されます。

f:id:hiratake55:20131220121857p:plain:w480 12/16(月)は、「セールス」、「直営店」、「値引き」など対面での応対に関する相談が多かったことが推測されます。

まとめ

本分析で行ったこと

本記事では、R 言語を利用して Salesforce.com に格納されたデータを MeCab により品詞分解し、TF-IDF 法にて特徴語を抽出しました。その結果をワードクラウドとして可視化したところ、日毎に異なる結果が得られました。

今回は 1 週間分の Tweet ログをもとに各日の特徴語を抽出しましたが、数か月以上長期間のデータを収集し週毎、曜日毎の分割により今回とは異なった角度から傾向や更に深い内容の示唆が得られる可能性があります。

辞書 (コーパス) について

今回は MeCab バンドルの辞書を使って形態素解析を行いましたが、独自に辞書を作成し、用語やサービス名を登録しておくことでより高精度な分析、有用な情報が得られると考えます。

(「フィーチャーフォン、ガラケー」「アップル、Apple」を同じ言葉としてカウントする、「"パケットし放題 for 4G", "パケットし放題フラット for 4G"等」をそれぞれ別々の 1 つの言葉として扱うなど)

本分析の応用例

応用例としては、以下の様な分析シナリオが考えられます。

  • センチメント分析(感情分析)などによるクレーム顧客の自動判定
  • 新商品、新サービスに関するお客様からのフィードバックのサマリレポート作成
  • 不良品、リコール対象となりうる可能性のある製品の早期発見
  • お客様からの返信内容の分析による担当者、サービスの対応品質の評価

さいごに

※この記事は Force.com advent Calendar 2013 の 12/21 分の記事です。

※今回利用した @SBCare 宛の Twitter の発言履歴は著作権保護の観点から分析後、Salesforce.com からデータを削除しております。

R と Treasure Data で Web サーバのアクセスログ解析

この記事では、Apache HTTP Server で稼働している Web サーバへ Fluentd (td-agent) を導入し、Treasure Data でアクセスログを収集し、R で収集したデータのアクセスログ解析を行うまでの手順を解説します。

Treasure Data (トレジャーデータ) とは

Treasure Data は Cloud 型 DWH (Data Warehouse) サービスの一つで、Cloud 上で SaaSとしてビッグデータの格納、処理を利用できるサービスです。 (Treasure Data 社では BigData-as-a-Service と呼んでいます)

同様のサービスでは、Amazon Web ServicesAmazon RedshiftElastic MapReduce 等がありますが、Treasure Data の特長で、バッチで大量のデータをバルクロードするのはもちろん、fluentd (td-agent) を用いて各種ログデータを効率よく送信し、格納できることから、リアルタイムで出力されるログデータの格納に向いている特長があります。

具体的には、以下のようなデータです。

  • Web サーバのアクセスログやアプリログ
  • 各種サーバやネットワーク機器から出力される Syslog
  • POS、EC サイトやオンラインゲームの購買履歴
  • 自動車や組み込み機器、工作機械等マシンデータ等のシリアルポート等から得られるログ

f:id:hiratake55:20131204233157p:plain

Treasure Data の活用イメージ

今回の分析例

今回の分析例は Apache で稼働している Web サーバのログを Treasure Data に転送するよう設定し、R を用いて Treasure Data に格納されたデータの抽出を行い、Rを使ってグラフ化するものです。

本例では、データソースにおなじみの統計分析ソフトウェア R 専門検索エンジンseekRをストアしているサーバのアクセスログを使用しました。

f:id:hiratake55:20131204233221p:plain

分析処理のフロー(イメージ)

今回使用した Web サーバの環境

本例で、取得元として使用した Web サーバの環境は以下です。

1. Treasure Data に テーブル を作成

Treasure Data へのユーザ登録

まず、Treasure Data 社サイトにアクセスし、ユーザ登録を行います。登録完了後、Treasure Data の Console にログインします。

DB とテーブル の作成

Console にログイン後、以下画面にて DB を作成します。本例では、「apache」という名前の DB を作成しました。

f:id:hiratake55:20131205162325p:plain

次にテーブルを作成します。本例では、「access」というテーブルを作成しました。

f:id:hiratake55:20131205162327p:plain

なお、Treasure Data では Column を自動的に認識し必要に応じて追加する機能を持っているため、Column を手動で作成する必要はありません。(もちろん、通常の RDBMS と同様に手動で作成することも可能です)

2. Web サーバ に fluentd (td-agent) をインストール

Treasure Data のクライアントである fluentd (td-agent) をログ採取対象の Web サーバにインストールする必要があります。なお、td-agent と fluentd の違いですが、Treasure Data 社によると、td-agent は fluentd の機能強化版、安定版という位置づけです。

手順は以下のサイトを参考にしました。

Installing Fluentd Using rpm Package | Fluentd

td-agent のインストール

curl -L http://toolbelt.treasuredata.com/sh/install-redhat.sh | sh

Apache アクセスログの Permission 変更

ログディレクトリを other (他ユーザ) でも Read できるよう、パーミッションを変更します。

chmod 0755 /var/log/httpd

設定ファイル作成

設定ファイルにて、Source (ログの場所) と Match (ログ抽出条件) を指定します。

vi /etc/td-agent/td-agent.conf
<source>
  type tail
  path /var/log/httpd-access.log #Apacheのアクセスログの場所
  pos_file /var/log/td-agent/httpd-access.log.pos # Position ファイルの場所
  tag apache.access #格納先の DB 名、テーブル名
  format apache2 # フォーマットをApache2形式に設定 (※)
</source>

<match **>
  type tdlog
  apikey <Treasure Data API key> # Trasure data の APIキー
  auto_create_table
  buffer_type file
  buffer_path /var/log/td-agent/buffer/td
</match>

(※) Format は以下のようにカスタムで定義することも可能です。私は Apache のカスタムログで以下のようにバーチャルホスト名をログに組み込んでいるため、Format を独自に定義しています。

httpd.conf

LogFormat "%V %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

td-agent.conf

format /^(?<vhost>[^ ]*) (?<host>[^ ]*) (?<logname>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/
time_format %d/%b/%Y:%H:%M:%S %z

サービス起動、自動起動設定

service td-agent start
chkconfig td-agent on

Console からデータが登録されたことを確認する

Fluentd の仕組み上、反映されるまでに少々時間がかかります。データ反映後、Console から以下のようにデータが参照できます。

f:id:hiratake55:20131205075817p:plain

3. R から Treasure Dataに接続

R からは、RJDBC 経由で Treasure Data に JDBC 接続します。 RJDBC は内部で rJava パッケージを用いており、事前に、Oracle 社サイトから適切な bit の JRE をインストールしておく必要があります。

Treasure Dataに接続する為の R コードは以下です。今回、約 14 万件のレコードから抽出処理を行いましたが、Hive の抽出、集計処理に要した時間はおよそ 1-2 分 でした。

Treasure Data の JDBC ドライバをダウンロードする

下記ページから Treasure Data の JDBC ドライバをダウンロードして、ローカルに保存します。

rJava, RJDBC パッケージをインストールする

install.packages("rJava")
install.packages("RJDBC")

RJDBC パッケージを使って Treasure Data に接続する

library(rJava)
library(RJDBC)
drv <- JDBC("com.treasure_data.jdbc.TreasureDataDriver",
            "./lib/td-jdbc-0.2.3-jar-with-dependencies.jar", # JDBCドライバの場所
            identifier.quote="`")
conn <- dbConnect(drv,
                  "jdbc:td://api.treasure-data.com/<DB name>",
                  "<登録した Email Address>",
                  "<登録した Password>")

HiveQL を利用してデータセットを取得する

HiveQL は HadoopMySQL ライクな SQLHadoop からデータの抽出が行える言語です。HiveQL を利用してデータセットを抽出します。

# HTTP レスポンスコード別のログ件数を取得する
> dataset.code <- dbGetQuery(conn, "SELECT code, count(1) FROM access where vhost='seekr.jp' group by code order by code desc")
> dataset.code
  code  cnt
1  200 9701
2  304 1264
3  404  245
4  206   93

# 日付別のログ件数を取得する
> dataset.date<-dbGetQuery(conn, "SELECT substr(from_unixtime(time),1,10) as dt, count(1) cnt FROM access where vhost='seekr.jp' group by substr(from_unixtime(time),1,10) order by dt")
> head(dataset.date)
           dt cnt
1  2013-11-12 126
2  2013-11-13 561
3  2013-11-14 435
4  2013-11-15 636
5  2013-11-16 248
6  2013-11-17 425

# 時間別のログ件数を取得する
> dataset.hr<-dbGetQuery(conn, "SELECT substr(from_unixtime(time),12,2) hr, count(1) cnt FROM access where vhost='seekr.jp' group by substr(from_unixtime(time),12,2) order by hr")
> head(dataset.hr)
   hr cnt
1  00 719
2  01 627
3  02 649
4  03 665
5  04 693
6  05 849

4. googleVis パッケージを利用してグラフ描画する

今回、レポーティング用途で扱うケースを想定し、グラフ描画には標準の plot 関数ではなく、GoogleVis パッケージを使用してみました。googleVis パッケージでは、Google Charts を用いて、高度なグラフを出力するものです。

# パッケージのロード
install.packages("googleVis")
library(googlevis)

# 折れ線グラフで表示 
chart.line <- gvisLineChart(dataset.date, options=list(height=400))

# 棒グラフで表示 
chart.bar <- gvisBarChart(dataset.hr, options=list(height=400))

# 円グラフで表示
chart.pie <- gvisPieChart(dataset.code, options=list(height=280))

# 折れ線グラフと棒グラフを結合
charts.top <- gvisMerge(chart.line,chart.bar,horizontal=T)

# 上記と円グラフを結合
charts <- gvisMerge(charts.top,chart.pie,horizontal=F)

# 描画
plot(charts)

plot 関数を実行後、Web ブラウザが起動し、出力結果を参照できます。チャート内では、オンマウスでラベルを表示するなどといった使い方が可能です。

f:id:hiratake55:20131205082435p:plain

※ 右上の図で深夜帯がピークになっているのは、Amazon EC2 (Amazon Linux) の Timezone を日本時間に設定するのを忘れているためです^^;

まとめ

本記事では、Treasure Data でのテーブル作成、Web サーバへの fluentd のインストール、R から Treasure Data にアクセスする方法、googleVis パッケージを利用してグラフを出力する手順を解説しました。

この仕組みを応用して Web サーバのアクセスログをもとに、以下のような分析を実施し Web サービスのパフォーマンス最適化やキャパシティプランニング等に利活用できると考えられます。

  • 異常アクセスの早期検知
  • 時系列解析によるアクセス頻度の高い期間の周期性発見
  • アクセス急増後のアクセス減少状況モデリングと予測
  • リンク切れコンテンツの発見と可視化
  • パフォーマンス改善のための診断
  • 処理時間の長いコンテンツの抽出

※ この Post は R Advent Calendar 2013 の 12/5 分の記事です。

参考サイト

宅配便の配達状況 Google Chrome 拡張を作った

20120204231119

 

遅くなりましたが、宅配便の配達状況 Google Chrome Extension をリリースしました。 アイコン、Illustrator で作りましたが、あまり満足してないので募集中です。

Firefox, Thunderbird アドオンもあります。