Sé que hay varias preguntas similares por aquí, pero ninguna de ellas parece abordar el problema preciso que estoy teniendo.
set.seed(4)
df = data.frame(
Key = c("A", "B", "A", "D", "A"),
Val1 = rnorm(5),
Val2 = runif(5),
Val3 = 1:5
)
Quiero poner a cero los valores de las columnas de valor para las filas donde Clave == "A" Los nombres de columna se referencian a través de un grep
:
cols = grep("Val", names(df), value = TRUE)
Normalmente para lograr lo que quiero en este caso, usaría data.table
así:
library(data.table)
df = as.data.table(df)
df[Key == "A", (cols) := 0]
Y la salida deseada es así:
Key Val1 Val2 Val3
1 A 0.000000 0.00000000 0
2 B -1.383814 0.55925762 2
3 A 0.000000 0.00000000 0
4 D 1.437151 0.05632773 4
5 A 0.000000 0.00000000 0
Sin embargo, esta vez necesito usarlo, dplyr
ya que estoy trabajando en un proyecto de equipo donde todos lo usan. Los datos que acabo de proporcionar son ilustrativos y mis datos reales son> 5m de filas con 16 columnas de valor para actualizar. La única solución que se me ocurre es usar mutate_at
así:
df %>% mutate_at(.vars = vars(cols), .funs = function(x) ifelse(df$Key == "A", 0, x))
Sin embargo, esto parece ser extremadamente lento en mis datos reales. Esperaba encontrar una solución que fuera más elegante y, lo que es más importante, más rápida.
He intentado muchas combinaciones usando map
, sin comillas usando !!
, usando get
y :=
(que molestamente puede enmascararse en el :=
archivo data.table), etc., pero creo que mi comprensión de cómo funcionan estos simplemente no es lo suficientemente profunda como para construir una solución válida.
fuente
Respuestas:
Con este comando dplyr,
En realidad está evaluando la declaración df $ Key == "A", n veces, donde n = el número de columnas que tiene.
Una solución es predefinir las filas que desea cambiar:
Una forma más limpia y mejor, señalada correctamente por @IceCreamToucan (ver comentarios a continuación), es usar la función reemplazar, mientras le pasa los parámetros adicionales:
Podemos poner a prueba todos estos enfoques, y creo que dplyr y data.table son comparables.
fuente
df %>% mutate_at(vars(contains('Val')), replace, df$Key == 'A', 0)
replace
método es un poco más lento que elidx
método original .dplyr::if_else()
es más rápido que la baseifelse()
.