从数据帧子集中有效抽样因子变量
Efficient sampling of factor variable from dataframe subsets
我有一个包含 6 列的数据框 df1
,其中两列 (var1
& var3
) 我正在使用 split
df1
,生成数据帧列表 ls1
。
对于 ls1
中的每个子数据帧,我想要 sample()
x$var2
、x$num
次 x$probs
概率如下:
创建数据:
var1 <- rep(LETTERS[seq( from = 1, to = 3 )], each = 6)
var2 <- rep(LETTERS[seq( from = 1, to = 3 )], 6)
var3 <- rep(1:2,3, each = 3)
num <- rep(c(10, 11, 13, 8, 20, 5), each = 3)
probs <- round(runif(18), 2)
df1 <- as.data.frame(cbind(var1, var2, var3, num, probs))
ls1 <- split(df1, list(df1$var1, df1$var3))
看看前几个列表元素:
$A.1
var1 var2 var3 num probs
1 A A 1 10 0.06
2 A B 1 10 0.27
3 A C 1 10 0.23
$B.1
var1 var2 var3 num probs
7 B A 1 13 0.93
8 B B 1 13 0.36
9 B C 1 13 0.04
lapply
超过ls1
:
ls1 <- lapply(ls1, function(x) {
res <- table(sample(x$var2, size = as.numeric(as.character(x$num)),
replace = TRUE, prob = as.numeric(as.character(x$probs))))
res <- as.data.frame(res)
cbind(x, res = res$Freq)
})
df2 <- do.call("rbind", ls1)
df2
查看结果的前几个列表元素:
$A.1
var1 var2 var3 num probs res
1 A A 1 10 0.06 2
2 A B 1 10 0.27 4
3 A C 1 10 0.23 4
$B.1
var1 var2 var3 num probs res
7 B A 1 13 0.93 10
8 B B 1 13 0.36 3
9 B C 1 13 0.04 0
因此,对于每个数据帧,都会创建一个新变量 res
,res
的总和等于 num
,var2
的元素表示在 res
与 probs
相关的比例。这做了我想要的,但是当有很多数据时它变得非常慢。
我的问题: 有没有办法用更多 efficient/faster 替换 lapply
代码?
我刚开始学习矢量化,我猜这可以矢量化吗?但我不确定如何实现它。
ls1
最终返回到一个数据帧结构,所以如果它不需要成为一个列表就更好了(尽管这一步数据的结构并不重要).
如有任何帮助,我们将不胜感激。
首先,您应该使用 data.frame() 创建 df1 而不是从矩阵转换,因为即使您同时拥有数字和字符变量,矩阵也会强制所有数据类型相同。
df1 <- data.frame(var1, var2, var3, num, probs)
接下来,不用sample
函数,rmultinom
函数效率高很多,因为它直接输出x$var2:
中每个值的绘制次数
ls1 <- lapply(ls1, function(x) {
x$res <- rmultinom(1, x$num[1], x$probs)
x
})
这应该比使用 sample
方法快得多。
与其将您的数据框分成几组,我会使用包 {dplyr} 和 group_by+mutate:
library(dplyr)
df1 %>%
mutate_at(vars(num, probs), as.numeric) %>%
group_by(var1, var3) %>%
mutate(res = c(rmultinom(1, num[1], probs)))
这样应该很快,而且可以保持原来的数据结构。
了解更多there。
我有一个包含 6 列的数据框 df1
,其中两列 (var1
& var3
) 我正在使用 split
df1
,生成数据帧列表 ls1
。
对于 ls1
中的每个子数据帧,我想要 sample()
x$var2
、x$num
次 x$probs
概率如下:
创建数据:
var1 <- rep(LETTERS[seq( from = 1, to = 3 )], each = 6)
var2 <- rep(LETTERS[seq( from = 1, to = 3 )], 6)
var3 <- rep(1:2,3, each = 3)
num <- rep(c(10, 11, 13, 8, 20, 5), each = 3)
probs <- round(runif(18), 2)
df1 <- as.data.frame(cbind(var1, var2, var3, num, probs))
ls1 <- split(df1, list(df1$var1, df1$var3))
看看前几个列表元素:
$A.1
var1 var2 var3 num probs
1 A A 1 10 0.06
2 A B 1 10 0.27
3 A C 1 10 0.23
$B.1
var1 var2 var3 num probs
7 B A 1 13 0.93
8 B B 1 13 0.36
9 B C 1 13 0.04
lapply
超过ls1
:
ls1 <- lapply(ls1, function(x) {
res <- table(sample(x$var2, size = as.numeric(as.character(x$num)),
replace = TRUE, prob = as.numeric(as.character(x$probs))))
res <- as.data.frame(res)
cbind(x, res = res$Freq)
})
df2 <- do.call("rbind", ls1)
df2
查看结果的前几个列表元素:
$A.1
var1 var2 var3 num probs res
1 A A 1 10 0.06 2
2 A B 1 10 0.27 4
3 A C 1 10 0.23 4
$B.1
var1 var2 var3 num probs res
7 B A 1 13 0.93 10
8 B B 1 13 0.36 3
9 B C 1 13 0.04 0
因此,对于每个数据帧,都会创建一个新变量 res
,res
的总和等于 num
,var2
的元素表示在 res
与 probs
相关的比例。这做了我想要的,但是当有很多数据时它变得非常慢。
我的问题: 有没有办法用更多 efficient/faster 替换 lapply
代码?
我刚开始学习矢量化,我猜这可以矢量化吗?但我不确定如何实现它。
ls1
最终返回到一个数据帧结构,所以如果它不需要成为一个列表就更好了(尽管这一步数据的结构并不重要).
如有任何帮助,我们将不胜感激。
首先,您应该使用 data.frame() 创建 df1 而不是从矩阵转换,因为即使您同时拥有数字和字符变量,矩阵也会强制所有数据类型相同。
df1 <- data.frame(var1, var2, var3, num, probs)
接下来,不用sample
函数,rmultinom
函数效率高很多,因为它直接输出x$var2:
ls1 <- lapply(ls1, function(x) {
x$res <- rmultinom(1, x$num[1], x$probs)
x
})
这应该比使用 sample
方法快得多。
与其将您的数据框分成几组,我会使用包 {dplyr} 和 group_by+mutate:
library(dplyr)
df1 %>%
mutate_at(vars(num, probs), as.numeric) %>%
group_by(var1, var3) %>%
mutate(res = c(rmultinom(1, num[1], probs)))
这样应该很快,而且可以保持原来的数据结构。
了解更多there。