Как использовать map from purrr с dplyr:: mutate для создания нескольких новых столбцов на основе пар столбцов
у меня есть следующая проблема с использованием R. короче говоря, я хочу создать несколько новых столбцов в фрейме данных на основе вычислений различных пар столбцов в фрейме данных.
выглядит следующим образом:
df <- data.frame(a1 = c(1:5),
b1 = c(4:8),
c1 = c(10:14),
a2 = c(9:13),
b2 = c(3:7),
c2 = c(15:19))
df
a1 b1 c1 a2 b2 c2
1 4 10 9 3 15
2 5 11 10 4 16
3 6 12 11 5 17
4 7 13 12 6 18
5 8 14 13 7 19
вывод должен выглядеть следующим образом:
a1 b1 c1 a2 b2 c2 sum_a sum_b sum_c
1 4 10 9 3 15 10 7 25
2 5 11 10 4 16 12 9 27
4 7 13 12 6 18 16 13 31
5 8 14 13 7 19 18 15 33
Я могу добиться этого, используя dplyr, выполняя некоторую ручную работу следующим образом:
df %>% rowwise %>% mutate(sum_a = sum(a1, a2),
sum_b = sum(b1, b2),
sum_c = sum(c1, c2)) %>%
as.data.frame()
Итак, что делается: возьмите столбцы с буквой "a" в нем, вычислить rowwise сумму, и создать новый столбец с суммой по имени sum_[письмо]. Повторите для столбцов с разными буквами.
это работает, однако, если у меня есть большой набор данных С 300 различными парами столбцов, ручной ввод будет значительным, так как мне придется писать 300 мутирующих вызовов.
недавно я наткнулся на пакет R "муррр" , и я предполагаю, что это решит мою проблему делать то, что я хочу, более автоматизированным способом.
In в частности, я бы подумал, что могу использовать purrr: map2, к которому я передаю два списка имен столбцов.
- list1 = все столбцы с номером 1 в нем
- list2 = все столбцы с номером 2 в нем
тогда я мог бы вычислить сумму каждой соответствующей записи списка в виде:
map2(list1, list2, ~mutate(sum))
однако я не могу понять, как лучше всего подойти к этой проблеме, используя purrr. Я довольно новичок в использовании purrr, поэтому я бы действительно ценю любую помощь по этому вопросу.
7 ответов
вот один из вариантов с purrr
. Мы получаем unique
код names
набора данных ('nm1'), используйте map
(от purrr
) для перебора уникальных имен,select
столбец matches
значение префикса "nm1", добавьте строки с помощью reduce
и привязка столбцов (bind_cols
) с исходным набором данных
library(tidyverse)
nm1 <- names(df) %>%
substr(1, 1) %>%
unique
nm1 %>%
map(~ df %>%
select(matches(.x)) %>%
reduce(`+`)) %>%
set_names(paste0("sum_", nm1)) %>%
bind_cols(df, .)
# a1 b1 c1 a2 b2 c2 sum_a sum_b sum_c
#1 1 4 10 9 3 15 10 7 25
#2 2 5 11 10 4 16 12 9 27
#3 3 6 12 11 5 17 14 11 29
#4 4 7 13 12 6 18 16 13 31
#5 5 8 14 13 7 19 18 15 33
Если вам нравится рассматривать базовый подход R, вот как вы могли бы это сделать:
cbind(df, lapply(split.default(df, substr(names(df), 0,1)), rowSums))
# a1 b1 c1 a2 b2 c2 a b c
#1 1 4 10 9 3 15 10 7 25
#2 2 5 11 10 4 16 12 9 27
#3 3 6 12 11 5 17 14 11 29
#4 4 7 13 12 6 18 16 13 31
#5 5 8 14 13 7 19 18 15 33
он разбивает столбец данных на список, основанный на первой букве каждого имени столбца (a, b или c).
Если у вас есть большое количество столбцов и вам нужно различать все символы, кроме чисел в конце каждого имени столбца, вы можете изменить подход к:
cbind(df, lapply(split.default(df, sub("\d+$", "", names(df))), rowSums))
в базе R все векторизовано:
nms <- names(df)
df[paste0("sum_",unique(gsub("[1-9]","",nms)))] <-
df[endsWith(nms,"1")] + df[endsWith(nms,"2")]
# a1 b1 c1 a2 b2 c2 sum_a sum_b sum_c
# 1 1 4 10 9 3 15 10 7 25
# 2 2 5 11 10 4 16 12 9 27
# 3 3 6 12 11 5 17 14 11 29
# 4 4 7 13 12 6 18 16 13 31
# 5 5 8 14 13 7 19 18 15 33
df %>%
mutate(sum_a = pmap_dbl(select(., starts_with("a")), sum),
sum_b = pmap_dbl(select(., starts_with("b")), sum),
sum_c = pmap_dbl(select(., starts_with("c")), sum))
a1 b1 c1 a2 b2 c2 sum_a sum_b sum_c
1 1 4 10 9 3 15 10 7 25
2 2 5 11 10 4 16 12 9 27
3 3 6 12 11 5 17 14 11 29
4 4 7 13 12 6 18 16 13 31
5 5 8 14 13 7 19 18 15 33
для хакерского аккуратного решения проверьте это:
library(tidyr)
library(dplyr)
df %>%
rownames_to_column(var = 'row') %>%
gather(a1:c2, key = 'key', value = 'value') %>%
extract(key, into = c('col.base', 'col.index'), regex = '([a-zA-Z]+)([0-9]+)') %>%
group_by(row, col.base) %>%
summarize(.sum = sum(value)) %>%
spread(col.base, .sum) %>%
bind_cols(df, .) %>%
select(-row)
в принципе, я собираю все пары столбцов с их значениями во всех строках, разделяю имя столбца на две части, вычисляю суммы строк для столбцов с одной и той же буквой и возвращаю ее в широкую форму.
еще одно решение, которое раскалывает df
по номерам, чем использовать Reduce
вычислить sum
library(tidyverse)
df %>%
split.default(., substr(names(.), 2, 3)) %>%
Reduce('+', .) %>%
set_names(paste0("sum_", substr(names(.), 1, 1))) %>%
cbind(df, .)
#> a1 b1 c1 a2 b2 c2 sum_a sum_b sum_c
#> 1 1 4 10 9 3 15 10 7 25
#> 2 2 5 11 10 4 16 12 9 27
#> 3 3 6 12 11 5 17 14 11 29
#> 4 4 7 13 12 6 18 16 13 31
#> 5 5 8 14 13 7 19 18 15 33
создано 2018-04-13 reprex пакет (версии v0.2.0).
1) dplyr/tidyr преобразовать в длинную форму, суммировать и преобразовать обратно в широкую форму:
library(dplyr)
library(tidyr)
DF %>%
mutate(Row = 1:n()) %>%
gather(colname, value, -Row) %>%
group_by(g = gsub("\d", "", colname), Row) %>%
summarize(sum = sum(value)) %>%
ungroup %>%
mutate(g = paste("sum", g, sep = "_")) %>%
spread(g, sum) %>%
arrange(Row) %>%
cbind(DF, .) %>%
select(-Row)
даем:
a1 b1 c1 a2 b2 c2 sum_a sum_b sum_c
1 1 4 10 9 3 15 10 7 25
2 2 5 11 10 4 16 12 9 27
3 4 7 13 12 6 18 16 13 31
4 5 8 14 13 7 19 18 15 33
2) База с использованием матричного умножения
nms
представляет собой вектор имен столбцов без цифр и предваряется sum_
. u
является вектором уникальных его элементов. Сформируйте логическую матрицу, используя outer
от того, что при умножении на DF
дает суммы -- журналы получают преобразованы в 0-1 когда это будет сделано. Наконец, привяжите его к входу.
nms <- gsub("(\D+)\d", "sum_\1", names(DF))
u <- unique(nms)
sums <- as.matrix(DF) %*% outer(nms, setNames(u, u), "==")
cbind(DF, sums)
даем:
a1 b1 c1 a2 b2 c2 sum_a sum_b sum_c
1 1 4 10 9 3 15 10 7 25
2 2 5 11 10 4 16 12 9 27
3 4 7 13 12 6 18 16 13 31
4 5 8 14 13 7 19 18 15 33
3) база с tapply
используя nms
из (2) применить tapply к каждой строке:
cbind(DF, t(apply(DF, 1, tapply, nms, sum)))
даем:
a1 b1 c1 a2 b2 c2 sum_a sum_b sum_c
1 1 4 10 9 3 15 10 7 25
2 2 5 11 10 4 16 12 9 27
3 4 7 13 12 6 18 16 13 31
4 5 8 14 13 7 19 18 15 33
вы можете заменить nms на factor(nms, levels = unique(nms))
в приведенном выше выражении, если имена не в порядке возрастания.