如何将两个小标题乘以列名

How can I multiply two tibbles by column names

我有两个列名相同的小标题:

tb1:

   date        a     b     c     d     e
  <date>     <dbl> <dbl> <dbl> <dbl> <dbl>
1 2017-06-01  113.  182.  21.0  31.9  111.

tb2:

   date        a     b     c     d     e
  <date>     <dbl> <dbl> <dbl> <dbl> <dbl>
1 2016-05-01  122. 106.   23.9  43.7  93.5
2 2016-06-01  117. 111.   20.8  41.6  111. 
3 2016-07-01  116.  94.4  22.5  41.0  92.4

我想将 tb2 的每一行乘以 tb1 的相应数字,有没有一种简单的方法可以用 dplyr 或其他方式做到这一点?我需要保留 tb2 中的日期,但我已将其从 tb1 中删除。

我们可以使 tb1tb2 中的行相等,然后将两个大小相等的数据帧相乘。

cbind(tb2[1], tb1[rep(1, nrow(tb2)), -1] * tb2[-1])

#       date     a       b     c       d       e
#1 2016-05-01 13786 19292.0 501.9 1394.03 10378.5
#2 2016-06-01 13221 20202.0 436.8 1327.04 12321.0
#3 2016-07-01 13108 17180.8 472.5 1307.90 10256.4

如果tb1tb2中的列顺序不一样,我们可以先做

tb1 <- tb1[match(names(tb2), names(tb1))]

然后使用上面的。

数据

tb1 <- structure(list(date = structure(1L, .Label = "2017-06-01", class = "factor"),
a = 113, b = 182, c = 21, d = 31.9, e = 111), class = "data.frame", row.names = "1")

tb2 <- structure(list(date = structure(1:3, .Label = c("2016-05-01", 
"2016-06-01", "2016-07-01"), class = "factor"), a = c(122, 117, 
116), b = c(106, 111, 94.4), c = c(23.9, 20.8, 22.5), d = c(43.7, 
41.6, 41), e = c(93.5, 111, 92.4)), class = "data.frame", 
row.names = c("1", "2", "3"))

为了很好地扩展并处理不匹配或乱序的列以及不同的行数,我会尝试重塑数据。如果您将两个数据集都制作成长格式,您将拥有 a、b、c... 列,您可以使用这些列将数据集连接在一起。保留您需要的列并重塑回宽格式。

我选择使用左连接,以便保留 tb2 中的每一行(具有更多行的行),即使它在 tb1 中没有匹配的行。我还设置了后缀参数,以使其更清楚每列来自哪个数据集。

library(dplyr)
library(tidyr)

left_join(
  pivot_longer(tb2, -date),
  pivot_longer(tb1, -date),
  by = "name", suffix = c("2", "1")
) %>%
  mutate(value = value1 * value2) %>%
  select(date = date2, name, value) %>%
  pivot_wider()
#> # A tibble: 3 x 6
#>   date           a      b     c     d      e
#>   <fct>      <dbl>  <dbl> <dbl> <dbl>  <dbl>
#> 1 2016-05-01 13786 19292   502. 1394. 10378.
#> 2 2016-06-01 13221 20202   437. 1327. 12321 
#> 3 2016-07-01 13108 17181.  472. 1308. 10256.

另一种选择是在 tb1tb2 的数字列上使用 base-R 的 sweep(检查 ?sweep 的用法),假设这些列是同样的顺序:

## sweep multiplication
tb3 <- sweep(data.matrix(tb2)[, -1], MARGIN = 2, STATS = data.matrix(tb1)[, -1], FUN = "*")

## convert back to data.frame
cbind(date = tb2[, 1], as.data.frame(tb3))
#>         date     a       b     c       d       e
#> 1 2016-05-01 13786 19292.0 501.9 1394.03 10378.5
#> 2 2016-06-01 13221 20202.0 436.8 1327.04 12321.0
#> 3 2016-07-01 13108 17180.8 472.5 1307.90 10256.4

另一个基本的 R 解决方案是使用 kronecker()tb1 中的行扩展为与 tb2[-1] 大小相同的矩阵,即:

res <- cbind(tb2[1],kronecker(rep(1,nrow(tb2)),as.matrix(tb1[-1]))*tb2[-1])

这样

> res
        date     a       b     c       d       e
1 2016-05-01 13786 19292.0 501.9 1394.03 10378.5
2 2016-06-01 13221 20202.0 436.8 1327.04 12321.0
3 2016-07-01 13108 17180.8 472.5 1307.90 10256.4