【问题标题】:R data.table dot product with matching column names (for each group)具有匹配列名的 R data.table 点积(对于每个组)
【发布时间】:2016-08-03 12:48:24
【问题描述】:

我有一个数据数据表和一个拟合系数数据表。我想计算每一行的拟合值。

dt = data.table(a = rep(c("x","y"), each = 5), b = rnorm(10), c = rnorm(10), d = rnorm(10))
coefs = data.table(a = c("x","y"), b = c(0, 1), d = c(2,3))
dt
#    a           b          c           d
# 1: x -0.25174915 -0.2130797 -0.67909764
# 2: x -0.35569766  0.6014930  0.35201386
# 3: x -0.31600957  0.4398968 -1.15475814
# 4: x -0.54113762 -2.3497952  0.64503654
# 5: x  0.11227873  0.0233775 -0.96891456
# 6: y  1.24077566 -1.2843439  1.98883516
# 7: y -0.23819626  0.9950835 -0.17279980
# 8: y  1.49353589  0.3067897 -0.02592004
# 9: y  0.01033722 -0.5967766 -0.28536224
#10: y  0.69882444  0.8702424  1.24131062

coefs # NB no "c" column
#   a b d
#1: x 0 2
#2: y 1 3

对于 dt 中的每个a=="x" 行,我想要0*b+2*d;对于 dt 中的每个 a=="y" 行,我想要 1*b+3*d

是否有一种无需对列名进行硬编码的数据表方法?我很乐意将列名放入变量 cols = colnames(coefs)[-1]

很容易将组和rbind 循环在一起,所以如果分组造成问题,请忽略该部分。

【问题讨论】:

  • 我的类似问题:stackoverflow.com/q/19279075 对于它的价值,我认为人们会关心动态匹配名称是很自然的,这根本不会使这个问题成为“移动目标”。

标签: r data.table


【解决方案1】:

加入data.tables:

dt[coefs, res := b * i.b + d * i.d, on = "a"]
 #   a           b            c          d        res
 #1: x  0.09901786 -0.362080111 -0.5108862 -1.0217723
 #2: x -0.16128422  0.169655945  0.3199648  0.6399295
 #3: x -0.79648896 -0.502279345  1.3828633  2.7657266
 #4: x -0.26121421  0.480548972 -1.1559392 -2.3118783
 #5: x  0.54085591 -0.601323442  1.3833795  2.7667590
 #6: y  0.83662761  0.607666970  0.6320762  2.7328562
 #7: y -1.92510391 -0.050515610 -0.3176544 -2.8780671
 #8: y  1.65639926 -0.167090105  0.6830158  3.7054466
 #9: y  1.48772354 -0.349713539 -1.2736467 -2.3332166
#10: y  1.49065993  0.008198885 -0.1923361  0.9136516

通常您会在此处使用矩阵乘积,但这意味着您必须将相应的子集强制转换为矩阵。这将导致制作副本,并且由于 data.tables 主要用于较大的数据,因此您希望避免复制。

如果您需要动态列名,想到的最简单的解决方案实际上是eval/parse 构造:

cols = colnames(coefs)[-1]
expr <- parse(text = paste(paste(cols, paste0("i.", cols), sep = "*"), collapse = "+"))
#expression(b*i.b+d*i.d)

dt[coefs, res := eval(expr), on = "a"] 

也许其他人可以提出更好的解决方案。

这是一个使用矩阵乘法的解决方案:

dt[, res := as.matrix(.SD) %*% unlist(coefs[a == .BY, .SD, .SDcols = cols]), 
  by = "a", .SDcols = cols]

当然,这会生成副本,这可能不如 eval 解决方案效率高。

【讨论】:

  • 谢谢。是否可以不对列名进行硬编码?我很乐意将它们放在像 cols = colnames(coefs)[-1] 这样的变量中,然后如何从那里开始?
  • 请不要让您的问题成为移动目标。提出问题时提供所有规范。
【解决方案2】:

我发现所有数值类型列的data.table都可以进行算术运算(+、-、*、/),但没有名称匹配——只是顺序匹配。

> coefs
   a b d
1: x 0 2
2: y 1 3
> coefs[, .(b,d)] * coefs[, .(b,d)]
   b d
1: 0 4
2: 1 9
> coefs[, .(b,d)] * coefs[, .(d,b)]
   b d
1: 0 0
2: 3 3

所以基于此的解决方案

> cols = colnames(coefs)[-1]
> zz = rowSums(coefs[dt[,.(a)], .SD, on = 'a', .SDcols = cols] * dt[, .SD, .SDcols = cols])
> dt[, newcol := zz]

【讨论】:

  • 如果您不介意复制(rowSums 通过将 data.table 强制转换为矩阵来实现),您应该使用我的最后一个建议,即矩阵乘法。
【解决方案3】:

另一种替代方法(但速度较慢)是:

dt$res <- unsplit(Map(function(x,y){x$b*y$b + x$d*y$d}, split(dt, dt$a=="x"), 
                 split(coefs,coefs$a=="x")),dt$a=="x")

    dt
    a           b          c          d        res
 1: x  0.47859729  1.3479271  0.5691897  1.1383794
 2: x  0.28491505 -0.3291934  1.8621365  3.7242730
 3: x -1.43894695  1.5555413  0.3685772  0.7371544
 4: x  0.04360066  0.1358920  0.5240700  1.0481400
 5: x -1.39897890 -0.0175886 -0.6876451 -1.3752901
 6: y -0.60952146  1.2331907 -0.3582176 -1.6841742
 7: y  0.31777772  1.4090295 -0.4053615 -0.8983067
 8: y  0.42758431 -0.3746061  2.1208417  6.7901094
 9: y -0.60701063 -0.9232092  1.9386482  5.2089341
10: y -1.52042316 -0.8871454 -0.9314232 -4.3146927

如果您的数据已经是 data.frames,那么同样的代码也可以在基础 R 中工作。

【讨论】:

  • 对,只是想展示一种不同的做法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-06
  • 2018-01-16
  • 1970-01-01
  • 1970-01-01
  • 2021-09-20
  • 2020-12-21
相关资源
最近更新 更多