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,等等)
在此先感谢您的帮助。
可能其他人有更好的方法 - 但这是一种可能性。
- 加载两个库并将
dfOld
设置为data.table
library(data.table)
library(magrittr)
setDT(dfOld)
- 获取有关列的信息,并制作成列表。
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"
- 这有点令人费解,但基本上,我正在获取
lv
列表中的每个元素,并且我正在重塑 dfOld
中的列,因此我可以进行所有减法立刻。然后我只保留我需要的变量,并使用 rbindlist
将每个 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)]
}))
- 最后一步很简单 - 只需
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)
)
我有一个宽格式的数据框,我想从不同系列的列中减去特定的列。理想情况下,我希望结果位于新的数据框中。
例如: 从这个示例数据框 (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 中非常简单的操作(尽管仍然不可扩展大数据集)。
部分答案可能已经在这里:
另一部分答案可能在这里:
在此先感谢您的帮助。
可能其他人有更好的方法 - 但这是一种可能性。
- 加载两个库并将
dfOld
设置为data.table
library(data.table)
library(magrittr)
setDT(dfOld)
- 获取有关列的信息,并制作成列表。
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"
- 这有点令人费解,但基本上,我正在获取
lv
列表中的每个元素,并且我正在重塑dfOld
中的列,因此我可以进行所有减法立刻。然后我只保留我需要的变量,并使用rbindlist
将每个 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)]
}))
- 最后一步很简单 - 只需
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)
)