dplyr:resumir desconhecido número de colunas?
Pergunta
Eu quero ser capaz de summarize
um agrupados quadro de dados, onde nem sempre eu sei de quais variáveis estarão presentes, mas eu sei como eu quero resumir cada variável, se ele estiver presente.
Vamos dizer que eu tenho uma dataframe como tal:
df <- data.frame(id = c(rep('a', 5), rep('b', 8), rep('c', 4)),
var1 = round(runif(17) * 10, 3),
var2 = sample(c(1:4), 17, replace = TRUE),
var4 = sample(1:1000, 17))
> df
id var1 var2 var4
1 a 5.930 4 360
2 a 7.265 2 713
3 a 3.704 3 117
4 a 5.149 2 782
5 a 3.777 2 640
6 b 4.183 2 802
7 b 0.107 2 638
8 b 5.323 4 327
9 b 4.322 2 631
10 b 0.937 3 921
11 b 5.558 2 570
12 b 5.902 4 363
13 b 0.671 3 432
14 c 0.475 1 845
15 c 1.562 3 620
16 c 4.464 2 997
17 c 1.714 2 714
Aviso var3 está faltando.Às vezes ele está lá, às vezes não é.É sempre o mesmo tipo quando ele está presente.Eu gostaria de ser capaz de ordenadamente manipular ambos os casos.
Digamos que, resumindo por id
, Eu quero que a média de var1
, a mediana de var2
, a mediana de var3
(quando presente) e o máximo de var4
.Se todas as variáveis estavam presentes eu poderia defini-lo assim:
library('dplyr')
set.seed(111)
result <- df %>% group_by(id) %>%
summarize(var1 = mean(var1),
var2 = median(var2),
var3 = median(var3),
var4 = max(var4))
No entanto, desde var3
não está lá, eu recebo um erro: Error in median(var3) : object 'var3' not found
.
Intuitivamente, eu iria tentar algo como:
result <- df %>% group_by(id) %>%
summarize(if('var1' %in% names(df)) var1 = mean(var1) else NULL,
if('var2' %in% names(df)) var2 = median(var2) else NULL,
if('var3' %in% names(df)) var3 = median(var3) else NULL,
if('var4' %in% names(df)) var4 = max(var4) else NULL)
Mas, obviamente, não funciona, ou talvez a minha intuição é um pouco fora.
Alguém tem alguma sugestão de como eu poderia fazer isso de forma limpa usando dplyr?Como você pode imaginar, df
na realidade é um grande quadro de dados com muitas colunas e var3
é um qualquer número de colunas que poderia estar faltando.
Solução
Esta não é exatamente a solução, mas talvez uma solução, se você não quiser criar todos os possíveis colunas iniciais, como sugerido por @joran.Ele primeiro irá criar todas as colunas que você especificar, mas alguns deles só serão NA
.Depois, você pode excluir as colunas utilizando aplicar.Note, porém, que o names(dd)
, quando usado dentro de dplyr
cadeia, só reconhece os nomes das colunas que estavam na entrada de dados.quadro, não aqueles criados na mesma operação.
dd <- dd %>%
group_by(id) %>%
summarize(var1 = ifelse("var1" %in% names(dd), mean(var1), NA),
var2 = ifelse("var2" %in% names(dd), max(var2), NA))
dd <- dd[,apply(dd, 2, function(x) ifelse(all(is.na(x)), FALSE, TRUE))]
Outra possível solução poderia estar usando o summarise_each
função, mas eu acho que depende se você deseja, por exemplo, a média, mediana e máximo de todas as colunas ou não.
Outras dicas
Eu acho que você pode fazer isso em várias etapas:
- converter da gama a muito tempo usando
melt
- resumir usando
dplyr
- converter de tempo para utilização larga
dcast
Por exemplo:
tmp <- melt(df, id.vars="id")
tmp <- tmp %>%
group_by(id, variable) %>%
summarise(mean = mean(value), median = median(value), max = max(value))
tmp <- melt(tmp, id.vars=c("id", "variable"), variable.name="stat")
tmp <- dcast(tmp, id ~ stat + variable)
Eu tive que adicionar mais um passo, porque você quer média, mediana e máximo para as diferentes variáveis.