このあたりから処理がアドホック寄りになるので、data.tableを使う場合でもパイプ(dplyr)を使って一度に実行するのではなく添字記法を使って1ステップずつ進めていくといい(1行ずつ実行する場合は添字記法を使った方がコード量が少なくて済む)。
目次
行の削除(抽出)
行の並べ替え(ソート)
変数の加工(データフレーム/data.frame共通)
標準化(scale)
指定した変数を標準化(平均=0、分散=1のスケールに圧縮/拡大)する。
scale()
関数を使う。
# データフレーム
x.dt$purchase_amount <- scale(x.dt$purchase_amount)
# data.table
x.dt[,purchase_amount := scale(purchase_amount)]
複数列をまとめてやるにはmutate_at()
を使って
x.dt %>%
mutate_at(vars(n_purchase, purchase_amount), funs(scale)) -> x.dt
などとする。
欠損値処理
NAをいずれかの列に含む行全体を削除(いわゆるリストワイズ法)→na.omit()
関数を使う
# データフレーム
x.df <- na.omit(x.df)
# data.table
x.dt <- na.omit(x.dt)
# パイプを使う場合
x.dt %>%
na.omit %>% ...
特定の値を代入する(以下の例では平均値)
# データフレーム
x.dt$n_purchase[is.na(x.dt$n_purchase)] <- mean(x.dt$n_purchase, na.rm = T)]
# data.table
x.dt[is.na(n_purchase), n_purchase := mean(n_purchase, na.rm = T)]
一般的な欠損値処理についてはこのあたりを参照
data.tableのままで(data.tableの性質を保持したままで)これらの処理をすることは不可能だが、データフレームに対しては実行できる。そのためdata.tableオブジェクトをそのままこれらの関数に渡し、処理結果のデータフレームをas.data.table()
でdata.table化する。
外れ値処理
以下の流れになる。
- 各列に対する外れ値を検出
- 外れ値を含む行を除外する→行の削除
各列に対する外れ値の検出はdata.tableに固有の方法はない。
スミルノフ・グラブス検定grubbs.test() {outliers}
などで外れ値の閾値を検出する。
たとえば
> grubbs.test(x.dt$n_purchase_shoes)
Grubbs test for one outlier
data: x
G = 6.55290, U = 0.56187, p-value = 3.242e-12
alternative hypothesis: highest value 50 is an outlier
という結果が出たら、
x.dt %>% filter(n_purchase_shoes != 50) -> x.dt
のようにする。これを各列について実行する。
変数の追加
- オリジナルのカテゴリでは使いにくく、分析用にカテゴリを組み替えたい
- 連続量をグループに分けたい
- 欠損値を特定の値に置換したい
- 行番号の列を作る
アドホックに変数を作っていきたい場合は添字指定がいい。
まとめて変数を定義する場合にmutate()
を使う。
コーディング
# データフレーム
page$monthly <- ifelse(is.na(page$monthly), page$daily*30, page$monthly)
page[page$click==0,"active"] <- 0
page[page$click>0,"active"] <- 1
ifelse()
はスカラしか返さないので主にデータフレームのコーディングで使う。
それ以外の処理の条件分岐では使わないほうがいい
特にそれ自体がリストとなるPOSIXltは強制的にdoubleに置換されるので日時の処理では使ってはならない**。
# データフレーム
x <- ifelse(is.na(date_a), as.POSIXct(Sys.Date()), as.POSIXct(date_a))
class(x)
[1] "numeric"
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1072289430
NA
さえなければ
ifelse(x>10, y, z)
(x>10)*y + (x<=10)*z
上記は同じ結果になる。
オリジナルのカテゴリでは使いにくく、分析用にカテゴリを組み替える
以下は地域名として
prefecture
(都道府県)、city
(市)、ward
(区)という3個の変数があり、
地域名に基づいて変数を作る例である。
prefectureが東京以外の場合はcityがあるが、東京の場合はcityがないのでwardを使いたい。
# prefecture == 'Tokyo'の場合ward、それ以外の場合cityを採用する
## データフレーム
x.dt$local <- as.factor(ifelse(x.dt$prefecture == 'Tokyo', x.dt$ward, x.dt$city))
## data.table
x.dt[,local := as.factor(ifelse(prefecture == 'Tokyo', ward, city))]
# Prefectureが'Tokyo', 'Osaka', 'Aichi'の場合、major_prefecture == TRUEにする
## データフレーム
x.dt$major_prefecture <- x.dt$prefecture %in% c('Tokyo', 'Osaka', 'Aichi')
## data.table
x.dt[,major_prefecture := prefecture %in% c('Tokyo', 'Osaka', 'Aichi')]
# major_prefectureを反転
## データフレーム
x.dt$minor_prefecture <- !x.dt$major_prefecture
## data.table
x.dt[,minor_prefecture := !major_prefecture]
# 特定のprefecture, city, wardの場合にフラグを立てる
## データフレーム
x.dt$capital <- x.dt$prefecture == 'Osaka' & x.dt$city == 'Osaka' & x.dt$ward == 'Kita-ku'
## data.table
x.dt[,capital := prefecture == 'Osaka' & city == 'Osaka' & ward == 'Kita-ku']
mutate()
でまとめて実行する場合
# data.table
x.dt %>%
mutate(
# prefecture == 'Tokyo'の場合ward、それ以外の場合cityを採用する
local = as.factor(ifelse(prefecture == 'Tokyo', ward, city)),
# Prefectureが'Tokyo', 'Osaka', 'Aichi'の場合、major_prefecture == TRUEにする
major_prefecture = prefecture %in% c('Tokyo', 'Osaka', 'Aichi'),
# major_prefectureを反転
minor_prefecture = !major_prefecture,
# 特定のprefecture, city, wardの場合にフラグを立てる
capital = prefecture == 'Osaka' & city == 'Osaka' & ward == 'Kita-ku'
) -> x.dt
連続量をグループに分ける
cut()
関数を使う。
年齢を「0−9」「10-19」「20-29」「30-39」「40-49」「50-59」「60-」の0歳区切りで分割し、ラベルを’~9′, ‘~19’, ‘~29’, ‘~39’, ‘~49’, ‘~59’, ’60~’にする。
# データフレーム
x.df$age_grp <- cut(x.df$age, breaks = c(0:6*10, Inf), include.lowest = T, labels = c(paste0('~', 1:6*10-1), '60~'), ordered_result = T)
# data.table
x.dt[,age_grp:=cut(age, breaks = c(0:6*10, Inf), include.lowest = T, labels = c(paste0('~', 1:6*10-1), '60~'), ordered_result = T)]
cut()
の使い方はcut(age, breaks = 下限と上限を含む区切り点のベクトル, include.lowest = T, labels = ラベル, ordered_result = T)
と覚えておくといい。breaksの長さ - 1 = labelsの長さ
になる。
mutate()
を使う場合
# data.table
x.dt %>%
mutate(
age_grp = cut(age, breaks = c(0:6*10, Inf), include.lowest = T, labels = c(paste0('~', 1:6*10-1), '60~'), ordered_result = T)
) -> x.dt
欠損値を特定の値に置換する
purchase_amount
がNAの場合に「0」にし、値がある場合はそれをそのまま採用する
x.dt[is.na(purchase_amount),purchase_amount:=0]
mutate()
を使う場合
x.dt %>%
mutate(
purchase_amount = ifelse(is.na(purchase_amount), 0, purchase_amount)
) -> x.dt
'NULL'
という文字列を欠損値(NA)化する
clicklog[page_name == 'NULL', page_name := NA]
列sample_
は本来2値変数で、本来TRUEの場合に文字列’Y’、FALSEの場合にそもそも値が入っていない(NA)。これをRのlogical型に変換する。
x.dt[sample_ == 'Y', sample := T]
x.dt[is.na(sample_), sample := F]
x.dt[,sample_ := NULL]
行番号
IDをrownameにする(データフレーム)
データフレームを分析処理の関数にかけるとき、
行を識別する目的の変数(ID)は除外しなければならない。
たとえば
kmeans(x, centers = 5)
とするとき、x
にはIDの列が含まれていてはならない。
その都度除外処理を入れるのは面倒なので、データフレームのrownameにしてしまい、
実データを表す列から除外してしまう。
rownames(customer) <- customer$id
customer$id <- NULL
行番号の列を作る(data.table)
x.dt[,my_id = .I]
mutate()
を使う場合
x.dt %>%
mutate(
my_id = as.integer(rownames(.))
) -> x.dt
変数の削除
不要になった変数を削除する。
# データフレーム
x.df$n_purchase <- NULL
# data.table
x.dt[,n_purchase:=NULL]
分析上意味のない(全レコードの値が同じ)列を除外する
adlog <- adlog[sapply(adlog, function(x) length(levels(factor(x,exclude=NULL)))>1)]
http://stackoverflow.com/questions/8805298/quickly-remove-zero-variance-variables-from-a-data-frame
結合
ジョイン(テーブルを横に結合)
データフレーム同士をマージする
共通の列名がある場合、それをキーにマージする。
category <- merge(page, clicklog)
列名を指定することも可能
category <- merge(page, clicklog, by.x="pageid", by.y="pid")
デフォルトでINNER JOINと同じ挙動。
– all.x=T
でLEFT JOIN
– all.y=T
でRIGHT JOIN
– 両方指定するとFULL OUTER JOIN
category <- merge(page, clicklog, all.y=T)
キーがrownameになっている場合、by.x=0
で指定できる。
category <- merge(page, clicklog, by.x=0, by.y="pid")
データフレームにベクトルをマージする
IDをキーにマージする
1変量ではIDを名前(names
属性)にとる名前付きベクトルを使うことが多い。
またtapply()
の出力結果はarray
になっており、names
属性が要素を識別するインデックスとなっている。
names()
でこれらを取得するとcharacter
型で返すので、IDをinteger
型で持たせているデータフレームとマージ際はas.integer(names(...))
でinteger
型に合わせるのが安全である。
click.category <- tapply(clicklog$click, clicklog$categoryId, sum)
category <- merge(
category,
cbind(categoryId = as.integer(names(click.category)), click = click.category)
)
data.table
データフレームと同様にジョイン処理はmerge()
関数を使うことができる
# 列名が同じ場合
merge(x.dt, y.dt, all.x = T, by='store_id') -> x.dt
# 列名が異なる場合
merge(x.dt, y.dt, all.x = T, by=c('reg_store' = 'store_id')) -> x.dt
# それぞれのテーブルに同じ列名がある場合
merge(x.dt, y.dt, all.x = T,, by=c('reg_store' = 'store_id'), suffix = c('', '_store_master')) -> x.dt
***_join()
という関数を使ってdplyrのパイプラインの中でジョインを入れることができる。
上と同じことをパイプラインで実現する例
# 列名が同じ場合
x.dt %>% left_join(y.dt, by='store_id') -> x.dt
# 列名が異なる場合
x.dt %>% left_join(y.dt, by=c('reg_store' = 'store_id')) -> x.dt
# それぞれのテーブルに同じ列名がある場合
x.dt %>% left_join(y.dt, by=c('reg_store' = 'store_id'), suffix = c('', '_store_master')) -> x.dt
inner_join()
、left_join()
、right_join()
、full_join()
があるmerge(x, y)
==inner_join(x, y)
merge(x, y, all.x = T)
==left_join(x, y)
merge(x, y, all.y = T)
==right_join(x, y)
merge(x, y, all = T)
==full_join(x, y)
by=
とsuffix=
の与え方はmerge()
やmerge.data.table()
と同じ- お互いのテーブルのキーとなる列の型が一致していないと使えない
- それぞれのテーブルに同じ列名がある場合、
suffix = c('x側の列につけるsuffix', 'y側の列につけるsuffix')
を指定する
以下のようにパイプラインの中でleft_joinを繰り返していくことが多い
データ %>%
left_join(マスタ) %>%
left_join(マスタ) %>%
:
ユニオン(行結合)
bind_rows()
を使う。
x1.dt
に行を追記していく例
x1.dt %>%
bind_rows(x2.dt) %>%
bind_rows(x3.dt) -> x1.dt
データの加工や分析で使うRの使い方 の記事一覧