No.22590 【R】 繰り返し処理の高速化  【明石】 2018/08/14(Tue) 18:06

青木先生 様;

お忙しいところを失礼いたします,明石と申します。
毎々,ご丁寧なご教示をいただき,誠にありがとうございます。
改めて,御礼を申し上げます。

青木先生にご教示いただきたいことが出てきました。
何卒どうぞよろしくお願いいたします。

---------------------------
受験者の生年月日と試験日から,試験日での年齢を計算したいと思います。

受験者データはデータフレームdatです。
生年月日の変数名はbirthです。日付型です。

試験日の変数名はtestです。日付型です。
test <- as.Date("2018-9-13") 
以下の繰り返し処理で,受験者の試験日での年齢yearを計算できることを確認しました。
for (i in 1:nrow(dat))  {
dat$year[i] <- length(seq(dat$birth[i], test, by = "year")) - 1
}
受験者データが大規模ですので,繰り返し処理を高速化したいと思います。

ご教示いただければ大変に助かります。
何卒どうぞ,よろしくお願いいたします。

No.22591 Re: 【R】 繰り返し処理の高速化  【青木繁伸】 2018/08/15(Wed) 02:12

50000 人分のテストデータを作ってやってみました
test <- as.Date("2018-9-13")
dat <- data.frame(birth=as.Date(Sys.Date()-1:50000)) # 50000 人分のテストデータ

system.time({
for (i in 1:nrow(dat)) {
dat$year[i] <- length(seq(dat$birth[i], test, by = "year")) - 1
}
})
ユーザ システム 経過
11.711 6.689 18.254
年齢の計算は,簡単なのです。試験年から生まれ年を引くだけ。
ただし,「生まれ月が試験月より後(大きい)なら」,
または,「生まれ月が試験月と同じで,生まれ日が試験日より後(大きい)なら」
1を引く。

わざわざ as.Date となっているけど,そこから年月日(スカラー y, m, d)を取り出す。
試験年月日は yyyy, mm, dd の整数で与える(as.Date から取りだしてもよいけど)
age2 <- function(x, yyyy = 2018, mm = 09, dd = 13) {
y <- as.integer(substr(x, 1, 4))
m <- as.integer(substr(x, 6, 7))
d <- as.integer(substr(x, 9, 10))
val <- yyyy - y
if (m > mm || (m == mm && d > dd)) {
val <- val-1
}
val
}
system.time({
for (i in 1:nrow(dat)) {
dat$year2[i] <- age2(dat$birth[i])
}
})
でも,これでは for を含むし,年月日の取り出しコストが掛かるのでかえって遅い。
   ユーザ   システム       経過  
13.662 6.667 20.312
一応答えは合っているようだ。
> all(dat$year == dat$year2)
[1] TRUE
for を避けるためにベクトル演算を行うようにする。
関数には dat$birth ベクトルを渡す。
年月日の取り出し,それに基づく年齢(val)の計算も全部がベクトル演算。
age3 <- function(birth, yyyy = 2018, mm = 09, dd = 13) {
y <- as.integer(substr(birth, 1, 4))
m <- as.integer(substr(birth, 6, 7))
d <- as.integer(substr(birth, 9, 10))
val <- yyyy - y
ifelse(m > mm | (m == mm & d > dd), val-1, val)
}
system.time(dat$year3 <- age3(dat$birth))
27 倍ほど速くなった。
   ユーザ   システム       経過  
0.424 0.023 0.448
答えも合っているようだ。
> all(dat$year == dat$year3)
[1] TRUE
なお,val を計算している 2 行を(ifelse を使うのが気持ち悪いからといって)
	yyyy - y - (m > mm | (m == mm & d > dd))
にしても,ほとんど速度に違いはない。

年月日が3つのベクトルで与えられれば as.Date から取り出す必要がないのでもっと速い。
birth を "2018-08-15" とわざわざ入力しているのか,2018, 8, 15 という3つの整数を入力しているのかによる。
後者なら,その3つの整数から文字列を作って as.Date していることになる。
3つの整数値を入力してそれをそのまま使うなら,人間もコンピュータも無駄なことをしなくてもよいということになる。

No.22592 御礼(Re: 【R】 繰り返し処理の高速化)  【明石】 2018/08/15(Wed) 08:04

青木先生 様;

お忙しいところを失礼いたします,明石と申します。

多面的にご教示いただき,
特に,for を避けるためにベクトル演算を行うようにするところは,
大変に勉強になります。

毎々,ご丁寧なご教示をいただき,誠にありがとうございます。
心から御礼を申し上げます。
ありがとうございました。

● 「統計学関連なんでもあり」の過去ログ--- 048 の目次へジャンプ
● 「統計学関連なんでもあり」の目次へジャンプ
● 直前のページへ戻る