Rでデータセットの抽出(行の抽出、並べ替え、サンプリング、分割)

この記事は連載「Rのデータ前処理」の全 4 ページ中 4 ページ目です。

前のページではデータフレーム、data.tableの列(変数)の処理について解説したが、今度は行の抽出、並べ替え、サンプリング、分割といった行の処理についてまとめる。

行の削除(抽出)

データフレーム

adlog <- adlog[adlog$imp>1000 & adlog$click<10, , drop=F]
adlog <- with(adlog, adlog[imp>1000 & click<10, , drop=F])

上下は同じ。with()関数はバッチの中でも使えるので便利。

データフレームの抽出・絞り込みでは第3添字にdrop=FALSEを付けること!
行列の添え字にdrop=FALSEを付けないと1行(列)のみマッチの場合にベクトルとして返す。そうなるとデータフレームを想定してその後の処理にrbind()をしていたのができなくなるなど、行列処理に思わぬ不具合をきたすことになる。
drop=FALSEを付けて1行n列の行列を返すように。

ただしtapply()などで使う1列取得の際は付けてはならない。ベクトルとして処理する必要がある。

data.table

n_purchase < 10の行を抽出

# dplyrのパイプを使う場合
x.dt %>%
  filter(n_purchase < 10) -> x.dt
# 添字を使う場合
x.dt[n_purchase < 10,] -> x.dt

n_purchase < 10の行を削除

# dplyrのパイプを使う場合
x.dt %>%
  filter(!n_purchase < 10) -> x.dt
# 添字を使う場合
x.dt[!n_purchase < 10,] -> x.dt

複数条件(AND)

dplyrのパイプを使う場合

# dplyrのパイプを使う場合
x.dt %>%
  filter(n_purchase < 10, interval_latest_purchase > 30) -> x.dt
# 添字を使う場合
x.dt[n_purchase < 10 & interval_latest_purchase > 30,] -> x.dt

filter()を使う場合でも&で条件を結合していい。

複数条件(OR)

dplyrのパイプを使う場合

# dplyrのパイプを使う場合
x.dt %>%
  filter(n_purchase < 10 | interval_latest_purchase > 30) -> x.dt
# 添字を使う場合
x.dt[n_purchase < 10 | interval_latest_purchase > 30,] -> x.dt

いずれも|で条件を結合する。

行の並べ替え(ソート)

データフレーム/行列

行列を第n列をキーに昇順に並び替え

x[order(x[,n]), ]

行列を第n列をキーに降順に並び替え

x[order(x[,n], decreasing=F),]

カラムimp(最優先), click(次に優先)をキーに昇順に並び替え

adlog <- adlog[order(adlog$imp, adlog$click),]

下記と同じ

adlog <- with(adlog, adlog[order(imp, click),])
adlog <- adlog[adlog$click,]
adlog <- adlog[adlog$imp,]

並べ替えの際は第3添字のdrop=Fは不要(そもそも複数行が前提)。

data.table

カラムn_purchase(最優先), interval_latest_purchase(次に優先)をキーに昇順に並び替え

# dplyrのパイプを使う場合
x.dt %>%
  arrange(n_purchase, interval_latest_purchase) -> x.dt
# 添字を使う場合
x.dt[order(n_purchase, interval_latest_purchase)] -> x.dt

カラムn_purchaseをキーに降順(最優先)、interval_latest_purchaseをキーに昇順(次に優先)に並び替え

# dplyrのパイプを使う場合
x.dt %>%
  arrange(-n_purchase, interval_latest_purchase) -> x.dt
# 添字を使う場合
x.dt[order(-n_purchase, interval_latest_purchase),] -> x.dt

characterに対してソートはできない。
arrange()を使ってfactorに対して降べきの順を指定する場合にはマイナス符号を使えず、列名をdesc()で囲んでarrange(desc(列名))のようにする。

サンプリング

単純なサンプリング

それなりに大きいデータではサンプリングして分析することになる。
Rのbaseではsample()関数、dplyrをインストールしている場合は便利なsample_n()関数とsample_frac()関数が使える。
(データフレーム、data.table共通)

使い方は

  • sample(母集団のサイズ, サンプルサイズ)
  • 母集団のデータフレーム %>% sample_n(サンプルサイズ)
  • 母集団のデータフレーム %>% sample_frac(サンプル率)

である。

サンプルの大きさ(行数)を指定する

データセットclicklogから1000行を抽出する場合

sample()関数では

# set.seed(1234)
row.sampled <- sample(nrow(clicklog), 1000)
clicklog.sampled <- clicklog[row.sampled, , drop=F]

sample_n()関数を使うと

# set.seed(1234)
click_log %>% sample_n(10) -> click_log.sampled

となる。

サンプルの抽出率を指定する

データセットclicklogから抽出率20%で無作為抽出する場合、

sample()関数では

nr <- nrow(clicklog)
# set.seed(1234)
row.sampled <- sample(nr, floor(nr * 0.2))
clicklog.sampled <- clicklog[row.sampled, , drop=F]

sample_frac()関数を使うと

# set.seed(1234)
click_log %>% sample_frac(0.2) -> click_log.sampled

確かにこちらのほうが簡単である。

IDでサンプリング

ユーザマスタ-/行動履歴などデータフレームが分かれている場合、行動履歴もユーザマスタのIDに基づいてサンプリングすることになる。
userIdを抽出率20%で無作為抽出し、そのuserIdに基づいてclick_logを抽出する。

  • マスタのオブジェクト:user
  • データのオブジェクト:click_log
  • 連携しているID(マスタのプライマリキー):userId

という場合

nr <- nrow(user)
# set.seed(1234)
row.sampled <- sample(nr, floor(nr * 0.2))
user.sampled <- user[row.sampled,]
click_log.sampled <- clicklog[is.element(click_log$userId, userId.sampled), , drop=F]

userIdを列名にしている場合

nr <- nrow(user)
# set.seed(1234)
row.sampled <- sample(nr, floor(nr * 0.2))
user.sampled <- rownames(user)[row.sampled]
click_log.sampled <- clicklog[is.element(click_log$userId, userId.sampled), , drop=F]

clicklogテーブルだけで抽出してしまう場合

# set.seed(1234)
userId.unique <- unique(click_log$userId)
userId.sampled <- sample(userId.unique, floor(length(userId.unique) * 0.2))
click_log.sampled <- clicklog[is.element(click_log$userId, userId.sampled), , drop=F]

分割

学習用と検証用とでデータセットを分割する

nr <- nrow(dat)
# set.seed(1234)
row.train <- sample(nr, floor(0.5 * nr))
dat.train <- dat[row.train,]
dat.test <- dat[-row.train,]
Series Navigation<< Rでデータクリーニング、変数の生成