将每行值更改为 rowsum-1 的更快代码,其中值为 1
Faster code for changing values per row to rowsum-1 where value is 1
在 R 中,我有一个带有采样位置和条目的大型数据框(23344 行 x 89 列)。
value 1 表示:在此采样位置找到对象
值 0 表示:未找到此采样位置的对象
要计算每个采样位置(节点)degrees/connections,每行,得到rowsum-1
(因为这等于度数)并将该行中的 1 更改为该值。
此后,我可以获得 colSum()
来计算每个样本位置的总度数。
我的数据框的可重现示例:
loc1 <- c(1,0,1)
loc2 <- c(0,1,1)
loc3 <- c(1,1,0)
loc4 <- c(1,1,0)
loc5 <- c(0,1,0)
df <- data.frame(loc1, loc2, loc3, loc4, loc5)
# loc1 loc2 loc3 loc4 loc5
# 1 1 0 1 1 0
# 2 0 1 1 1 1
# 3 1 1 0 0 0
所需的输出如下所示
# loc1 loc2 loc3 loc4 loc5
# 1 2 0 2 2 0 #rowsum = 3 so change values>1 to 2
# 2 0 3 3 3 3 #rowsum = 4 so change values>1 to 3
# 3 1 1 0 0 0 #rowsum = 2 so change/keep values>1 to 1
我有可用的代码,但速度很慢(包含 for 循环)那么有 better/faster 方法可以做到这一点吗?我知道函数 rowSums()
可能是解决方案的一部分。
我目前的代码如下:
for (r in 1:nrow(df)){
df[r, df[r,] == 1] <- sum(df[r,]) - 1}
degrees_per_sample <- colSums(df)
您可以尝试在数据框上使用 ifelse()
:
df[] <- ifelse(df == 1, rowSums(df) - 1, 0)
给出:
loc1 loc2 loc3 loc4 loc5
1 2 0 2 2 0
2 0 3 3 3 3
3 1 1 0 0 0
您可以使用:
df[] <- +(df > 0) * (rowSums(df) - 1)
df
# loc1 loc2 loc3 loc4 loc5
#1 2 0 2 2 0
#2 0 3 3 3 3
#3 1 1 0 0 0
认为对于这些东西使用矩阵而不是 data.frames 的好处可能有趣:
set.seed(1)
df = as.data.frame(matrix(rbinom(23344*89,1, 0.5), ncol=89))
m = as.matrix(df) # deliberately did the coercion outside the benchmark
all.equal(as.data.frame(ifelse(df == 1, rowSums(df) - 1, 0)), df* (rowSums(df) - 1))
microbenchmark::microbenchmark(
a = {ifelse(df == 1, rowSums(df) - 1, 0)},
b = {df* (rowSums(df) - 1)},
c = {m* (rowSums(m) - 1)}
)
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# a 112.29431 142.70233 165.39007 149.7674 157.63988 304.6195 100 b
# b 193.05255 222.24858 245.57206 228.2012 236.38952 402.2677 100 c
# c 18.49041 26.92273 33.77159 27.3092 27.80769 181.4236 100 a
**结果类存在差异,会影响时间。
在 R 中,我有一个带有采样位置和条目的大型数据框(23344 行 x 89 列)。
value 1 表示:在此采样位置找到对象 值 0 表示:未找到此采样位置的对象
要计算每个采样位置(节点)degrees/connections,每行,得到rowsum-1
(因为这等于度数)并将该行中的 1 更改为该值。
此后,我可以获得 colSum()
来计算每个样本位置的总度数。
我的数据框的可重现示例:
loc1 <- c(1,0,1)
loc2 <- c(0,1,1)
loc3 <- c(1,1,0)
loc4 <- c(1,1,0)
loc5 <- c(0,1,0)
df <- data.frame(loc1, loc2, loc3, loc4, loc5)
# loc1 loc2 loc3 loc4 loc5
# 1 1 0 1 1 0
# 2 0 1 1 1 1
# 3 1 1 0 0 0
所需的输出如下所示
# loc1 loc2 loc3 loc4 loc5
# 1 2 0 2 2 0 #rowsum = 3 so change values>1 to 2
# 2 0 3 3 3 3 #rowsum = 4 so change values>1 to 3
# 3 1 1 0 0 0 #rowsum = 2 so change/keep values>1 to 1
我有可用的代码,但速度很慢(包含 for 循环)那么有 better/faster 方法可以做到这一点吗?我知道函数 rowSums()
可能是解决方案的一部分。
我目前的代码如下:
for (r in 1:nrow(df)){
df[r, df[r,] == 1] <- sum(df[r,]) - 1}
degrees_per_sample <- colSums(df)
您可以尝试在数据框上使用 ifelse()
:
df[] <- ifelse(df == 1, rowSums(df) - 1, 0)
给出:
loc1 loc2 loc3 loc4 loc5
1 2 0 2 2 0
2 0 3 3 3 3
3 1 1 0 0 0
您可以使用:
df[] <- +(df > 0) * (rowSums(df) - 1)
df
# loc1 loc2 loc3 loc4 loc5
#1 2 0 2 2 0
#2 0 3 3 3 3
#3 1 1 0 0 0
认为对于这些东西使用矩阵而不是 data.frames 的好处可能有趣:
set.seed(1)
df = as.data.frame(matrix(rbinom(23344*89,1, 0.5), ncol=89))
m = as.matrix(df) # deliberately did the coercion outside the benchmark
all.equal(as.data.frame(ifelse(df == 1, rowSums(df) - 1, 0)), df* (rowSums(df) - 1))
microbenchmark::microbenchmark(
a = {ifelse(df == 1, rowSums(df) - 1, 0)},
b = {df* (rowSums(df) - 1)},
c = {m* (rowSums(m) - 1)}
)
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# a 112.29431 142.70233 165.39007 149.7674 157.63988 304.6195 100 b
# b 193.05255 222.24858 245.57206 228.2012 236.38952 402.2677 100 c
# c 18.49041 26.92273 33.77159 27.3092 27.80769 181.4236 100 a
**结果类存在差异,会影响时间。