Sei que há várias perguntas semelhantes por aqui, mas nenhuma delas parece abordar o problema exato que estou tendo.
set.seed(4)
df = data.frame(
Key = c("A", "B", "A", "D", "A"),
Val1 = rnorm(5),
Val2 = runif(5),
Val3 = 1:5
)
Desejo zerar os valores das colunas de valor das linhas em que Key == "A" Os nomes das colunas são referenciados por meio de um grep
:
cols = grep("Val", names(df), value = TRUE)
Normalmente, para alcançar o que eu quero, neste caso, eu usaria data.table
assim:
library(data.table)
df = as.data.table(df)
df[Key == "A", (cols) := 0]
E a saída desejada é assim:
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
No entanto, desta vez eu preciso usá-lo, dplyr
pois estou trabalhando em um projeto de equipe onde todos o usam. Os dados que acabei de fornecer são ilustrativos e meus dados reais são> 5m linhas com 16 colunas de valor a serem atualizadas. A única solução que eu poderia encontrar é usar mutate_at
assim:
df %>% mutate_at(.vars = vars(cols), .funs = function(x) ifelse(df$Key == "A", 0, x))
No entanto, isso parece ser extremamente lento nos meus dados reais. Eu esperava encontrar uma solução mais elegante e, mais importante, mais rápida.
Eu tentei muitas combinações usando map
, sem citar usando !!
, usando get
e :=
(que irritantemente podem ser mascarados pela :=
data.table) etc, mas acho que meu entendimento de como esses trabalhos simplesmente não são profundos o suficiente para construir uma solução válida.
fonte
Respostas:
Com este comando dplyr,
Na verdade, você está avaliando a instrução df $ Key == "A", n vezes, em que n = o número de colunas que você possui.
Uma solução alternativa é pré-definir as linhas que você deseja alterar:
Uma maneira mais limpa e melhor, corretamente apontada pelo @IceCreamToucan (veja os comentários abaixo), é usar a função replace, passando os parâmetros extras:
Podemos testar todas essas abordagens, e acho que dplyr e data.table são comparáveis.
fonte
df %>% mutate_at(vars(contains('Val')), replace, df$Key == 'A', 0)
replace
método é um pouco mais lento que oidx
método original .dplyr::if_else()
é mais rápido que a baseifelse()
.