【问题标题】:Normalize each row of data.table规范化data.table的每一行
【发布时间】:2015-12-13 13:44:25
【问题描述】:

这似乎应该很容易,但我找不到答案:(。我正在尝试像这样规范化 data_table 的每一行:

normalize <- function(x) {
  s = sum(x)
  if (s>0) {
    return(x/s)
  } else {
    return 0
  }
}

如何在 data.table 的每一行上调用此函数并返回规范化的 data.table?我可以做一个 for 循环,但这肯定不是正确的方法,据我所知,apply(data, 1, normalize) 会将我的 data.table 转换为矩阵,这将对性能产生很大影响。

【问题讨论】:

  • 另一种解读是:“在这里使用 data.table 而不是矩阵会带来很大的性能损失”(而不是认为 apply 会带来它)。下面的最佳答案 rowSums 仍然会为您强制转换为矩阵。
  • @Frank Fair 点。我正在尝试在大量非常大的矩阵上执行此操作,并且在 data.table 中使用 fread() 比其他任何方法都快得多,所以我希望我可以留在 data.table 的“域”中并执行这很快。你是说我还不如转换成矩阵并运行上面的apply 命令,因为没有更快的方法来实现这一点?
  • 是的,我认为您不妨转换为矩阵并使用rowSums 和其他(无论出于何种原因)比相应的apply 方法更快的函数。我可以想到rowMeanscol,但可能还有其他人。希望其他人在这方面不同意我的观点。
  • 既然你排除了s &lt; 0的情况,我们可以认为x都是非负数吗?

标签: r performance data.table normalization


【解决方案1】:

这就是我想出的。首先你需要编辑你的函数(我相信),让它返回rep(0, length(x)) 而不仅仅是0

set.seed(123); DT <- data.table(x=rnorm(1e3), y=rnorm(1e3), z=rnorm(1e3))
> DT
                x           y          z
   1: -0.56047565 -0.99579872 -0.5116037
   2: -0.23017749 -1.03995504  0.2369379
   3:  1.55870831 -0.01798024 -0.5415892
   4:  0.07050839 -0.13217513  1.2192276
   5:  0.12928774 -2.54934277  0.1741359
  ---                                   
 996: -0.08997520  0.07664366  1.0609662
 997:  1.07051604  0.25516476 -0.4455056
 998: -1.35110039  0.27744682 -0.4291802
 999: -0.52261670  0.53685602  1.1890118
1000: -0.24919068 -0.46048557  0.8342941
> DT[, c('x', 'y', 'z') := as.list(normalize(c(x, y, z))), by=1:nrow(DT)]                                                                             
> DT
                x           y          z
   1:  0.00000000  0.00000000  0.0000000
   2:  0.00000000  0.00000000  0.0000000
   3:  1.56005167 -0.01799574 -0.5420559
   4:  0.06091117 -0.11418417  1.0532730
   5:  0.00000000  0.00000000  0.0000000
  ---                                   
 996: -0.08588413  0.07315877  1.0127254
 997:  1.21625341  0.28990225 -0.5061557
 998:  0.00000000  0.00000000  0.0000000
 999: -0.43433718  0.44617122  0.9881660
1000: -1.99963905 -3.69518205  6.6948211

【讨论】:

    【解决方案2】:

    使用 apply 可能有一种更简单(更快)的方法来执行此操作,但此方法有效。我认为它也更具可读性,但这只是我的看法。

    # Creating sample data.
    myDF <- data.frame(a = seq(1, 50), b = seq(1, 100, 2) , c = seq(1, 200, 4))
    # Going through each row and dividing its contents by the sum of that row.
    for (row in rownames(myDF)) { myDF[row, ] <- myDF[row, ] / sum(myDF[row, ]) }
    

    请注意,这确实要求您的行名是数字。

    【讨论】:

      【解决方案3】:

      考虑这个示例数据集(下次请自己提供示例数据集)

      set.seed(123) 
      DT <- data.table(x = rnorm(10), y = rnorm(10), z = rnorm(10))
      

      我会尝试避免按行操作并使用 rowSums 进行 vecotrize,如下所示

      DT[, names(DT) := {temp = rowSums(.SD) ; (.SD / temp) * (temp > 0)}]
      DT
      #              x          y          z
      #  1:  0.0000000  0.0000000  0.0000000
      #  2:  0.0000000  0.0000000  0.0000000
      #  3:  1.6697906  0.4293327 -1.0991233
      #  4:  0.0000000  0.0000000  0.0000000
      #  5:  0.0000000  0.0000000  0.0000000
      #  6:  0.9447911  0.9843707 -0.9291618
      #  7:  0.2565558  0.2771142  0.4663301
      #  8:  0.0000000  0.0000000  0.0000000
      #  9:  0.0000000  0.0000000  0.0000000
      # 10: -1.3289000 -1.4097961  3.7386962
      

      我创建temp 的原因是为了避免两次运行rowSums(.SD)*(temp &gt; 0) 部分基本上是您的 ifelse 声明。它返回TRUE/FALSE 的逻辑向量,然后将其转换为1/0,然后乘以(.SD/temp)

      【讨论】:

        【解决方案4】:

        这是避免强制转换为矩阵的一种方法:

        cols = names(DT)
        DT[, s := Reduce("+",.SD)]
        DT[s > 0, (cols) := lapply(.SD,"/",s), .SDcols = cols]
        DT[s <=  0, (cols) := 0]
        DT[, s := NULL]
        

        如果有充分的理由在矩阵上使用 data.table(在后面的步骤中),我会这样做。

        【讨论】:

        • 谢谢,很有帮助!
        • 您当然想从s &gt;= 0 中排除=。至于&lt; 0 的情况,OP 隐含的data 可能是非负数。
        • @smci 谢谢,已修复。
        猜你喜欢
        • 2018-05-18
        • 1970-01-01
        • 2022-08-03
        • 1970-01-01
        • 2013-03-08
        • 2019-04-30
        • 2012-05-22
        • 2016-05-17
        相关资源
        最近更新 更多