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