R:如何从不同系列的列中重复减去特定列,并输出到新的数据框?

R: How to repeatedly subtract specific columns from different series of columns, and output to a new dataframe?

我有一个宽格式的数据框,我想从不同系列的列中减去特定的列。理想情况下,我希望结果位于新的数据框中。

例如: 从这个示例数据框 (dfOld) 中,我希望 A、B 和 C 列每个减去 D,E、F 和 G 列每个减去 H 列。在实际数据集中,这会继续进行,需要迭代。

image of dfOld as table

示例数据:

dfOld <- data.frame(ID = c(1,2,3,4,5,6,7,8,9,10), A = c(2, 3, 4,5,4,6,7,1,9,12), B = c(3, 4, 5,2,4,5,1,7,0,8), C = c(5, 6, 7,2,4,1,5,4,6,13), D = c(68, 7, 8,2,1,5,7,9,78,7), E = c(2, 3, 42,5,4,6,7,1,9,12), F = c(37, 4, 5,2,48,5,1,7,60,8), G = c(5, 6, 7,2,4,1,5,4,6,13), H = c(35, 7, 8,2,1,5,7,9,78,7))

理想情况下,结果会在一个新的数据框中,其中的列具有 A-D、B-D、C-D、E-H、F-H、G-H 的值和名称,如下所示:

image of dfNew as table

在 Excel 中,公式将是“=B2-$E2”,向下拖动行,跨 3 列,然后再次重复“F2-$I2”等,使用“$ " 标志以锁定列

在 R 中,我只能手动执行此操作,有点像之前针对类似问题发布的答案 (Subtracting two columns to give a new column in R)

dfOld$A-D<-(dfOld$A-dfOld$D)
dfOld$B-D<-(dfOld$B-dfOld$D)
dfOld$C-D<-(dfOld$C-dfOld$D)
dfOld$E-H<-(dfOld$E-dfOld$H)
dfOld$F-H<-(dfOld$F-dfOld$H)
dfOld$G-H<-(dfOld$G-dfOld$H)

然后将新列分离到新数据集中。

但是,对于我更大的数据集,这显然不可扩展,我真的很想了解如何进行这种在 Excel 中非常简单的操作(尽管仍然不可扩展大数据集)。

部分答案可能已经在这里: 但是这个答案(其他几个类似的答案)改变了同一数据框中的值,并且列保持相同的名称。 我无法对其进行调整,以便新值具有新列和新名称(最好在新数据框中)

另一部分答案可能在这里: 这些答案将减去的结果放在具有新名称的新列中,但此数据框中的每一列都会减去所有其他列的值(A、B、C、D、E、F、G、H 各减去 C)。而且我似乎无法对其进行调整,使其适用于特定系列的列(A、B、C 各减去 D,然后 E、F、G 各减去 H,等等)

在此先感谢您的帮助。

可能其他人有更好的方法 - 但这是一种可能性。

  1. 加载两个库并将dfOld设置为data.table
library(data.table)
library(magrittr)
setDT(dfOld)
  1. 获取有关列的信息,并制作成列表。
lv = names(dfOld)[-1][seq(1,ncol(dfOld)-1)%%4>0]
lv = split(lv, ceiling(seq_along(lv)/3))
names(lv) = names(dfOld)[-1][seq(1,ncol(dfOld)-1)%%4==0]

lv 看起来像这样:

> lv
$D
[1] "A" "B" "C"

$H
[1] "E" "F" "G"
  1. 这有点令人费解,但基本上,我正在获取 lv 列表中的每个元素,并且我正在重塑 dfOld 中的列,因此我可以进行所有减法立刻。然后我只保留我需要的变量,并使用 rbindlist
  2. 将每个 data.table 的结果列表绑定到一个数据表中
res =rbindlist(lapply(names(lv), function(x)  {
  melt(dfOld,id=c("ID", x),measure.vars = lv[[x]]) %>% 
    .[,`:=`(nc=value-get(x),variable=paste0(variable,"-",x))] %>%
    .[,.(ID,variable,nc)]
}))
  1. 最后一步很简单 - 只需 dcast 返回
dcast(res,ID~variable, value.var="nc")

输出

    ID A-D B-D C-D E-H F-H G-H
 1:  1 -66 -65 -63 -33   2 -30
 2:  2  -4  -3  -1  -4  -3  -1
 3:  3  -4  -3  -1  34  -3  -1
 4:  4   3   0   0   3   0   0
 5:  5   3   3   3   3  47   3
 6:  6   1   0  -4   1   0  -4
 7:  7   0  -6  -2   0  -6  -2
 8:  8  -8  -2  -5  -8  -2  -5
 9:  9 -69 -78 -72 -69 -18 -72
10: 10   5   1   6   5   1   6

首先,我创建了一个函数来进行简单的计算,其中我们有数据框,然后是列名作为输入。然后,我使用 purrr map2 来传递函数(我复制了所需的次数,在本例中为 6)。然后,我提供参数列表以将该函数应用于每个列对。然后,我使用 invoke 来应用函数和参数。现在,我们留下了一个数据框列表(因为输出是一个单独的列,使用 ID). Then, I use reduce` 将它们组合回一个数据框,然后更新列名。

library(tidyverse)

subtract <- function(x, a, b){
  x %>% 
    mutate(!! a  := !!rlang::parse_expr(a) - !!rlang::parse_expr(b)) %>% 
    dplyr::select(ID, which(colnames(x)==a))
}

col_names <- c("ID", "A-D", "B-D", "C-D", "E-H", "F-H", "G-H")

map2(
  flatten(list(rep(list(
    subtract
  ), 6))),
  list(
    expression(a = "A", b = "D"),
    expression(a = "B", b = "D"),
    expression(a = "C", b = "D"),
    expression(a = "E", b = "H"),
    expression(a = "F", b = "H"),
    expression(a = "G", b = "H")
  ),
  ~ invoke(.x, c(list(dfOld), as.list(.y)))
) %>%
  reduce(left_join, by = "ID") %>% 
  set_names(col_names)

输出

   ID A-D B-D C-D E-H F-H G-H
1   1 -66 -65 -63 -33   2 -30
2   2  -4  -3  -1  -4  -3  -1
3   3  -4  -3  -1  34  -3  -1
4   4   3   0   0   3   0   0
5   5   3   3   3   3  47   3
6   6   1   0  -4   1   0  -4
7   7   0  -6  -2   0  -6  -2
8   8  -8  -2  -5  -8  -2  -5
9   9 -69 -78 -72 -69 -18 -72
10 10   5   1   6   5   1   6

数据

dfOld <- structure(
  list(
    ID = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
    A = c(2,
          3, 4, 5, 4, 6, 7, 1, 9, 12),
    B = c(3, 4, 5, 2, 4, 5, 1, 7, 0,
          8),
    C = c(5, 6, 7, 2, 4, 1, 5, 4, 6, 13),
    D = c(68, 7, 8, 2,
          1, 5, 7, 9, 78, 7),
    E = c(2, 3, 42, 5, 4, 6, 7, 1, 9, 12),
    F = c(37,
          4, 5, 2, 48, 5, 1, 7, 60, 8),
    G = c(5, 6, 7, 2, 4, 1, 5, 4, 6,
          13),
    H = c(35, 7, 8, 2, 1, 5, 7, 9, 78, 7)
  ),
  class = "data.frame",
  row.names = c(NA,-10L)
)