Этот вопрос относится к предыдущему, который я задал , но пытаюсь быть более обобщенным c. Я хочу использовать формулы для выполнения операций над несколькими «группами» данных (например, a_data1
, a_data2
, b_data1
, b_data2
, а затем выполнять операции с использованием столбцов *_data1
).
Основываясь на ответе @ akrun на этот вопрос, я создал следующую функцию. Он принимает одностороннюю формулу и применяет ее ко всем «группам данных»:
suppressPackageStartupMessages({
library(dplyr)
library(tidyr)
})
polymutate <- function(df, formula,
pattern = "(.)_(.*)",
staticCols = NULL) {
staticCols <- rlang::enquo(staticCols)
rhs <- rlang::f_rhs(formula)
names <- all.vars(rhs)
df %>%
mutate(
rn = row_number()
) %>%
pivot_longer(
cols = -c(rn, !!staticCols),
names_to = c(".value", "grp"),
names_pattern = pattern
) %>%
mutate(
new = eval(rhs)
) %>%
pivot_wider(
names_from = grp,
values_from = c(names, "new")
) %>%
select(
-rn
) %>%
rename_at(
vars(starts_with("new")),
gsub, pattern = "^new_", replacement = ""
)
}
df <- data.frame(a_data1 = 1:3, b_data1 = 2:4,
a_data2 = 3:5, b_data2 = 4:6,
static = 5:7)
polymutate(df, ~ a + b, staticCols = static)
#> # A tibble: 3 x 7
#> static a_data1 a_data2 b_data1 b_data2 data1 data2
#> <int> <int> <int> <int> <int> <int> <int>
#> 1 5 1 3 2 4 3 7
#> 2 6 2 4 3 5 5 9
#> 3 7 3 5 4 6 7 11
Создано в 2020-03-13 пакетом Представление (v0 .3.0)
Итак, этот polymutate
преобразует фрейм данных в более длинный формат, чтобы у нас был один столбец с именем группы (data1
или data2
) и по одному на префикс (a
и b
). Затем он оценивает данную формулу в контексте этого более глубокого фрейма данных (очевидно, что имена в формуле должны соответствовать префиксам). Как только это сделано, он расширяет кадр данных до его первоначальной формы.
Это работает довольно хорошо, но немного медленно. Использование его на фрейме данных с 20 000 строк и 11 «группами» занимает 0,77 секунды.
Я подумал, что это связано с необходимостью дважды реструктурировать такой большой фрейм данных: углубить, а затем расширить его.
Так что я подумал, смогу ли я сделать это без этих хлопот. Я нашел пакет wrapr
, который позволяет нам создавать псевдонимы для имен. Поэтому я должен быть в состоянии выполнить что-то похожее на вышесказанное, передав формулу и имена столбцов, которые я хочу изменить.
Затем он может извлечь переменные, используемые в формуле, и использовать их для перестройки желаемого Имена столбцов, создать сопоставление псевдонимов, а затем использовать это сопоставление для применения формулы к кадру данных. Я подошел довольно близко, но не смог получить реальную формулу для оценки:
suppressPackageStartupMessages({
library(dplyr)
})
polymutate2 <- function(df, formula, name) {
vars <- all.vars(formula)
rhs <- rlang::f_rhs(formula)
aliases <- paste0(vars, "_", name)
mapping <- rlang::list2(!!!aliases)
names(mapping) <- vars
mapping <- do.call(wrapr::qc, mapping)
wrapr::let(
mapping,
df %>% mutate(!!name := a + b)
)
}
df <- data.frame(a_data1 = 1:3, b_data1 = 2:4,
a_data2 = 3:5, b_data2 = 4:6,
static = 5:7)
polymutate2(df, ~ a + b, "data1")
#> a_data1 b_data1 a_data2 b_data2 static data1
#> 1 1 2 3 4 5 3
#> 2 2 3 4 5 6 5
#> 3 3 4 5 6 7 7
Создано в 2020-03-13 пакетом Представить (v0.3.0 )
Вы заметите, что вызов mutate
имеет жестко выраженное выражение, поскольку я не смог заставить его работать с данной формулой. Замена этого выражения на eval(rhs)
, как в предыдущей версии, приводит к ошибке object 'a' not found
:
suppressPackageStartupMessages({
library(dplyr)
# library(tidyr)
})
polymutate2 <- function(df, formula, name) {
vars <- all.vars(formula)
rhs <- rlang::f_rhs(formula)
aliases <- paste0(vars, "_", name)
mapping <- rlang::list2(!!!aliases)
names(mapping) <- vars
mapping <- do.call(wrapr::qc, mapping)
wrapr::let(
mapping,
df %>% mutate(!!name := eval(rhs))
)
}
polymutate2(df, ~ a + b, "data1")
#> Error in eval(rhs): object 'a' not found
Если я смогу заставить это работать (и при условии, что решение не оказывает существенного влияния на производительность), это очень быстрее: всего за 0,03 секунды запускается цепочка из polymutate2
(по одной для каждой из 11 групп в моем кадре данных из 20 000 строк).
Итак, как мне заставить polymutate2
работать с любая формула? Я открыт для любых предложений, нет необходимости использовать wrapr
, если существует какое-то другое решение. (Я также обеспокоен тем, что это решение может не работать, если формула сложная, вызывает функции или еще что-то, просто еще не удалось проверить).