在R中,要创建一组新列,我需要将一些列的值乘以另一个数据集中的值.我的数据是这样的:

data1 <- data.frame(
  id  = seq(1:5),
  d01 = c(1.5, 4, 3, 2, 1),
  d02 = c(1, 2, 1, 4.5, 3),
  d03 = c(2, 4, 3, 2, 5)
)

data2 <- data.frame(
  id = c('d01', 'd02', 'd03'),
  w  = c(2, 4, 1.5)
)

我想把data1$d01中的值乘以2,因为在data2中的行中,id == d01w == 2.所以我的结果应该是这样的:

result <- data.frame(
  id  = seq(1:5),
  d01 = c(1.5, 4, 3, 2, 1),
  wd01 = c(3, 8, 6, 4, 2),
  d02 = c(1, 2, 1, 4.5, 3),
  wd02 = c(4, 8, 4, 18, 12),
  d03 = c(2, 4, 3, 2, 5),
  wd03 = c(3, 6, 4.5, 3, 7.5)
)

在我的实际数据中,data1有300.000行,这发生在一个函数中,每次我调用该函数10.000次,就会创建一个不同的data2data1.因此,计算速度是关键,而我可以自己编写的解决这个问题的循环太慢了.data2中的行数(data1+id中的列数和我的实际数据中的其他变量)在每次调用函数时都有所不同,但始终在2到35之间,而data2中的列数始终为4(为简单起见,未在此处显示列).我需要相乘的列data1总是从d0开始,并且是唯一具有这种命名模式的列(data2中的单元格的值也是如此).

在函数内部,我还需要将data1中的列与data2中不同列中的值相乘,因此我倾向于使用相乘后的值向data1中添加列,而不是修改原始列.

推荐答案

下面是一个简单的for循环方法.如果有更快的解决方案而不reshape 你的数据,试图将其转化为矩阵乘法,我会感到惊讶,但reshape 可能会比这一次操作获得的效率yield 更昂贵.

提高速度的一种方法可能是使用data.table个数据帧而不是基本数据帧.或者可能只使用矩阵--在您的示例中没有显示非数字数据;如果matrix适用于data1,而data2是列顺序正确的向量,那么速度可能会快一些.

for(i in 1:nrow(data2)) {
  data1[paste0("w", data2$id[i])] = data1[[data2$id[i]]] * data2$w[i]
}

data1
#   id d01 d02 d03 wd01 wd02 wd03
# 1  1 1.5 1.0   2    3    4  3.0
# 2  2 4.0 2.0   4    8    8  6.0
# 3  3 3.0 1.0   3    6    4  4.5
# 4  4 2.0 4.5   2    4   18  3.0
# 5  5 1.0 3.0   5    2   12  7.5

事实证明,我的直觉是错误的,for循环方法比矩阵乘法快得多,但lapply方法的速度要快得多.这里有一个关于"全尺寸"数据的基准测试(首先是结果,然后是基准测试代码).

bench::mark(
  matrix_mult = matrix_mult(m1, w),
  for_loop = for_loop(data1, data2),
  lapply = lapply_replace(data1, data2),
  pivot = pivot(data1, data2),
  check = FALSE
)
  
#   expression       min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory               time            gc      
#   <bch:expr>  <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list>               <list>          <list>  
# 1 matrix_mult 226.35ms 264.33ms     3.78    323.9MB     3.78     2     2   528.65ms <NULL> <Rprofmem [90 × 3]>  <bench_tm [2]>  <tibble>
# 2 for_loop     47.28ms     52ms    17.4     120.3MB     1.93     9     1   516.81ms <NULL> <Rprofmem [440 × 3]> <bench_tm [9]>  <tibble>
# 3 lapply         8.4ms   9.34ms    84.9      81.3MB     5.93    43     3   506.21ms <NULL> <Rprofmem [244 × 3]> <bench_tm [43]> <tibble>
# 4 pivot          1.99s    1.99s     0.503     2.5GB     2.01     1     4      1.99s <NULL> <Rprofmem [737 × 3]> <bench_tm [1]>  <tibble>
# Warning message:
# Some expressions had a GC in every iteration; so filtering is disabled. 

基准代码:

set.seed(47)
nr = 3e5
nc = 35
data1 = data.frame(id = 1:nr, replicate(nc, runif(nr)))
names(data1)[-1] = sprintf("d%02d", 1:nc)
data2 = data.frame(id = sprintf("d%02d", 1:nc), w = runif(nc))

m1 = as.matrix(data1)
w = data2$w

matrix_mult = function(m1, w) {
  res = m1[, -1] %*% diag(w)
  colnames(res) = paste0("w", colnames(m1)[-1])
  cbind(m1, res)
}

for_loop = function(d1, d2) {
  for(i in 1:nrow(d2)) {
      d1[paste0("w", d2$id[i])] = d1[[d2$id[i]]] * d2$w[i]
  }
  d1
}

lapply_replace = function(d1, d2) {
   d1[paste0("w",names(d1)[-1])] <- lapply(names(d1)[-1], \(x) {
      d1[,match(x, d2$id)+1] * d2[match(x, d2$id), "w"]
    })
   d1
}

pivot = function(d1, d2) {
  d1 %>%
      # Unpivot to 'id' | 'name' | 'value'.
      pivot_longer(!id) %>%
      # Match each 'w' to its appropriate column 'name'.
      left_join(d2,
        join_by(name == id)
      ) %>%
      mutate(
        # Multiply by 'w'...
        prod = value * w,
        # ...and also prefix the column 'name' with "w".
        name = paste0("w", name)
      ) %>%
      # Pivot to 'id' | 'wd01' | ... | 'wd03'. 
      pivot_wider(
        id_cols = id,
        values_from = prod,
        names_from = name
      ) %>%
      # Use 'id' to associate each 'wd*' with its 'd*'.
      right_join(d1, "id")
}

R相关问答推荐

使用Shiny组合和显示复制和粘贴的数据

卸载安装了BRM的模型发出的警告

如何根据包含相同值的某些列获取总额

在值和NA的行顺序中寻找中断模式

如何根据条件计算时差(天)

查找图下的面积

如何根据组大小应用条件过滤?

有没有一个R函数允许你从一个数字变量中提取一个数字,而不考虑它的位置(不仅仅是第一个或最后一个数字?

将非重复序列高效转换为长格式

如何编辑gMarginal背景以匹配绘图背景?

在R中使用Scale_y_Break后更改y轴标签

在R gggplot2中是否有一种方法将绘图轴转换成连续的 colored颜色 尺度?

如何在R中描绘#符号?

仅当后续值与特定值匹配时,才在列中回填Nas

如何为混合模型输出绘制不同的线型?

如何创建直方图与对齐的每月箱?

如何准确地指出Read_delim所面临的问题?

真实世界坐标的逆st_变换

如何从矩阵绘制环弦图

Gggvenn为Venn增加了不存在的价值