针对三元数据帧的行操作优化 R 代码
Optimize R code for row operations on ternary data frame
问题
我有这个功能,我需要让它运行得更快:)
if (length(vec) == 0) { # first case
count = sum(apply(df, 1, function(x) {
all(x == 0, na.rm = T)
}))
} else if (length(vec) == 1) { # second case
count = sum(df[, vec], na.rm = T)
} else {
count = sum(apply(df[, vec], 1, function(x) { # third case
all(x == 1) }), na.rm = T)
}
df
是 data.frame
, 只有 1、0 或 NA 值。 vec
是 colnames(df)
.
的子向量
- 第一种情况:计算删除 NA 后的行数,它们只有 0(或什么都没有 - 例如该行只有 NA - 你也算)
- 第二种情况:在删除 NA
后计算向量中的 1(仅选择 1 列)
- 第三种情况:从过滤后data.frame得到所有值都等于1的行数
问题
您认为有什么方法可以使用 dplyr
或其他方式使此代码 运行 更快,因为它逐行处理数据?例如,当我将更简单的(第二种情况)- count = sum(df[, vec], na.rm = T)
与 dplyr
: sum(df %>% select(vec), na.rm = T)
交换并进行基准测试时,情况要差得多(但好吧,我不认为第二种情况使用任何方法都可以变得相当快)。
欢迎为第 2 和第 3 种情况提供任何提示或技巧!
基准测试
足够 data.frame 一起玩:df = matrix(data = sample(c(0,1,NA), size = 100000, replace = TRUE), nrow = 10000, ncol = 10)
。
- 第一种情况:
rbenchmark::benchmark("prev" = {sum(apply(df, 1, function(x) {all(x == 0, na.rm = T)}))}, "new-long" = {sum((rowSums(df == 0, na.rm = TRUE) + rowSums(is.na(df)) == ncol(df)))}, "new-short" = {sum(!rowSums(df != 0, na.rm = TRUE))}, replications = 1000, columns = c("test", "replications", "elapsed", "relative", "user.self", "sys.self"))
结果:
test replications elapsed relative user.self sys.self
2 new-long 1000 1.267 1.412 1.267 0
3 new-short 1000 0.897 1.000 0.897 0
1 prev 1000 11.857 13.219 11.859 0
- 第三种情况(以
vec = 1:5
为例):
rbenchmark::benchmark("prev" = {sum(apply(df[, vec], 1, function(x) { all(x == 1) }), na.rm = T)}, "new" = {sum(!rowSums(replace(df[, vec], is.na(df[, vec]), -999) != 1))}, replications = 1000, columns = c("test", "replications", "elapsed", "relative", "user.self", "sys.self"))
结果:
test replications elapsed relative user.self sys.self
2 new 1000 0.179 1.000 0.175 0.004
1 prev 1000 2.219 12.397 2.219 0.000
总体而言,使用 rowSums
的加速效果不错!也用它代替 apply
!
对于第一种和第三种情况,这里有一个使用 rowSums
优化代码的选项。由于当行值为 NA
时会出现边缘情况,一种选择是用不在数据集中的值替换这些值,创建一个逻辑矩阵,使用 rowSums
将其转换为逻辑 vector
并获得 sum
的 TRUE
值
sum((rowSums(df == 0, na.rm = TRUE) + rowSums(is.na(df)) == ncol(df)))
或
sum(!rowSums(df != 0, na.rm = TRUE))
sum(!rowSums(replace(df[, vec], is.na(df[, vec]), -999) != 1))
问题
我有这个功能,我需要让它运行得更快:)
if (length(vec) == 0) { # first case
count = sum(apply(df, 1, function(x) {
all(x == 0, na.rm = T)
}))
} else if (length(vec) == 1) { # second case
count = sum(df[, vec], na.rm = T)
} else {
count = sum(apply(df[, vec], 1, function(x) { # third case
all(x == 1) }), na.rm = T)
}
df
是 data.frame
, 只有 1、0 或 NA 值。 vec
是 colnames(df)
.
- 第一种情况:计算删除 NA 后的行数,它们只有 0(或什么都没有 - 例如该行只有 NA - 你也算)
- 第二种情况:在删除 NA 后计算向量中的 1(仅选择 1 列)
- 第三种情况:从过滤后data.frame得到所有值都等于1的行数
问题
您认为有什么方法可以使用 dplyr
或其他方式使此代码 运行 更快,因为它逐行处理数据?例如,当我将更简单的(第二种情况)- count = sum(df[, vec], na.rm = T)
与 dplyr
: sum(df %>% select(vec), na.rm = T)
交换并进行基准测试时,情况要差得多(但好吧,我不认为第二种情况使用任何方法都可以变得相当快)。
欢迎为第 2 和第 3 种情况提供任何提示或技巧!
基准测试
足够 data.frame 一起玩:df = matrix(data = sample(c(0,1,NA), size = 100000, replace = TRUE), nrow = 10000, ncol = 10)
。
- 第一种情况:
rbenchmark::benchmark("prev" = {sum(apply(df, 1, function(x) {all(x == 0, na.rm = T)}))}, "new-long" = {sum((rowSums(df == 0, na.rm = TRUE) + rowSums(is.na(df)) == ncol(df)))}, "new-short" = {sum(!rowSums(df != 0, na.rm = TRUE))}, replications = 1000, columns = c("test", "replications", "elapsed", "relative", "user.self", "sys.self"))
结果:
test replications elapsed relative user.self sys.self
2 new-long 1000 1.267 1.412 1.267 0
3 new-short 1000 0.897 1.000 0.897 0
1 prev 1000 11.857 13.219 11.859 0
- 第三种情况(以
vec = 1:5
为例):
rbenchmark::benchmark("prev" = {sum(apply(df[, vec], 1, function(x) { all(x == 1) }), na.rm = T)}, "new" = {sum(!rowSums(replace(df[, vec], is.na(df[, vec]), -999) != 1))}, replications = 1000, columns = c("test", "replications", "elapsed", "relative", "user.self", "sys.self"))
结果:
test replications elapsed relative user.self sys.self
2 new 1000 0.179 1.000 0.175 0.004
1 prev 1000 2.219 12.397 2.219 0.000
总体而言,使用 rowSums
的加速效果不错!也用它代替 apply
!
对于第一种和第三种情况,这里有一个使用 rowSums
优化代码的选项。由于当行值为 NA
时会出现边缘情况,一种选择是用不在数据集中的值替换这些值,创建一个逻辑矩阵,使用 rowSums
将其转换为逻辑 vector
并获得 sum
的 TRUE
值
sum((rowSums(df == 0, na.rm = TRUE) + rowSums(is.na(df)) == ncol(df)))
或
sum(!rowSums(df != 0, na.rm = TRUE))
sum(!rowSums(replace(df[, vec], is.na(df[, vec]), -999) != 1))