Minha pergunta envolve somar valores em várias colunas de um quadro de dados e criar uma nova coluna correspondente a esse somatório usando dplyr
. As entradas de dados nas colunas são binárias (0,1). Estou pensando em um análogo de linha da função summarise_each
ou mutate_each
de dplyr
. Abaixo está um exemplo mínimo do quadro de dados:
library(dplyr)
df=data.frame(
x1=c(1,0,0,NA,0,1,1,NA,0,1),
x2=c(1,1,NA,1,1,0,NA,NA,0,1),
x3=c(0,1,0,1,1,0,NA,NA,0,1),
x4=c(1,0,NA,1,0,0,NA,0,0,1),
x5=c(1,1,NA,1,1,1,NA,1,0,1))
> df
x1 x2 x3 x4 x5
1 1 1 0 1 1
2 0 1 1 0 1
3 0 NA 0 NA NA
4 NA 1 1 1 1
5 0 1 1 0 1
6 1 0 0 0 1
7 1 NA NA NA NA
8 NA NA NA 0 1
9 0 0 0 0 0
10 1 1 1 1 1
Eu poderia usar algo como:
df <- df %>% mutate(sumrow= x1 + x2 + x3 + x4 + x5)
mas isso envolveria escrever os nomes de cada uma das colunas. Eu tenho cerca de 50 colunas. Além disso, os nomes das colunas mudam em diferentes iterações do loop em que desejo implementar esta operação, portanto, gostaria de evitar ter de fornecer nomes de coluna.
Como posso fazer isso com mais eficiência? Qualquer ajuda seria muito apreciada.
dplyr
? Por que não apenas um simplesdf$sumrow <- rowSums(df, na.rm = TRUE)
da base R? Oudf$sumrow <- Reduce(`+`, df)
se você deseja replicar exatamente o que você fezdplyr
.dplyr
tambémdf %>% mutate(sumrow = Reduce(`+`, .))
oudf %>% mutate(sumrow = rowSums(.))
dplyr
versão mais recente e funcionará.Respostas:
E se
some cada coluna
df %>% replace(is.na(.), 0) %>% summarise_all(funs(sum))
some cada linha
df %>% replace(is.na(.), 0) %>% mutate(sum = rowSums(.[1:5]))
fonte
summarise_each
soma ao longo de cada coluna enquanto o que é necessário é a soma ao longo de cada linha(.[1:5])
parte, mas infelizmente não estou familiarizado com a sintaxe nem sei como procurar ajuda sobre ela. Tentei,mutate(sum = rowSums(is.numeric(.)))
mas não funcionou.df %>% replace(is.na(.), 0) %>% select_if(is.numeric) %>% summarise_each(funs(sum))
uma chance?summarise_all
vez desummarise_each
porque está obsoleto.mutate(sum = rowSums(.[,-1]))
pode ser útil se você não souber com quantas colunas precisa lidar.Se você quiser somar apenas algumas colunas, eu usaria algo assim:
library(dplyr) df=data.frame( x1=c(1,0,0,NA,0,1,1,NA,0,1), x2=c(1,1,NA,1,1,0,NA,NA,0,1), x3=c(0,1,0,1,1,0,NA,NA,0,1), x4=c(1,0,NA,1,0,0,NA,0,0,1), x5=c(1,1,NA,1,1,1,NA,1,0,1)) df %>% select(x3:x5) %>% rowSums(na.rm=TRUE) -> df$x3x5.total head(df)
Desta forma, você pode usar
dplyr::select
a sintaxe de.fonte
Eu usaria correspondência de expressão regular para somar variáveis com determinados nomes de padrão. Por exemplo:
df <- df %>% mutate(sum1 = rowSums(.[grep("x[3-5]", names(.))], na.rm = TRUE), sum_all = rowSums(.[grep("x", names(.))], na.rm = TRUE))
Desta forma, você pode criar mais de uma variável como uma soma de certo grupo de variáveis de seu quadro de dados.
fonte
-
sinal:rowSums(.[-grep("x[3-5]", names(.))], na.rm = TRUE)
Usar
reduce()
frompurrr
é ligeiramente mais rápido do querowSums
e definitivamente mais rápido do queapply
, uma vez que você evita a iteração em todas as linhas e apenas tira proveito das operações vetorizadas:library(purrr) library(dplyr) iris %>% mutate(Petal = reduce(select(., starts_with("Petal")), `+`))
Veja isto para horários
fonte
na.rm = TRUE
rowSums(select(., matches("myregex")) , na.rm = TRUE))
porque era disso que eu precisava para ignorar NAs. Então, se os números forem,sum(NA, 5)
o resultado é 5. Mas você disse que reduzir é melhor do querowSums
então eu queria saber se há uma maneira de usá-lo nessa situação?rowSums
versão é provavelmente a melhor. A principal desvantagem é que apenasrowSums
erowMeans
estão disponíveis (é um pouco mais lento do que reduzir, mas não muito). Se você precisar realizar outra operação (não a soma), areduce
versão é provavelmente a única opção. Apenas evite usarapply
neste caso.Eu encontro esse problema com frequência, e a maneira mais fácil de fazer isso é usar a
apply()
função em ummutate
comando.library(tidyverse) df=data.frame( x1=c(1,0,0,NA,0,1,1,NA,0,1), x2=c(1,1,NA,1,1,0,NA,NA,0,1), x3=c(0,1,0,1,1,0,NA,NA,0,1), x4=c(1,0,NA,1,0,0,NA,0,0,1), x5=c(1,1,NA,1,1,1,NA,1,0,1)) df %>% mutate(sum = select(., x1:x5) %>% apply(1, sum, na.rm=TRUE))
Aqui você pode usar o que quiser para selecionar as colunas usando os
dplyr
truques padrão (por exemplo,starts_with()
oucontains()
). Fazendo todo o trabalho em um únicomutate
comando, essa ação pode ocorrer em qualquer lugar dentro de umdplyr
fluxo de etapas de processamento. Finalmente, usando oapply()
função, você tem a flexibilidade de usar qualquer resumo de que precisar, incluindo sua própria função de resumo criada para o propósito.Alternativamente, se a ideia de usar uma função não-tidyverse não for atraente, você pode reunir as colunas, resumi-las e, finalmente, juntar o resultado de volta ao quadro de dados original.
df <- df %>% mutate( id = 1:n() ) # Need some ID column for this to work df <- df %>% group_by(id) %>% gather('Key', 'value', starts_with('x')) %>% summarise( Key.Sum = sum(value) ) %>% left_join( df, . )
Aqui eu usei a
starts_with()
função para selecionar as colunas e calculei a soma e você pode fazer o que quiser com osNA
valores. A desvantagem dessa abordagem é que, embora seja bastante flexível, ela realmente não se encaixa em umdplyr
fluxo de etapas de limpeza de dados.fonte
apply
quandorowSums
foi projetado para isso.rowSums
funciona muito bemrowMeans
, mas sempre me senti um pouco estranho pensando: "E se o que eu preciso calcular não for uma soma ou uma média?" No entanto, 99% das vezes eu tenho que fazer algo assim, é uma soma ou uma média, então talvez a parte extra de flexibilidade no uso daapply
função geral não seja garantida.Em versões mais novas do,
dplyr
você pode usarrowwise()
junto comc_across
para realizar agregação por linha para funções que não possuem variantes específicas por linha, mas se a variante por linha existir, ela deve ser mais rápida.Uma vez que
rowwise()
é apenas uma forma especial de agrupar e alterar a forma como os verbos funcionam, você provavelmente vai querer canalizá-lo paraungroup()
após fazer sua operação de linha.Para selecionar um intervalo de linhas:
df %>% dplyr::rowwise() %>% dplyr::mutate(sumrange = sum(dplyr::c_across(x1:x5), na.rm = T)) # %>% dplyr::ungroup() # you'll likely want to ungroup after using rowwise()
Para selecionar linhas por tipo:
df %>% dplyr::rowwise() %>% dplyr::mutate(sumnumeric = sum(c_across(where(is.numeric)), na.rm = T)) # %>% dplyr::ungroup() # you'll likely want to ungroup after using rowwise()
No seu caso específico, existe uma variante de linha para que você possa fazer o seguinte (observe o uso de
across
):df %>% dplyr::mutate(sumrow = rowSums(dplyr::across(x1:x5), na.rm = T))
Para obter mais informações, consulte a página sobre rowwise .
fonte