【问题标题】:Faster code for changing values per row to rowsum-1 where value is 1将每行值更改为值为 1 的 rowsum-1 的更快代码
【发布时间】:2020-07-29 12:20:02
【问题描述】:

在 R 中,我有一个带有采样位置和条目的大型数据框(23344 行 x 89 列)。

值 1 表示:在此采样位置找到的对象 值 0 表示:在此采样位置找不到对象

要计算每个采样位置(节点)的度数/连接数,我想每行获取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 循环),那么有没有更好/更快的方法来做到这一点?我知道函数 rowSums() 可能是解决方案的一部分。

我目前的代码如下:

for (r in 1:nrow(df)){
    df[r, df[r,] == 1] <- sum(df[r,]) - 1}

degrees_per_sample <- colSums(df)

【问题讨论】:

  • 如果您的数据都是数字的,那么使用矩阵会更快。你可以做df* (rowSums(df) - 1),但 df is a matrix 会更快

标签: r dataframe rowsum


【解决方案1】:

认为使用矩阵而不是 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 中使用数据帧而不是矩阵,所以这个建议非常有用,将来会对我有所帮助。它确实比使用数据帧快 10 倍。复选标记保留上面的答案,因为它为我节省了强制我的 df 的步骤。
【解决方案2】:

你可以使用:

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

【讨论】:

    【解决方案3】:

    您可以尝试在数据框上使用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
    

    【讨论】:

    • 这也有效,谢谢,我接受了@Ronak Shah 他的回答,因为它只是快了一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-16
    • 2013-02-17
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 2013-05-10
    • 1970-01-01
    相关资源
    最近更新 更多