如何使用dplyr:mutate将由变量名的部分指定的列成对相乘

4

我有以下例子:

df <- data.frame(
id = c(1,2,3),
  fix_01.2012 = c(2,5,7),
  fix_02.2012 = c(5,1,7),
  fix_03.2012 = c(6,1,5),
  fox_01.2012 = c(0.4, 0.5, 0.7),
  fox_02.2012 = c(0.6, 0.5, 0.8),
  fox_03.2012 = c(0.7, 0.5, 0.9)
  )

  id fix_01.2012 fix_02.2012 fix_03.2012 fox_01.2012 fox_02.2012 fox_03.2012
1  1           2           5           6         0.4         0.6         0.7
2  2           5           1           1         0.5         0.5         0.5
3  3           7           7           5         0.7         0.8         0.9

下面的表格是我想要获取的内容。 我想为每个日期(例如“01.2012”)创建一个新列:
res_date = fix_date * fox_date 由于我有许多日期/日期对,所以我猜需要通过循环来完成此操作。
 id fix_01.2012 fix_02.2012 fix_03.2012 fox_01.2012 fox_02.2012 fox_03.2012 res_01.2012 res_02.2012 res_03.2012
1  1           2           5           6         0.4         0.6         0.7         0.8         3.0         4.2
2  2           5           1           1         0.5         0.5         0.5         2.5         0.5         0.5
3  3           7           7           5         0.7         0.8         0.9         4.9         5.6         4.5

有人可以帮忙吗?非常感谢!
3个回答

2

如果您想使用 tidyverse 方法,需要使用一些整洁的评估来获得所需结果。

library(tidyverse)

df <- data.frame(
  id = c(1,2,3),
  fix_01.2012 = c(2,5,7),
  fix_02.2012 = c(5,1,7),
  fix_03.2012 = c(6,1,5),
  fox_01.2012 = c(0.4, 0.5, 0.7),
  fox_02.2012 = c(0.6, 0.5, 0.8),
  fox_03.2012 = c(0.7, 0.5, 0.9)
)

# colnames with "fix" 
fix <- names(df)[grepl("fix",names(df))]

# colnames with "fox"
fox <- names(df)[grepl("fox",names(df))]

# Iterate over the two vectors of names and column bind the results (map2_dfc).  
# Since these are strings, we need to have them evaluated as symbols
# Creating the column name just requires the string to be evaluated.

map2_dfc(fix, fox, ~transmute(df, !!paste0("res", str_extract(.x, "_(0\\d)")) := !!sym(.x) * !!sym(.y)))

#>   res_01 res_02 res_03
#> 1    0.8    3.0    4.2
#> 2    2.5    0.5    0.5
#> 3    4.9    5.6    4.5

谢谢您的回答和解释!这使得我很容易理解您的方法。 - user138089
非常简洁的代码!但是这需要列按正确顺序排列,否则将会错误地相乘。 - dufei

2
这是一个使用split.default根据相似的列名(基于您的条件)拆分数据框的想法。然后我们循环遍历该列表并乘以列。在这种情况下,我们使用Reduce(而不是i[1]*i[2])来进行乘法运算,以便考虑到超过两列的情况。
do.call(cbind, 
   lapply(split.default(df[-1], gsub('.*_', '', names(df[-1]))), function(i) Reduce(`*`, i)))

#     01.2012 02.2012 03.2012
#[1,]     0.8     3.0     4.2
#[2,]     2.5     0.5     0.5
#[3,]     4.9     5.6     4.5

使用cbind.data.frame()将它们与原始数据框绑定。

1
使用sapply可以减少do.call步骤。 sapply(split.default(df[-1], gsub('.*_', '', names(df[-1]))), function(i) Reduce(*, i)) 。顺便说一句,答案很好。 - Ronak Shah

1
比其他答案更冗长,但在我看来更易于阅读/编辑/调整的方法是采用大量收集-扩展的方法(如果我逐步解决问题,这就是我推理问题的方式):
library(tidyr)
library(dplyr)

df %>% 
  gather(-id, key=colname, value=value) %>% 
  separate(colname, c('fixfox', 'date'), sep='_') %>% 
  spread(key=fixfox, value=value) %>% 
  mutate(res=fix*fox) %>% 
  gather(-id, -date, key=colname, value=value) %>% 
  unite(new_colname, colname, date, sep='_') %>% 
  spread(key=new_colname, value=value)

是的,同意。这是一个非常好的方法,使得阅读和适应变得容易。非常感谢! - user138089

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接