R 在 purrr::map 中使用 dplyr::mutate() 而不重复行
R using dplyr::mutate() within purrr::map without duplicating rows
这是数据:
library(tidyverse)
col_pre <- c('a', 'b', 'c')
df <- tibble(a1 = 1:3, a2 = 4:6, b1 = 7:9, b2 = 10:12, c1 = 13:15, c2 = 16:18)
我想使用 purrr::map()
和 dplyr::mutate()
创建三个新列,它们是 df
中列的总和。
我可以使用 map()
迭代 a、b、c 列前缀的向量。我想出了 tidyeval
操作,以便
下面的代码运行没有错误。
out <- col_pre %>%
map_df(~ df %>%
mutate(!!as.name(paste0(.x, '3')) := !!as.name(paste0(.x, '1')) + !!as.name(paste0(.x, '2')))
)
但是,out
现在有六个虚假行:
a1 a2 b1 b2 c1 c2 a3 b3 c3
1 1 4 7 10 13 16 5 NA NA
2 2 5 8 11 14 17 7 NA NA
3 3 6 9 12 15 18 9 NA NA
4 1 4 7 10 13 16 NA 17 NA
5 2 5 8 11 14 17 NA 19 NA
6 3 6 9 12 15 18 NA 21 NA
7 1 4 7 10 13 16 NA NA 29
8 2 5 8 11 14 17 NA NA 31
9 3 6 9 12 15 18 NA NA 33
它所做的是不必要地复制输入的三行 df
。
这是我想要的输出:
a1 a2 b1 b2 c1 c2 a3 b3 c3
1 1 4 7 10 13 16 5 17 29
2 2 5 8 11 14 17 7 19 31
3 3 6 9 12 15 18 9 21 33
我觉得 purrr::reduce()
可能是解决方案,但我不确定如何应用它。
感谢任何帮助!
我们可以在进行评估之前将字符串转换为 sym
bol,而不是 mutate
使用 transmute
然后将列与原始数据集绑定
library(stringr)
library(purrr)
library(dplyr)
col_pre %>%
map_dfc(~ df %>%
transmute(!! str_c(.x, '3') := !! rlang::sym(str_c(.x, '1')) +
!! rlang::sym(str_c(.x, 2)))) %>%
bind_cols(df, .)
# A tibble: 3 x 9
# a1 a2 b1 b2 c1 c2 a3 b3 c3
# <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1 1 4 7 10 13 16 5 17 29
#2 2 5 8 11 14 17 7 19 31
#3 3 6 9 12 15 18 9 21 33
或者另一种选择是 parse_exprs
df %>%
mutate(!!! rlang::parse_exprs(str_c(sprintf("%s1 + %s2",
col_pre, col_pre), collapse=";"))) %>%
rename_at(vars(contains("+")), ~ str_c(col_pre, 3))
# A tibble: 3 x 9
# a1 a2 b1 b2 c1 c2 a3 b3 c3
# <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1 1 4 7 10 13 16 5 17 29
#2 2 5 8 11 14 17 7 19 31
#3 3 6 9 12 15 18 9 21 33
或者另一种选择是使用pivot_longer
将其转换为'long'格式,然后进行计算
library(tidyr)
df %>%
mutate(rn = row_number()) %>%
pivot_longer(cols = -rn, names_to = c(".value", "group"),
names_sep ="(?<=[a-z])(?=[0-9])") %>%
group_by(rn) %>%
summarise_at(vars(col_pre), list(`3` = sum)) %>%
select(-rn) %>%
bind_cols(df, .)
或者如果我们使用dplyr
的devel
版本(‘0.8.99.9000’
),那么across
可以和summarise
[=24=一起使用]
df %>%
mutate(rn = row_number()) %>%
pivot_longer(cols = -rn, names_to = c(".value", "group"),
names_sep ="(?<=[a-z])(?=[0-9])") %>%
group_by(rn) %>%
summarise(across(col_pre, sum)) %>%
select(-rn) %>%
rename_all(~ str_c(., 3)) %>%
bind_cols(df, .)
# A tibble: 3 x 9
# a1 a2 b1 b2 c1 c2 a3 b3 c3
# <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1 1 4 7 10 13 16 5 17 29
#2 2 5 8 11 14 17 7 19 31
#3 3 6 9 12 15 18 9 21 33
我们可以使用 map_dfc
和 transmute
library(dplyr)
library(purrr)
bind_cols(df, map_dfc(col_pre, ~df %>%
transmute(!!paste0(.x, 3) := !!sym(paste0(.x, 1)) + !!sym(paste0(.x, 2)))))
# A tibble: 3 x 9
# a1 a2 b1 b2 c1 c2 a3 b3 c3
# <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1 1 4 7 10 13 16 5 17 29
#2 2 5 8 11 14 17 7 19 31
#3 3 6 9 12 15 18 9 21 33
在基础 R 中,我们可以使用 split.default
df[paste0(col_pre, 3)] <- lapply(split.default(df,
sub('\d', '', names(df))), rowSums)
或者不拆分,我们可以按照@thelatemail 的建议,根据列的起始名称对数据进行子集化
df[paste0(col_pre,3)] <- lapply(col_pre, function(x)
rowSums(df[startsWith(names(df), x)]))
这是数据:
library(tidyverse)
col_pre <- c('a', 'b', 'c')
df <- tibble(a1 = 1:3, a2 = 4:6, b1 = 7:9, b2 = 10:12, c1 = 13:15, c2 = 16:18)
我想使用 purrr::map()
和 dplyr::mutate()
创建三个新列,它们是 df
中列的总和。
我可以使用 map()
迭代 a、b、c 列前缀的向量。我想出了 tidyeval
操作,以便
下面的代码运行没有错误。
out <- col_pre %>%
map_df(~ df %>%
mutate(!!as.name(paste0(.x, '3')) := !!as.name(paste0(.x, '1')) + !!as.name(paste0(.x, '2')))
)
但是,out
现在有六个虚假行:
a1 a2 b1 b2 c1 c2 a3 b3 c3
1 1 4 7 10 13 16 5 NA NA
2 2 5 8 11 14 17 7 NA NA
3 3 6 9 12 15 18 9 NA NA
4 1 4 7 10 13 16 NA 17 NA
5 2 5 8 11 14 17 NA 19 NA
6 3 6 9 12 15 18 NA 21 NA
7 1 4 7 10 13 16 NA NA 29
8 2 5 8 11 14 17 NA NA 31
9 3 6 9 12 15 18 NA NA 33
它所做的是不必要地复制输入的三行 df
。
这是我想要的输出:
a1 a2 b1 b2 c1 c2 a3 b3 c3
1 1 4 7 10 13 16 5 17 29
2 2 5 8 11 14 17 7 19 31
3 3 6 9 12 15 18 9 21 33
我觉得 purrr::reduce()
可能是解决方案,但我不确定如何应用它。
感谢任何帮助!
我们可以在进行评估之前将字符串转换为 sym
bol,而不是 mutate
使用 transmute
然后将列与原始数据集绑定
library(stringr)
library(purrr)
library(dplyr)
col_pre %>%
map_dfc(~ df %>%
transmute(!! str_c(.x, '3') := !! rlang::sym(str_c(.x, '1')) +
!! rlang::sym(str_c(.x, 2)))) %>%
bind_cols(df, .)
# A tibble: 3 x 9
# a1 a2 b1 b2 c1 c2 a3 b3 c3
# <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1 1 4 7 10 13 16 5 17 29
#2 2 5 8 11 14 17 7 19 31
#3 3 6 9 12 15 18 9 21 33
或者另一种选择是 parse_exprs
df %>%
mutate(!!! rlang::parse_exprs(str_c(sprintf("%s1 + %s2",
col_pre, col_pre), collapse=";"))) %>%
rename_at(vars(contains("+")), ~ str_c(col_pre, 3))
# A tibble: 3 x 9
# a1 a2 b1 b2 c1 c2 a3 b3 c3
# <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1 1 4 7 10 13 16 5 17 29
#2 2 5 8 11 14 17 7 19 31
#3 3 6 9 12 15 18 9 21 33
或者另一种选择是使用pivot_longer
将其转换为'long'格式,然后进行计算
library(tidyr)
df %>%
mutate(rn = row_number()) %>%
pivot_longer(cols = -rn, names_to = c(".value", "group"),
names_sep ="(?<=[a-z])(?=[0-9])") %>%
group_by(rn) %>%
summarise_at(vars(col_pre), list(`3` = sum)) %>%
select(-rn) %>%
bind_cols(df, .)
或者如果我们使用dplyr
的devel
版本(‘0.8.99.9000’
),那么across
可以和summarise
[=24=一起使用]
df %>%
mutate(rn = row_number()) %>%
pivot_longer(cols = -rn, names_to = c(".value", "group"),
names_sep ="(?<=[a-z])(?=[0-9])") %>%
group_by(rn) %>%
summarise(across(col_pre, sum)) %>%
select(-rn) %>%
rename_all(~ str_c(., 3)) %>%
bind_cols(df, .)
# A tibble: 3 x 9
# a1 a2 b1 b2 c1 c2 a3 b3 c3
# <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1 1 4 7 10 13 16 5 17 29
#2 2 5 8 11 14 17 7 19 31
#3 3 6 9 12 15 18 9 21 33
我们可以使用 map_dfc
和 transmute
library(dplyr)
library(purrr)
bind_cols(df, map_dfc(col_pre, ~df %>%
transmute(!!paste0(.x, 3) := !!sym(paste0(.x, 1)) + !!sym(paste0(.x, 2)))))
# A tibble: 3 x 9
# a1 a2 b1 b2 c1 c2 a3 b3 c3
# <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1 1 4 7 10 13 16 5 17 29
#2 2 5 8 11 14 17 7 19 31
#3 3 6 9 12 15 18 9 21 33
在基础 R 中,我们可以使用 split.default
df[paste0(col_pre, 3)] <- lapply(split.default(df,
sub('\d', '', names(df))), rowSums)
或者不拆分,我们可以按照@thelatemail 的建议,根据列的起始名称对数据进行子集化
df[paste0(col_pre,3)] <- lapply(col_pre, function(x)
rowSums(df[startsWith(names(df), x)]))