r dplyr rlang tidyeval nse

Uso de evaluación no estándar basada en tidyeval en recodificación en el lado derecho de la mutación



dplyr rlang (3)

Aquí, en el lado derecho de := , podemos especificar sym para convertir en símbolo y luego evaluar ( !! )

fix_question <- function(df, question) { df %>% mutate(!!question := recode(!! rlang::sym(question), "A" = NA_character_)) } fix_question(sample_df, "q1") # A tibble: 3 x 2 # q1 q2 # <chr> <chr> #1 <NA> B #2 B B #3 C A

Un mejor enfoque que funcionaría tanto para las entradas citadas como no citadas es ensym

fix_question <- function(df, question) { question <- ensym(question) df %>% mutate(!!question := recode(!! question, "A" = NA_character_)) } fix_question(sample_df, q1) # A tibble: 3 x 2 # q1 q2 # <chr> <chr> #1 <NA> B #2 B B #3 C A fix_question(sample_df, "q1") # A tibble: 3 x 2 # q1 q2 # <chr> <chr> #1 <NA> B #2 B B #3 C A

Considere un tibble donde cada columna es un vector de caracteres que puede tomar muchos valores, digamos "A" a "F".

library(tidyverse) sample_df <- tibble(q1 = c("A", "B", "C"), q2 = c("B", "B", "A"))

Deseo crear una función que tome el nombre de una columna como argumento y recodifique esa columna para que cualquier respuesta "A" se convierta en un NA y el df se devuelva como está. La razón para diseñarlo de esta manera es encajar en una tubería más amplia que realiza una serie de operaciones utilizando una columna dada.

Hay muchas maneras de hacer esto. Pero estoy interesado en comprender cuál sería el mejor enfoque idiomático tidy_eval / tidyverse. Primero, el nombre de la pregunta debe estar en el lado izquierdo de un verbo mutado, ¡así que usamos el !! y := operadores apropiadamente. Pero entonces, ¿qué poner en el lado derecho?

fix_question <- function(df, question) { df %>% mutate(!!question := recode(... something goes here...)) } fix_question(sample_df, "q1") # should produce a tibble whose first column is (NA, "B", "C")

Mi pensamiento inicial fue que esto funcionaría:

df %>% mutate(!!question := recode(!!question, "A" = NA_character_))

Pero, por supuesto, el bang-bang dentro de la función solo devuelve la cadena de caracteres literal (por ejemplo, "q1"). Terminé tomando lo que parece una ruta hacky para hacer referencia a los datos en el lado derecho, usando la base R [[ operador y confiando en el . construir desde dplyr, y funciona, así que en cierto sentido he resuelto mi problema subyacente:

df %>% mutate(!!question := recode(.[[question]], "A" = NA_character_))

Estoy interesado en recibir comentarios de personas que son muy buenas en tidyeval en cuanto a si hay una forma más idiomática de hacer esto, con la esperanza de que ver un ejemplo trabajado mejoraría mi comprensión del conjunto de funciones tidyeval en general. ¿Alguna idea? Gracias de antemano por tu tiempo.


Puede hacer que la función sea un poco más flexible permitiendo que también se ingrese un vector de valores recodificados como argumento. Por ejemplo:

library(tidyverse) sample_df <- tibble(q1 = c("A", "B", "C"), q2 = c("B", "B", "A")) fix_question <- function(df, question, recode.vec) { df %>% mutate({{question}} := recode({{question}}, !!!recode.vec)) } fix_question(sample_df, q1, c(A=NA_character_, B="Was B"))

q1 q2 1 <NA> B 2 Was B B 3 C A

Tenga en cuenta que recode.vec está "sin comillas" con !!! . Puede ver lo que está haciendo con este ejemplo, adaptado de la viñeta Programación con dplyr (busque "empalme" para ver los ejemplos relevantes). Tenga en cuenta cómo !!! "empalma" los pares de valores de recodificación en la función de recode para que se utilicen como ... argumento en recode .

x = c("A", "B", "C") args = c(A=NA_character_, B="Was B") quo(recode(x, !!!args)) <quosure> expr: ^recode(x, A = <chr: NA>, B = "Was B") env: global

Si desea ejecutar potencialmente la función de recodificación en varias columnas, puede convertirla en una función que solo tome el nombre de una columna y un vector de recodificación. Parece que este enfoque sería más amigable con las tuberías.

fix_question <- function(question, recode.vec) { recode({{question}}, !!!recode.vec) } sample_df %>% mutate_at(vars(matches("q")), list(~fix_question(., c(A=NA_character_, B="Was B"))))

q1 q2 1 <NA> Was B 2 Was B Was B 3 C <NA>

O para recodificar una sola columna:

sample_df %>% mutate(q1 = fix_question(q1, c(A=NA_character_, B="Was B")))


Puede usar el método "rizado rizado" ahora si tiene argot> = 0.4.0 .

Explicación gracias a @ eipi10:

Esto combina el proceso de dos pasos entre comillas y luego entre comillas en un solo paso, por lo que {{question}} es equivalente a !!enquo(question)

fix_question <- function(df, question){ df %>% mutate({{question}} := recode({{question}}, A = NA_character_)) } fix_question(sample_df, q1) # # A tibble: 3 x 2 # q1 q2 # <chr> <chr> # 1 NA B # 2 B B # 3 C A

Tenga en cuenta que, a diferencia del enfoque ensym , esto no funciona con los nombres de los personajes. Peor aún, hace lo incorrecto en lugar de simplemente dar un error.

fix_question(sample_df, ''q1'') # # A tibble: 3 x 2 # q1 q2 # <chr> <chr> # 1 q1 B # 2 q1 B # 3 q1 A