mutate - dplyr en r
Pasar argumentos a las funciones dplyr (6)
Al igual que las versiones anteriores de dplyr hasta 0.5, el nuevo dplyr tiene instalaciones para evaluación estándar (SE) y evaluación no estándar (NSE). Pero se expresan de manera diferente que antes.
Si desea una función NSE, pase las expresiones simples y use enquo para capturarlas como quosures . Si desea una función SE, omite enquo
y simplemente pasa quosures (o símbolos) directamente. Aquí está la solución SE a la pregunta:
library(tidyverse)
library(rlang)
f1 <- function(df, grp.var, uniq.var) {
df %>%
group_by(!!grp.var) %>%
summarise(n_uniq = n_distinct(!!uniq.var)) %>%
filter(n_uniq > 1)
}
a <- f1(iris, quo(Sepal.Length), quo(Sepal.Width))
b <- f1(iris, sym("Sepal.Length"), sym("Sepal.Width"))
identical(a, b)
#> [1] TRUE
Tenga en cuenta que la versión SE le permite trabajar con argumentos de cadena; simplemente conviértalos en símbolos primero usando sym()
. Para obtener más información, consulte la programación con dplyr vignette.
Quiero parametrizar el siguiente cálculo utilizando dplyr
que encuentra qué valores de Sepal.Length
están asociados con más de un valor de Sepal.Width
:
library(dplyr)
iris %>%
group_by(Sepal.Length) %>%
summarise(n.uniq=n_distinct(Sepal.Width)) %>%
filter(n.uniq > 1)
Normalmente escribiría algo como esto:
not.uniq.per.group <- function(data, group.var, uniq.var) {
iris %>%
group_by(group.var) %>%
summarise(n.uniq=n_distinct(uniq.var)) %>%
filter(n.uniq > 1)
}
Sin embargo, este enfoque arroja errores porque dplyr
usa una evaluación no estándar . ¿Cómo debe escribirse esta función?
En la versión actual de dplyr
(0.7.4) el uso de las versiones de la función de evaluación estándar (anexado ''_'' al nombre de la función, por ejemplo, group_by_
) está en desuso. En su lugar, debe confiar en tidyeval cuando escriba funciones.
Aquí hay un ejemplo de cómo se vería tu función en ese momento:
# definition of your function
not.uniq.per.group <- function(data, group.var, uniq.var) {
# enquotes variables to be used with dplyr-functions
group.var <- enquo(group.var)
uniq.var <- enquo(uniq.var)
# use ''!!'' before parameter names in dplyr-functions
data %>%
group_by(!!group.var) %>%
summarise(n.uniq=n_distinct(!!uniq.var)) %>%
filter(n.uniq > 1)
}
# call of your function
not.uniq.per.group(iris, Sepal.Length, Sepal.Width)
Si desea conocer todos los detalles, existe una viñeta excelente del equipo de dplyr sobre cómo funciona esto.
En la versión de desarrollo de dplyr
(que pronto se lanzará 0.6.0
), también podemos usar una sintaxis ligeramente diferente para pasar las variables.
f1 <- function(df, grp.var, uniq.var) {
grp.var <- enquo(grp.var)
uniq.var <- enquo(uniq.var)
df %>%
group_by(!!grp.var) %>%
summarise(n_uniq = n_distinct(!!uniq.var)) %>%
filter(n_uniq >1)
}
res2 <- f1(iris, Sepal.Length, Sepal.Width)
res1 <- not.uniq.per.group(iris, "Sepal.Length", "Sepal.Width")
identical(res1, res2)
#[1] TRUE
Aquí enquo
toma los argumentos y devuelve el valor como quosure
(similar al sustituto en la base R) al evaluar los argumentos de la función de forma perezosa y dentro del resumen, le pedimos que descuente (!! o UQ) para que se evalúe.
He escrito una función en el pasado que hace algo similar a lo que está haciendo, excepto que explora todas las columnas fuera de la clave principal y busca múltiples valores únicos por grupo.
find_dups = function(.table, ...) {
require(dplyr)
require(tidyr)
# get column names of primary key
pk <- .table %>% select(...) %>% names
other <- names(.table)[!(names(.table) %in% pk)]
# group by primary key,
# get number of rows per unique combo,
# filter for duplicates,
# get number of distinct values in each column,
# gather to get df of 1 row per primary key, other column,
# filter for where a columns have more than 1 unique value,
# order table by primary key
.table %>%
group_by(...) %>%
mutate(cnt = n()) %>%
filter(cnt > 1) %>%
select(-cnt) %>%
summarise_each(funs(n_distinct)) %>%
gather_(''column'', ''unique_vals'', other) %>%
filter(unique_vals > 1) %>%
arrange(...) %>%
return
# Final dataframe:
## One row per primary key and column that creates duplicates.
## Last column indicates how many unique values of
## the given column exist for each primary key.
}
Esta función también funciona con el operador de tuberías:
dat %>% find_dups(key1, key2)
Puede evitar lazyeval
usando do
para llamar a una función anónima y luego usar get
. Esta solución se puede usar más generalmente para emplear agregaciones múltiples. Normalmente escribo la función por separado.
library(dplyr)
not.uniq.per.group <- function(df, grp.var, uniq.var) {
df %>%
group_by_(grp.var) %>%
do((function(., uniq.var) {
with(., data.frame(n_uniq = n_distinct(get(uniq.var))))
}
)(., uniq.var)) %>%
filter(n_uniq > 1)
}
not.uniq.per.group(iris, "Sepal.Length", "Sepal.Width")
dplyr
usar las versiones de evaluación estándar de las funciones dplyr
(simplemente dplyr
''_'' a los nombres de las funciones, es decir, group_by_
& summarise_
) y pase cadenas a su función, que luego deberá convertir en símbolos. Para parametrizar el argumento de summarise_, necesitará usar interp()
, que se define en el paquete lazyeval
. Concretamente:
library(dplyr)
library(lazyeval)
not.uniq.per.group <- function(df, grp.var, uniq.var) {
df %>%
group_by_(grp.var) %>%
summarise_( n_uniq=interp(~n_distinct(v), v=as.name(uniq.var)) ) %>%
filter(n_uniq > 1)
}
not.uniq.per.group(iris, "Sepal.Length", "Sepal.Width")
Vea la vignette dplyr
para una evaluación no estándar para más detalles.