Re: [問題]不使用for改使用apply寫法的可能性

作者: celestialgod (天)   2020-03-26 09:31:12
※ 引述《locka (locka)》之銘言:
: (同是apply家族愛好者先shout out一下XD)
: 以前看過版主C大跟其他前輩版友的討論,結論是 apply 效率並沒有比較好
: (印象中好像背後都還是用for迴圈?!)
: 不過我覺得向量化(vectorized)是R語言裡面很重要的一個特性
: 也是functional programming跟其他程式語言不一樣的地方
: 我自己也喜歡做for轉apply的練習
: === 分隔線 ===
: 以下的寫法只是隨便寫的(甚至可以說骨子裡還是for的邏輯...)
: 請其他版友分享更好的寫法!!
: 1. cross validation:
: 1.a 還是用到folds
: sapply(1:5,function(i){
: testIndexes <- which(folds==i,arr.ind=T)
: # 做你想做的事情
: # ex: 找測試集的最大值跟訓練集的最小值
: max_intest <- max(dataset[testIndexes,])
: min_intrain <- min(dataset[-testIndexes,])
: #回傳結果
: c(max_intest,min_intrain)
: })
: 其實這個跟for的寫法根本換湯不換藥XD
: 1.b 使用split (不要用cut跟folds)
: 原po原本的方法是先用sample()抽樣打亂原始data的順序後再用cut()指定組別
: 提供sapply + split的做法:
: sapply(split(dataset,1:5),function(x){
: test <- x # test dataset
: train <- dataset[-as.numeric(row.names(x)),] # train dataset
: # 做你想做的
: c(max(test),max(train))
: })
: 這個做法直接把dataset分成5份然後將該dataframe傳進sapply裡
: 不過須注意這作法取樣的順序跟cut不一樣
: cut是111..222...333...444..555(各20次)
: split是12345...12345...12345...(共20次)
: 不過因為一開始已經用sample打亂過順序了,所以個人覺得後面cut或split應該沒差
: (有錯請指正>"<)
library(pipeR)
library(data.table)
N <- 100000L
x <- runif(N) * 10
y <- x+rnorm(N) * 0.1
num_folds <- 10L
split_idx <- seq_len(N) %>>% cut(breaks=num_folds, labels = FALSE) %>>% sample
dataset <- data.table(x, y, split_idx, key = "split_idx")
library(microbenchmark)
microbenchmark(apply_func = {
models <- seq_len(num_folds)%>>%
lapply( function(i){
lm(y ~ x, dataset[split_idx != i])
})
mse_folds <- seq_len(num_folds) %>>%
sapply(function(i){
mean((predict(models[[i]], dataset[split_idx == i]) -
dataset[split_idx == i]$y)^2)
})
},
for_loop = {
models <- vector("list", num_folds)
mse_folds <- vector("numeric", num_folds)
for (i in seq_len(num_folds)) {
models[[i]] <- lm(y ~ x, dataset[split_idx != i])
mse_folds[i] <- mean((predict(models[[i]], dataset[split_idx == i]) -
dataset[split_idx == i]$y)^2)
}
},
times = 20L
)
# Unit: milliseconds
# expr min lq mean median uq max neval
# apply_func 192.4187 199.8315 236.0988 209.2901 284.0653 365.0350 20
# for_loop 198.8673 206.2947 230.8822 210.7969 260.0005 301.9913 20
基本上沒差多少,開心用什麼就用什麼,重點還是forloop前的變數要allocate
apply寫法有很多種,有人是不存model,那就不用拆兩段
有人直接一次return list(model, mse),這樣也不用拆兩段
我是覺得apply或是for-loop看coder開心就好XD
請參考 https://www.ptt.cc/man/R_Language/D5C7/M.1437921117.A.AC4.html
: 2. 偷偷問的小問題
: x <- c(2:3)
: y <- c(4:8)
: sapply(x,function(i){
: i*y
: })
: 其實我後來很常用
: sapply(x,function(){
: ....
: })
可以用microbenchmark去看效能,我猜其實沒差
這個例子outer最快
作者: locka (locka)   2020-03-26 13:36:00
謝謝大大!沒想到還真的被釣出來了(欸可能我的使用情境來說都還不到效能瓶頸,所以以前不太需要注意效能問題,但還是很感謝C大分享!

Links booklink

Contact Us: admin [ a t ] ucptt.com