RでHDFS上のデータを読み書きするパッケージを公開しました
RでHadoopを使うパッケージは、RHadoopとかRHIPEとかありますが、
単純にHDFS上のデータを読み書き出来るだけのシンプルなものが欲しかったのでパッケージを作って公開してみました。
JavaとかPigで一次集計したデータをRで読み込む時に使うイメージです。
準備
install.packages("rHadoopClient") library(rHadoopClient)
HDFS上のデータを読み込む
./tmp/iris にアップしたirisのデータを読み込む
data.hdfs <- read.hdfs("tmp/iris") names(data.hdfs) <- names(iris) head(data.hdfs)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species ## 1 5.1 3.5 1.4 0.2 setosa ## 2 4.9 3.0 1.4 0.2 setosa ## 3 4.7 3.2 1.3 0.2 setosa ## 4 4.6 3.1 1.5 0.2 setosa ## 5 5.0 3.6 1.4 0.2 setosa ## 6 5.4 3.9 1.7 0.4 setosa
HDFSにデータを書き出す
./tmp/output_irisにデータを書き出す
write.hdfs(iris, "tmp/output_iris")
$ hadoop fs -cat ./tmp/output_iris | head 5.1 3.5 1.4 0.2 setosa 4.9 3 1.4 0.2 setosa 4.7 3.2 1.3 0.2 setosa 4.6 3.1 1.5 0.2 setosa 5 3.6 1.4 0.2 setosa 5.4 3.9 1.7 0.4 setosa 4.6 3.4 1.4 0.3 setosa 5 3.4 1.5 0.2 setosa 4.4 2.9 1.4 0.2 setosa 4.9 3.1 1.5 0.1 setosa
Hiveを実行して、その結果を読み込む
irisのデータが入っているiris_tableからのデータ読み込み
data.hive <- read.hive("select * from iris_table") names(data.hive) <- names(iris) head(data.hive)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species ## 1 5.1 3.5 1.4 0.2 setosa ## 2 4.9 3.0 1.4 0.2 setosa ## 3 4.7 3.2 1.3 0.2 setosa ## 4 4.6 3.1 1.5 0.2 setosa ## 5 5.0 3.6 1.4 0.2 setosa ## 6 5.4 3.9 1.7 0.4 setosa
Rで簡単にHTMLレポートを送れるパッケージを公開しました
先日やったRでHTMLレポート配信を、パッケージ化して公開しました!
準備
- 修正版sendmailRのインストール
$ git clone git@github.com:yokkuns/sendmailR.git
$ cd sendmailR
$ sudo make install
または、パッケージを作成して普通に、install.packagesでインストール
$ make package
> install.packages("sendmailR_1.1-2.tar.gz")
- インストール
> install.packages("EasyHTMLReport", contriburl=contrib.url("http://cran.r-project.org/"))
- 読み込み
> library(EasyHTMLReport) 要求されたパッケージ sendmailR をロード中です 要求されたパッケージ base64enc をロード中です 要求されたパッケージ markdown をロード中です 要求されたパッケージ knitr をロード中です
Rmdファイルの用意
前回作った stock_anomaly_detection_sample.Rmd をそのまま使う
HTMLレポート送信!
> easyHtmlReport(rmd.file="stock_anomaly_detection_sample.Rmd", from="yokkuns@tkul.jp", to="yokkuns@tkul.jp", subject="EasyHTMLReport Sample1")
#CROSS2013で「今日から始まるデータサイエンティスト」やります!
告知が大分遅くなってしまいましたが、1/18の#CROSS2013で、
「今日から始まるデータサイエンティスト」というセッションをやらせて頂きます!
求めらるデータサイエンティストの人材像や、実際の活用について語るセッションにしたいと思っているので、興味のある方は是非ご参加下さい!
以下、概要です。
セッション概要
セッション1:『活躍するデータサイエンティストの人材像』
近年、データサイエンティストの必要性が熱く取り上げられているが、実際の現場で必要とされる人物像・必要スキルは明確ではない。
今回、実際に分析ビジネスを行う経営・マネジメント陣が、分析現場で必要とされるデータサイエンティストの人物像・必要スキルを徹底議論。明日からデータサイエンティストになりたい方はぜひご参加下さい。
スピーカー
倉橋 一成(@isseing333): セッション1進行
- 合同会社iAnalysis
- An expert on data analysis at Japan's premier university. 【iAnalysis】アイアナリシスの代表・最高解析責任者、「おとうさんの解析日記」。
佐々木 智之(@crkz666)
- 株式会社gumi
- 法人向けプロバイダ事業を展開する企業でネットワーク構築・保守業務に従事した後、PC向け海外ソフトウェアの輸入販売を手がける企業でサイト構築・保守やローカライズを担当。後にポータル事業を展開する企業に買収され、PC向けオンラインゲーム事業の立ち上げを経て2011年9月にgumi入社。主にアプリを開発・運営するスタッフのための仕組み化やシステムを考えたり作ったりしてます。
草野 隆史(@zaakya)
益村 勝将(@Masumura187)
西郷 彰
里 洋平(@yokkuns): セッション2進行
上村 崇(@t_uemura)
- 株式会社ALBERT
- ALBERTで社長をしています。分析力をコアとして情報を最適化します。データドリブンのCRMは今世界で一番ホットでセクシーな領域。そのど真ん中を突き進みます。アドテク領域でも独自開発の最適化エンジンでイノベーションを起こし続けます! http://www.albert2005.co.jp/
濱田 晃一(@hamadakoichi)
光田 健一(@mitsudaa)
- グリー株式会社
PigのPython UDFを試してみた
PythonでUDFが書けるので試してみた。
日付の差を計算するUDFを書いてみる
とりあえず、日付の差を計算するUDFを書いてみた。
udf.py
@outputSchemaで、出力のスキーマを書いて、あとは普通に書く。
#!/usr/bin/env python import time @outputSchema("datediff:int") def DateDiff(a,b): a_time = time.mktime(time.strptime(a,'%Y-%m-%d')) b_time = time.mktime(time.strptime(b,'%Y-%m-%d')) return int( (a_time - b_time) / 86400 )
date_diff_sample.pig
Register 'udf.py' using jython as myfuncs って感じで記述すると、Pythonで定義したDateDiff関数を、myfuncs.DateDiffで使う事が出来る。
Register 'udf.py' using jython as myfuncs; A = load 'dummy'; A = foreach A generate myfuncs.DateDiff('2012-12-12','2012-12-01'); dump A;
- HDFSに適当なdummyファイルを置いておくと、好きなデータを作れるので、こういうのを試すのにとても便利。
実行
$ pig -f date_diff_sample.pig
...
(11)
注意点
Pythonで書いたUDFは、カレントディレクトリにそのファイルが無いと動かない。
なので、例えば、以下のような感じで書くとエラーになる。
Register '/path/to/udf.py' using jython as myfuncs; ...
そこで、以下のような簡単なスクリプトを書き、どこからでも作成したUDFを使えるようにする。
- pig-tool
#!/usr/bin/perl -w use strict; use Cwd; my $pig_exec_dir = '/path/to/pig-udf-dir'; # UDFを置いてるディレクトリ my $wd = Cwd::getcwd(); # -fと-mオプションで指定されたパスが相対パスの場合、絶対パスに書き換える my @arr; while (my $opt = shift @ARGV) { if( ($opt eq '-m' || $opt eq '-param_file') || ($opt eq '-f' || $opt eq '-file') ){ my $file = shift @ARGV; die ('No exist file: $file') unless -e $file; $file = "$wd/$file" unless $file =~ m|^/| || $file =~ m|^~|; $opt = "$opt $file"; } push @arr, $opt; } # UDFが置いてるディレクトリに移動してpigを実行 chdir $pig_exec_dir; `pig @arr`;
- 実行
$ pig-tool -f hogehoge.pig
TokyoApache.pigについて
TokyoApache.PigというPigのコミュニティも作成したので、興味のある方は是非ご参加下さい!
年明けくらいから活動したいなと思っています!
sendmailRで画像付きHTMLメールを送る
前回は、R Advent Calendar 2012 : ATNDに間に合わせるために、現バージョンではHTMLメールが送れない問題と、画像が表示されない問題について苦肉の策をとらざるを得なかった。
さすがにあのままじゃちょっと残念な感じなので、これらの問題に対応してみた。
sendmailRパッケージの修正
今回の対応で、sendmailRパッケージ自体をいじる必要があったので、いじって開発者の方にpatchを送ってみた。
次のバージョンとかで反映されてると良いな〜
- yokkuns/sendmailR:Compare View
- パッケージの内部の関数でContent-Typeがベタ書きされてるので、これを引数headersで指定出来るように変更
- 添付ファイルのContent-IDが設定されていないので、内部で自動で設定するように変更
- この時、添付画像として指定しやすいように、IDを<ファイル名>とした
- インストール
上記の変更を行ったsendmailRパッケージは、以下のようにインストールが可能です。
$ git clone git@github.com:yokkuns/sendmailR.git
$ cd sendmailR
$ sudo make install
または、パッケージを作成して普通に、install.packagesでインストール
$ make package
> install.packages("sendmailR_1.1-2.tar.gz")
本文で画像を表示
markdownは、デフォルトでは、画像をbase64エンコードしたものを直接htmlに埋め込むようになっている。
これは、htmlファイル一個で完結することで、依存ファイルとか気にしなくて良いよって配慮なのだが、HTMLメールはこれに対応していない。
そこで、画像ファイルは、添付して、その画像を読み込むように変更する
- imgタグの画像へのリンクになるように変更
## デフォルトで、base64_imagesが設定されるので外す markdownToHTML(file=md.file,output=mail.html.file, stylesheet="/usr/lib/rstudio-server/resources/markdown.css", options=c("hard_wrap","use_xhtml","smartypants"))
- 出力されたhtmlに関して、figure/を、cid:に置換する
## figure/xxx.pngというリンクになるので、これを添付ファイル用のcid:に置き換える html.str <- gsub("figure/","cid:",html.str)
- 出来上がった修正版 sendStockAnomalyDetectionReport.R
#!/usr/bin/env Rscript library(knitr) library(markdown) library(sendmailR) main <- function(){ mail.from <- "yokkuns@tkul.jp" mail.to <- "yokkuns@tkul.jp" mail.subject <- "Sample Report from R server" rmd.file <- "stock_anomaly_detection_sample.Rmd" #RStudioで作成したRmdファイル ## HTMLレポート送信 sendHtmlReport(rmd.file,mail.from,mail.to,mail.subject) } sendHtmlReport <- function(rmd.file,from,to,subject,headers=list(),control=list()){ f <- rmd.file md.file <- paste(f,"md",sep=".") mail.html.file <- paste(f,".html",sep="") ## メール用 web.html.file <- paste("~/public_html/",f,".html",sep="") ## Web用 knit(input=rmd.file,output=md.file) markdownToHTML(file=md.file,output=web.html.file, stylesheet="/usr/lib/rstudio-server/resources/markdown.css") markdownToHTML(file=md.file,output=mail.html.file, stylesheet="/usr/lib/rstudio-server/resources/markdown.css", options=c("hard_wrap","use_xhtml","smartypants")) html.str <- paste(readLines(mail.html.file),collapse="\n") html.str <- gsub("figure/","cid:",html.str) imgs <- sapply(list.files("figure"), function(f){ mime_part(paste("figure",f,sep="/"),f) }) body <- unlist(list(list(html.str),imgs)) headers <- list("Content-Type"="text/html; charset=\"utf-8\"") ## control <- list(smtpServer="hogehoge.com",smtpPort=25) sendmail(from,to,subject,body,headers=headers,control) } ## 実行 main()
[R] knitr+sendmailR でHTMLレポート配信
R Advent Calendar2012、4日目です。
ふと、Rで分析した結果を定常的にメールでレポートしたいなーと思ったので、knitr+sendmailRで試してみた。
大きくは、以下のような流れ。
- RStudioで分析&レポート作成
- 上で作成したRmdファイルからHTMLファイルを生成
- 生成されたHTMLを読み込んで、HTMLメールを送信
ただ、最新版(1.1-1)のsendmailRだと、Content-Typeを内部で上書きされてHTMLメールが出来なかったので、前のバージョン(1.0-0)を使用した。
$ wget http://cran.r-project.org/src/contrib/Archive/sendmailR/sendmailR_1.0-0.tar.gz
> install.packages("sendmailR_1.0-0.tar.gz")
Rmdファイルの用意
今回は、異常検知(変化点検出)のパッケージを作ってみた - yokkunsの日記の時に作ったものを改良した。
- stock_anomaly_detection_sample.Rmd
株価異常検知 ======================================= ```{r,echo=FALSE,warning=FALSE,error=FALSE,message=FALSE} library(ChangeAnomalyDetection) library(ggplot2) library(RFinanceYJ) library(reshape2) library(xtable) ``` ```{r warning=FALSE,echo=FALSE} stock <- quoteStockXtsData("2432.t", since="2011-01-01") stock <- as.data.frame(stock) stock$date <- as.POSIXct(rownames(stock)) ``` ## 過去20日間の株価 ```{r warning=FALSE,echo=FALSE,results='asis'} stock.tail <- tail(stock,n=20) stock.tail$date <- as.character(stock.tail$date) print(xtable::xtable(stock.tail),type="html") ``` ## 過去20日間のClose値推移 ```{r fig.width=12, fig.height=6,warning=FALSE,echo=FALSE} ggplot(stock, aes(x=date,y=Close,col="Stock")) + geom_line() + geom_point() ``` ```{r,warning=FALSE,echo=FALSE} change.score <- changeAnomalyDetection(x=stock$Close, term=30, order=c(1,1,0)) stock$change.score <- change.score ``` ## 過去20日間のClose値異常スコア ```{r warning=FALSE,echo=FALSE,results='asis'} stock.tail <- tail(stock,n=20)[,c("date","Close","change.score")] stock.tail$date <- as.character(stock.tail$date) print(xtable::xtable(stock.tail),type="html") ``` ## 異常スコアの推移 ```{r fig.width=12, fig.height=6,warning=FALSE,echo=FALSE} ggplot(stock, aes(x=date,y=change.score,col="Score")) + geom_line() ```
Rmdファイルを読み込んでHTMLメールを送信
以下のような、R実行スクリプトを作成
- sendStockAnomalyDetectionReport.R
#!/usr/bin/env Rscript library(knitr) library(markdown) library(sendmailR) main <- function(){ mail.from <- "yokkuns@tkul.jp" mail.to <- "yokkuns@tkul.jp" mail.subject <- "Sample Report" rmd.file <- "stock_anomaly_detection_sample.Rmd" #RStudioで作成したRmdファイル ## HTMLレポート送信 sendHtmlReport(rmd.file,mail.from,mail.to,mail.subject) } sendHtmlReport <- function(rmd.file,from,to,subject,headers=list(),control=list()){ f <- rmd.file md.file <- paste(f,"md",sep=".") html.file <- paste("~/public_html/",f,".html",sep="") ## ブラウザからも確認出来るように公開ディレクトリに生成する knit(input=rmd.file,output=md.file) markdownToHTML(file=md.file,output=html.file) html.str <- paste(readLines(html.file),collapse="\n") body <- html.str headers <- list(`Content-Type`="text/html; charset = \"utf-8\"", `Content-Trensfer-Encoding`="7bit") ## control <- list(smtpServer="hogehoge.com",smtpPort=25) sendmail(from,to,subject,body,headers=headers,control) } ## 実行 main()
実行
$ chmod +x sendStockAnomalyDetectionReport.R
$ ./sendStockAnomalyDetectionReport.R
結果
無事、メールが届いた!
しかし、何故か、画像が表示されない・・・
ちょっと調べる時間が無かったので、生成されるHTMLのURLを記載する事にした。
- stock_anomaly_detection_sample.Rmdに追記
## 詳細 詳細については、以下のURLをご確認下さい。 http://example.com/~yokkuns/stock_anomaly_detection_sample.html
これで、クリックすれば以下のようなレポートが見れるのでオッケー(という事にしよう)
ChangeAnomalyDetectionパッケージを使ってHadoopにあるデータの異常検知(変化点検出)
先日作ったChangeAnomalyDetectionパッケージを使ってHadoop上のデータの異常検知する仕組みを考えてみる。
今回は、以下のようにhadoop上にデイリーで蓄積される購買履歴のようなデータを想定する
$ hadoop fs -ls /user/yokkuns/buying_history | head Found 100 items -rw-r--r-- 1 yokkuns supergroup 184 2012-10-14 13:00 /user/yokkuns/buying_history/2012-01-01 -rw-r--r-- 1 yokkuns supergroup 65 2012-10-14 13:00 /user/yokkuns/buying_history/2012-01-02 -rw-r--r-- 1 yokkuns supergroup 76 2012-10-14 13:00 /user/yokkuns/buying_history/2012-01-03 -rw-r--r-- 1 yokkuns supergroup 60 2012-10-14 13:00 /user/yokkuns/buying_history/2012-01-04 -rw-r--r-- 1 yokkuns supergroup 46 2012-10-14 13:00 /user/yokkuns/buying_history/2012-01-05 -rw-r--r-- 1 yokkuns supergroup 145 2012-10-14 13:00 /user/yokkuns/buying_history/2012-01-06 -rw-r--r-- 1 yokkuns supergroup 33 2012-10-14 13:00 /user/yokkuns/buying_history/2012-01-07 -rw-r--r-- 1 yokkuns supergroup 385 2012-10-14 13:00 /user/yokkuns/buying_history/2012-01-08 -rw-r--r-- 1 yokkuns supergroup 238 2012-10-14 13:00 /user/yokkuns/buying_history/2012-01-09
異常検知用コマンドの作成
Unixでは、標準入力/出力で扱えるとパイプとか使えて便利なので、それようのコマンドを作る
Rで標準入力を扱うには、file関数でdescriptionにstdinを指定する。また、ついでにレポートメールも出すようにした。
#!/usr/bin/env Rscript library(ChangeAnomalyDetection) library(sendmailR) library(ggplot2) main <- function(){ con <- file(description="stdin", open="r") data <- read.table(con, header=F, stringsAsFactors=F) close(con) names(data) <- c("log_name","date","x") x <- as.numeric(data$x) data$score <- changeAnomalyDetection(x, term=10, order=c(1,0,0)) reportMail(data) write.table(data[,c("log_name","date","x","score")],row.names=F,quote=F,col.names=F) } reportMail <- function(data){ if(data$score[nrow(data)] >= 2){ subject <- paste("Change in the trend: ",data$log_name[1],data$date[nrow(data)]) } else { subject <- paste("Normal:",data$log_name[1],data$date[nrow(data)]) } data$date <- as.POSIXct(data$date) score.graph <- ggplot(data, aes(x=date,y=score,group=log_name)) + geom_line(aes(col=log_name)) from <- sprintf("<changeAnomalyDetection@%s>", Sys.info()[4]) to <- "<yokkuns@tkul.jp>" body <- list(mime_part(data),mime_part(score.graph)) sendmail(from, to, subject, body) } main()
これで、change_anomaly_detection.R に標準入力でデータを渡せば、log名、日付、異常検知したい値、異常スコアが標準出力される。
hadoopコマンドでの実行
とりあえず、購買履歴のようなデータは、1レコードの大きさはほとんど変わらないと思われるので、まずはファイルサイズでトレンドに変化が無いかを見てみる。
急に大きくなっていれば、レコード数が増加を意味するので、何かが流行だしてる可能性があるとか、逆に減っている場合には、飽きられ始めてるかもとか、意外と全体の傾向が見えるので面白い。
hdfs_change_anomaly_detection.sh
hadoop fs -dusコマンドを使って、HDFS上のファイルサイズのリストを取得し、パイプでさっき作ったchange_anomaly_detection.Rに渡す。
#!/bin/bash source ~/.bashrc base_dir=$1 date_from=$2 date_to=$3 dates=`ruby -r date -e "Date.parse('${date_from}').upto(Date.parse('${date_to}')){|d|print d, ','}" | sed -e 's/,$//'` hadoop fs -dus $base_dir/{$dates} | perl -pe 's|hdfs://(.+)$base_dir/||g' | awk '{print "hdfs-size\t"$2"\t"$1}' | ./change_anomaly_detection.R
- usage
./hdfs_change_anomaly_detection.sh <base_dir> <date_from> <date_to>
- 実行例
$ ./hdfs_change_anomaly_detection.sh /user/yokkuns/buying_history 2012-01-01 2012-04-09 | tail -n 30 hdfs-size 2012-03-21 1182 0.721664957812903 hdfs-size 2012-03-22 1179 0.553532151383951 hdfs-size 2012-03-23 1118 0.476977584156091 hdfs-size 2012-03-24 1101 0.405535686607921 hdfs-size 2012-03-25 1088 0.35572429299516 hdfs-size 2012-03-26 1054 1.28066195323186 hdfs-size 2012-03-27 1335 1.79544225093878 hdfs-size 2012-03-28 1439 1.91945745582947 hdfs-size 2012-03-29 1081 2.03207005627137 hdfs-size 2012-03-30 790 1.04783664240509 hdfs-size 2012-03-31 805 0.478221556963071 hdfs-size 2012-04-01 782 0.322057929907172 hdfs-size 2012-04-02 1000 0.198707064341006 hdfs-size 2012-04-03 811 0.205308947142793 hdfs-size 2012-04-04 1033 0.161568530679506 hdfs-size 2012-04-05 973 0.0915506267593984 hdfs-size 2012-04-06 1030 0.102608803858441 hdfs-size 2012-04-07 846 0.125225930603473 hdfs-size 2012-04-08 833 0.253216213846101 hdfs-size 2012-04-09 628 0.185198971252599
hiveでの実行
HDFS上のファイルサイズでの異常検知は、全体のレコード数の傾向は見れるものの、何が増えたのかとか、などの内部の傾向の変化は捉えられない。
そこで、hiveを使ってカテゴリ別の集計を行い、その結果をchange_anomaly_detection.Rに渡すといった方法も考えられる。
- SQL作成
select category, to_date(from_unixtime(buy_time)), count(buying_id) as cnt from buying_history where to_date(from_unixtime(buy_time)) >= '$date_from' and to_date(from_unixtime(buy_time)) <= '$date_to' group by category,to_date(from_unixtime(buy_time))
- 実行
今回は、データを用意出来てないので、実行コマンドだけ。
$ hive -hiveconf date_from=2012-01-01 -hiveconf date_to=2012-04-09 -f category_buy_count.sql | ./change_anomaly_detection.R
hive + R で実行
hiveで集計した結果に、何らかの統計手法などを用いた結果に対して、異常検知を行いたいという事もあるかと思う。
その場合には、hiveで実行した結果を受けて、何らかの処理をした後標準出力するようなスクリプトを組めば、これまでと同様にパイプでつなげるだけで実行出来る
main <- function(argv){ con <- file(description="stdin", open="r") data <- read.table(con, header=F, stringsAsFactors=F) close(con) ... 何らかの処理 ... write.table(data[,c("log_name","date","y")],row.names=F,quote=F,col.names=F) }
- 実行例
こちらもデータを用意出来てないので、実行コマンドだけ。
$ hive -hiveconf date_from=2012-01-01 -hiveconf date_to=2012-04-09 -f category_buy_count.sql | ./hogehoge.R | ./change_anomaly_detection.R