Recentemente, ao tentar fazer gráficos exploratórios de um grande conjunto de dados (~300 mil observações), fiz uma rápida busca na internet e achei alternativas interessantes aos histogramas de frequência e densidade e também aos famosos boxplots. Abaixo, apresento exemplos do que encontrei.
Nesse roteiro, todos os gráficos utilizarão como base o pacote ggplot2
, e algumas manipulações dos dados com o pacote dplyr
. Ah, e eu também uso o pacote cowplot
para tornar os plots do ggplot estéticamente melhores.
library(ggplot2)
library(dplyr)
library(cowplot)
library(lvplot)
Vamos usar o conjunto de dados ontime
do pacote lvplot
, que detalha o desempenho no horário dos vôos nacionais dos EUA em janeiro de 2015, veja uma amostra dos dados na Tabela 1.
FlightDate | UniqueCarrier | FlightNum | DepTime | ArrTime | TaxiOut | TaxiIn |
---|---|---|---|---|---|---|
2015-01-01 | AA | 1 | 0855 | 1237 | 17 | 7 |
2015-01-02 | AA | 1 | 0850 | 1211 | 15 | 9 |
2015-01-03 | AA | 1 | 0853 | 1151 | 15 | 13 |
2015-01-04 | AA | 1 | 0853 | 1218 | 14 | 19 |
2015-01-05 | AA | 1 | 0853 | 1222 | 27 | 24 |
2015-01-06 | AA | 1 | 0856 | 1300 | 85 | 4 |
2015-01-07 | AA | 1 | 0859 | 1221 | 29 | 12 |
2015-01-08 | AA | 1 | 0856 | 1158 | 26 | 3 |
2015-01-09 | AA | 1 | 0901 | 1241 | 43 | 4 |
2015-01-10 | AA | 1 | 0903 | 1235 | 37 | 10 |
Comecemos com os histogramas!
Histogramas
O primeiro gráfico para comparar a distribuição de variáveis contínuas é o histograma de frequência (Figura 1) ou densidade (Figura 2). Muitas vezes o que queremos fazer é comparar os histogramas em função de algum variável categórica, como no exemplo abaixo, onde criamos os histogramas da variável TaxiOut
1 em funcão das companhias aéreas (UniqueCarrier
).
hi <- ggplot(ontime, aes(x = TaxiOut, fill = UniqueCarrier)) +
facet_wrap(~UniqueCarrier, scales = "free") + #deixei as escalas livres
scale_x_log10() +
theme(legend.position = "none")
hi + geom_histogram()
hi + geom_density()
Como os histogramas separados em cada plot, às vezes fica difícil compará-los para entender possíveis diferenças entre os grupos analisados.
Alternativa 1: Ridgeline plots
Uma alternativa ao histograma de densidade são os ridgeline plots do pacote ggridges
que são gráficos parcialmente sobrepostos criando a impressão de uma cadeia de montanhas (Figura 3). São gráficos particularmente bons para visualizar as mudanças na distribuição no tempo e espaço, pois permite uma comparação visualmente mais fácil entre os grupos. Não deixe de olhar a vinheta do pacote para exemplos de customização.
Neste exemplo, primeiro eu ordeno as companhias aéreas por sua mediana2.
library(ggridges)
rplot <- ontime %>%
mutate(group = reorder(UniqueCarrier, TaxiOut, median)) %>%
ggplot(aes(x = TaxiOut, y = UniqueCarrier, fill=UniqueCarrier)) +
scale_x_log10(limits=c(5,50), breaks= c(5,10,25,50)) +
theme(legend.position = "none")
rplot + geom_density_ridges()
A altura de cada densidade pode ser ajustada para haver menos sobreposição:
rplot + geom_density_ridges(scale=1)
Também podemos usar as “montanhas” como frequências e não densidade, utilizando o argumento stat = "linline"
.
rplot + geom_density_ridges2(stat="binline")
Alternativa 2: violin plots
Outra alternativa, mais parecida com boxplots (tratados a seguir) é o violin plot (do próprio pacote do ggplot2
), que é um plot de densidade espelhado (lados simétricos) mas disposto como se fosse um boxplot (Figura 5).
vio <- ontime %>% mutate(group = reorder(UniqueCarrier, TaxiOut, median)) %>%
ggplot(aes(y = TaxiOut, x = UniqueCarrier, fill = UniqueCarrier)) +
scale_y_log10() +
theme(legend.position = "none")
vio + geom_violin()
Uma possibilidade é colocar boxplots (sem valores extremos para não poluir a figura) dentro do violin plot para facilitar encontrar a mediana e quartis:
vio + geom_violin() + geom_boxplot(width=0.4, outlier.alpha = 0)
Boxplots
Os boxplots deixam de ser úteis quando o volume de dados aumenta (Hofmann et al. 2017), pois o que eles consideram valores extremos (outliers) aumenta linearmente com o tamanho amostral (Figura 7). E é por isso que no violin plot nós removemos os outliers do gráfico. Ainda sim é um dos gráficos mais utilizados para se observar distribuição dos dados (veja aqui sobre 40 anos de história do boxplot).
ontime %>% mutate(group = reorder(UniqueCarrier, TaxiOut, median)) %>%
ggplot(aes(y = TaxiOut, x = UniqueCarrier, fill = UniqueCarrier)) +
scale_y_log10() +
theme(legend.position = "none") +
geom_boxplot()
Alternativa 3: letter-value plot
Uma recém criada alternativa aos boxplots convencionais, são os letter-value plots (veja Hofmann et al. 2017), que estão disponíveis no pacote lvplot
. Este plot é baseado na estimativa de quantis (outros que não o primeiro e terceiro como no boxplot). Entretanto, as estimativas somente são confiáveis se há bastante dados!
Abaixo alguns exemplos de uso dos lvplots, com algumas opções para a largura das caixas:
linear
: torna a largura de cada caixa inversamente proporcional ao quantil (letter-value) que ela representa, ou seja, começando com os quartis, cada caixa subsequente será um passo mais fina do que a anterior.area
: torna a área de cada caixa proporcional ao número de observações nela.height
: torna a largura de cada caixa proporcional ao número de pontos nela.
library(lvplot)
library(patchwork) # para fazer painel com vários plots
lv <- ontime %>% mutate(group = reorder(UniqueCarrier, TaxiOut, median)) %>%
ggplot(aes(y = TaxiOut, x = UniqueCarrier)) +
scale_y_log10() +
theme(legend.position = "none")
lv + geom_lv(aes(fill=..LV..), width.method = "height") +
scale_fill_lv() + ggtitle("Largura da caixa `height`")
lv + geom_lv(aes(fill=..LV..), width.method = "linear") +
scale_fill_lv() + ggtitle("Largura da caixa `linear`")
Eu particularmente acho o gráfico com largura height
o mais bonito3!
Compare a diferença no número de valores extremos encontrados no boxplot comum e no lvplot!
Referências
Hofmann, H., Wickham, H., Kafadar, K., 2017. Letter-Value Plots: Boxplots for Large Data. Journal of Computational and Graphical Statistics 26, 469–477. https://doi.org/10.1080/10618600.2017.1305277 link alternativo
O tempo de Taxi-out é definido como o tempo gasto por um vôo entre o tempo de desligamento real (AOBT) e o tempo de descolagem real (ATOT). Variável numérica do tempo de táxi em minutos.↩
como as medianas são bem parecidas, o efeito não fica tão legal quanto poderia…↩
Apesar de ainda achar as cores padrão não tão bonitas, mas com umas linhas a mais de código resolvemos esse problema.↩