Visualizando muitas variáveis ​​em um gráfico

25

Gostaria de mostrar como os valores de certas variáveis ​​(~ 15) mudam ao longo do tempo, mas também gostaria de mostrar como as variáveis ​​diferem umas das outras a cada ano. Então eu criei esse enredo:

insira a descrição da imagem aqui

Mas mesmo ao alterar o esquema de cores ou adicionar diferentes tipos de linha / forma, isso parece confuso. Existe uma maneira melhor de visualizar esse tipo de dados?

Dados de teste com código R:

structure(list(Var = structure(c(1L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 6L, 
6L, 6L, 6L, 6L, 6L, 7L, 7L, 7L, 7L, 7L, 7L, 7L, 8L, 8L, 8L, 8L, 
8L, 8L, 8L, 9L, 9L, 9L, 9L, 9L, 9L, 9L, 11L, 11L, 11L, 11L, 11L, 
11L, 11L, 12L, 12L, 12L, 12L, 12L, 12L, 13L, 14L, 14L, 14L, 14L, 
14L, 14L, 14L, 16L, 16L, 16L, 16L, 16L, 16L, 17L, 17L, 17L, 17L, 
17L, 17L, 17L, 18L, 18L, 18L, 18L, 18L, 18L, 18L), .Label = c("A", 
"B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", 
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"), class = "factor"), 
    Year = c(2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 2011L, 
    2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 2011L, 2015L, 1991L, 
    1993L, 1996L, 2000L, 2004L, 2011L, 2015L, 1993L, 1996L, 2000L, 
    2004L, 2011L, 2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 2011L, 
    2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 2011L, 2015L, 1991L, 
    1993L, 1996L, 2000L, 2004L, 2011L, 2015L, 1991L, 1993L, 1996L, 
    2000L, 2004L, 2011L, 2015L, 1993L, 1996L, 2000L, 2004L, 2011L, 
    2015L, 2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 2011L, 2015L, 
    1991L, 1993L, 1996L, 2000L, 2011L, 2015L, 1991L, 1993L, 1996L, 
    2000L, 2004L, 2011L, 2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 
    2011L, 2015L), Val = c(25.6, 22.93, 20.82, 24.1, 24.5, 29, 
    25.55, 24.5, 24.52, 20.73, 25.8, 25.5, 29.5, 27.7, 25.1, 
    25, 24.55, 26.75, 25, 30.5, 27.25, 25.1, 22.4, 27.07, 26, 
    29, 27.2, 24.2, 23, 24.27, 27.68, 27, 30.5, 28.1, 24.9, 23.75, 
    22.75, 27.25, 25, 29, 28.45, 24, 20.25, 17.07, 24.45, 25, 
    28.5, 26.75, 24.9, 21.25, 20.65, 25.1, 24.5, 26.5, 25.35, 
    23.5, 21.93, 26.5, 24.5, 29, 29.1, 26.4, 28.1, 23.75, 26.5, 
    28.05, 27, 30.5, 25.65, 23.3, 23.25, 24.57, 26.07, 27.5, 
    28.85, 27.7, 22, 23.43, 26.88, 27, 30.5, 29.25, 28.1, 23, 
    23.8, 28.32, 27, 29.5, 29.15, 27.6)), row.names = c(1L, 4L, 
5L, 6L, 7L, 8L, 9L, 10L, 13L, 14L, 15L, 16L, 17L, 18L, 19L, 20L, 
21L, 22L, 23L, 24L, 25L, 26L, 27L, 28L, 29L, 30L, 31L, 32L, 35L, 
36L, 37L, 38L, 39L, 40L, 41L, 44L, 45L, 46L, 47L, 48L, 49L, 50L, 
53L, 54L, 55L, 56L, 57L, 58L, 59L, 62L, 63L, 64L, 65L, 66L, 67L, 
68L, 69L, 70L, 71L, 72L, 73L, 74L, 75L, 78L, 79L, 80L, 81L, 82L, 
83L, 84L, 87L, 88L, 89L, 90L, 91L, 92L, 95L, 96L, 97L, 98L, 99L, 
100L, 101L, 104L, 105L, 106L, 107L, 108L, 109L, 110L), na.action = structure(c(2L, 
3L, 11L, 12L, 33L, 34L, 42L, 43L, 51L, 52L, 60L, 61L, 76L, 77L, 
85L, 86L, 93L, 94L, 102L, 103L), .Names = c("2", "3", "11", "12", 
"33", "34", "42", "43", "51", "52", "60", "61", "76", "77", "85", 
"86", "93", "94", "102", "103"), class = "omit"), class = "data.frame", .Names = c("Var", 
"Year", "Val"))
ameba diz Restabelecer Monica
fonte
2
Você pode postar os dados? É fácil encontrar exemplos mais ou menos semelhantes, mas, para manter a linha unida, as pessoas com a mesma caixa de areia para brincar ajudariam. Além disso, qual é o significado da zona verde?
Nick Cox
Veja também sugestões em stats.stackexchange.com/questions/126480/…
Nick Cox
@ NickCox Claro, deveria ter pensado nisso antes! Eu deixei de fora da zona verde, uma vez que não é essencial (ele mostra apenas o intervalo de valores que é considerado "suficiente")

Respostas:

42

Felizmente ou não, seu exemplo é de tamanho ideal (até 7 valores para cada um dos 15 grupos) primeiro, para mostrar que há um problema graficamente; e segundo, permitir outras soluções bastante simples. O gráfico é do tipo geralmente chamado de espaguete por pessoas em diferentes campos, embora nem sempre seja claro se esse termo deve ser afetuoso ou abusivo. O gráfico mostra o comportamento coletivo ou familiar de todos os grupos, mas é bastante inútil mostrar os detalhes a serem explorados.

Uma alternativa padrão é apenas mostrar os grupos separados em painéis separados, mas isso, por sua vez, pode dificultar comparações precisas entre grupos; cada grupo é separado do seu contexto dos outros grupos.

Então, por que não combinar as duas idéias: um painel separado para cada grupo, mas também mostrar os outros grupos como pano de fundo? Isso depende crucialmente de destacar o grupo que está em foco e subestimar os outros, o que é fácil neste exemplo, dado o uso de cores, espessuras de linhas etc. Em outros exemplos, as escolhas de marcadores ou símbolos de pontos podem ser naturais.

insira a descrição da imagem aqui

Nesse caso, são destacados detalhes de possível importância ou interesse prático ou científico:

  1. Temos apenas um valor para A e M.

  2. Não temos todos os valores para todos os anos em todos os outros casos.

  3. Alguns grupos traçam alto, outros baixo, e assim por diante.

Não vou tentar uma interpretação aqui: os dados são anônimos, mas essa é a preocupação do pesquisador em qualquer caso.

Dependendo do que é fácil ou possível em seu software, há espaço para alterar pequenos detalhes aqui, como a repetição de rótulos e títulos de eixos (existem argumentos simples a favor e contra).

A questão maior é até que ponto essa estratégia funcionará de maneira mais geral. O número de grupos é o principal fator, mais do que o número de pontos em cada grupo. Grosso modo, a abordagem pode trabalhar com até 25 grupos (por exemplo, uma tela 5 x 5): com mais grupos, os gráficos não apenas se tornam menores e mais difíceis de ler, como também o pesquisador perde a inclinação de analisar todos os painéis. Se houvesse centenas (milhares, ...) de grupos, normalmente seria essencial selecionar um pequeno número de grupos para mostrar. Seria necessário algum mix de critérios, como a seleção de alguns painéis "típicos" e "extremos"; isso deve ser orientado pelos objetivos do projeto e por alguma idéia do que faz sentido para cada conjunto de dados. Outra abordagem que pode ser eficiente é enfatizar um pequeno número de séries em cada painel. Tão, se houvesse 25 grupos amplos, cada grupo amplo poderia ser mostrado com todos os outros como pano de fundo. Como alternativa, pode haver alguma média ou outra sumarização. Usar (por exemplo) componentes principais ou independentes também pode ser uma boa ideia.

Embora o exemplo exija gráficos de linhas, o princípio é naturalmente muito geral. Exemplos podem ser multiplicados, gráficos de dispersão, gráficos de diagnóstico de modelos etc.

Algumas referências para essa abordagem [outras são bem-vindas]:

Cox, NJ 2010. Gráficos de subconjuntos. Stata Journal 10: 670-681.

Knaflic, CN 2015. Contar histórias com dados: um guia de visualização de dados para profissionais de negócios. Hoboken, NJ: Wiley.

Koenker, R. 2005. Regressão quantílica. Cambridge: Cambridge University Press. Veja as páginas 12-13.

Schwabish, JA 2014. Guia de um economista para visualizar dados. Journal of Economic Perspectives 28: 209-234.

Unwin, A. 2015. Análise de Dados Gráficos com R. Boca Raton, FL: CRC Press.

Wallgren, A., B. Wallgren, R. Persson, U. Jorner e J.-A. Haaland. 1996. Representação gráfica de estatísticas e dados: Criando gráficos melhores. Newbury Park, CA: Sábio.

Nota: O gráfico foi criado no Stata. subsetplotdeve ser instalado primeiro com ssc inst subsetplot. Os dados foram copiados e colados a partir de R e os rótulos de valor foram definidos para mostrar anos como 90 95 00 05 10 15. O comando principal é

subsetplot connected Val Year, by(Var) c(L) lcolor(gs12) backdrop(line) xtitle("") combine(imargin(small)) subset(lcolor(blue) mcolor(blue))

EDIT Extra referências maio, setembro, dezembro de 2016; Abril, junho de 2017, dezembro de 2018, abril de 2019:

Cairo, A. 2016. A arte verdadeira: dados, gráficos e mapas de comunicação. San Francisco, CA: Novos Cavaleiros. p.211

Camões, J. 2016. Dados em ação: práticas recomendadas para criar gráficos eficazes e gráficos de informações no Microsoft Excel . San Francisco, CA: Novos Cavaleiros. p.354

Carr, DB e Pickle, LW 2010. Visualização de padrões de dados com Micromaps. Boca Raton, FL: CRC Press. p.85.

Grant, R. 2019. Visualização de dados: gráficos, mapas e gráficos interativos. Boca Raton, FL: CRC Press. p.52.

Koponen, J. e Hildén, J. 2019. The Data Visualization Handbook. Espoo: Aalto ARTS Books. Veja a p.101.

Kriebel, A. e Murray, E. 2018. #MakeoverMonday: Melhorando como visualizamos e analisamos dados, um gráfico de cada vez. Hoboken, NJ: John Wiley. 303.

Rougier, NP, Droettboom, M. e Bourne, PE 2014. Dez regras simples para melhores números. Biologia Computacional PLOS 10 (9): e1003833. doi: 10.1371 / journal.pcbi.1003833 link aqui

Schwabish, J. 2017. Better Presentations: A Guide for Scholars, Researchers and Wonks. Nova York: Columbia University Press. Veja a p.98.

Wickham, H. 2016. ggplot2: Gráficos elegantes para análise de dados. Cham: Springer. Veja a p.157.

Nick Cox
fonte
+1, maravilhoso, existe uma função R ou SAS capaz de fazer esse tipo de gráfico? É realmente ótimo.
forecaster
Eu realmente gosto dessa ideia! Apenas imaginando a melhor maneira de plotar isso em R usando o ggplot2. Vou esperar um pouco antes de aceitar a resposta, espero que esteja tudo bem.
2
Desculpe, não tenho idéia de como fazer algo no SAS. Certamente nada Stata pode fazer, R pode fazer tão bem ou melhor, ou pelo menos seus usuários ficam me dizendo ....
Nick Cox
@NickCox Não é um problema, eu descobri, parece realmente bom e é perfeito para o meu propósito.
@NickCox, mais duas referências são 1. Os Elementos de Representação Gráfica de Dados por WS Cleveland . Um novo livro, 2. Contar histórias com dados: um guia de visualização de dados para profissionais de negócios por Cole Nussbaumer Knaflic . capítulo chamado "Estratégias para evitar o gráfico de espaguete".
Forecaster
22

Como complemento à resposta de Nick, aqui está um código R para fazer um gráfico semelhante usando dados simulados:

library(ggplot2)

get_df <- function(label="group A", n_obs=10, drift=runif(1)) {
    df <- data.frame(time=seq(1, n_obs), label=label)
    df$y <- df$time * drift + cumsum(rnorm(n_obs))
    return(df)
}
df_list <- lapply(sprintf("group %s", toupper(letters[1:9])),
                  function(label) { get_df(label) })
df <- do.call(rbind, df_list)
df$label2 <- df$label

p <- (ggplot(df, aes(x=time, y=y, group=label2)) +
      geom_line(size=0.9, alpha=0.8,
                data=df[, c("time", "y", "label2")], color="grey") +
      geom_line(size=1.1, color="black") +
      ylab("") +
      theme_bw() +
      theme(panel.border=element_blank()) +
      theme(strip.background=element_blank()) +
      facet_wrap(~ label))
p
ggsave("example_facet.png", p, width=10, height=8)

plot de exemplo

Adrian
fonte
6

Para aqueles que desejam usar uma ggplot2abordagem em R, considere a facetshadefunção no pacote extracat. Isso oferece uma abordagem geral, não apenas para gráficos de linha. Aqui está um exemplo com gráficos de dispersão (no rodapé desta página ):

data(olives, package="extracat")
library(scales)
fs1 <- facetshade(data = olives,
                  aes(x = palmitic, y = palmitoleic), f = .~Area)
fs1 + geom_point(colour = alpha("black", 0.05)) +
      geom_point(data = olives, colour = "red") +
      facet_wrap(f=~Area, nrow=3) + theme(legend.position="none")

insira a descrição da imagem aqui


EDIT: Usando o conjunto de dados simulado de Adrian de sua resposta anterior:

library(extracat)
facetshade(df, aes(x=time, y=y), f = .~label, bg.all = FALSE, keep.orig = TRUE) +
           geom_line(aes(x=time, y=y, group=orig.label),colour = alpha(1,0.3)) +
           geom_line(data=df, aes(colour=label), size = 1.2) + xlab("") + ylab("")

Outra abordagem é desenhar duas camadas separadas, uma para o plano de fundo e outra para os casos destacados. O truque é desenhar a camada de segundo plano usando o conjunto de dados sem a variável de faceta. Para o conjunto de dados de azeite, o código é:

data(olives, package="extracat")
ggplot(olives, aes(palmitic, palmitoleic)) + 
  facet_wrap(~Area, nrow=3) + 
  geom_point(data=olives %>% select(-Area), colour=alpha("black", 0.05)) + 
  geom_point(data=olives, colour="red") + 
  theme(legend.position="none")
Antony Unwin
fonte
11
Parece uma boa abordagem geral (+1), mas o exemplo específico está mais relacionado a um problema diferente. Um monte de gráficos de dispersão repetidos com regiões diferentemente destacadas não funcionará para a pergunta que é sobre séries temporais.
Sextus Empiricus
@martin Na verdade, é e essa também é a solução de Adrian. Observe que ele usa duas variáveis ​​de rotulagem idênticas para que uma possa ser descartada na camada de segundo plano. A idéia de codificação é mais óbvia com a notação ordenada abaixo e, como muitas vezes, a formatação elegante de gráficos pode mascarar as partes importantes do código. ggplot(df %>% select(-label), aes(x=time, y=y, group=label2)) + geom_line(alpha=0.8, color="grey") + labs(y=NULL) + geom_line(data=df, color="red") + facet_wrap(~ label)
Antony Unwin
5

Aqui está uma solução inspirada no cap. 11.3, a seção "Texas Housing Data", no livro de Hadley Wickham sobre ggplot2 . Aqui, encaixo um modelo linear para cada série temporal, pego os resíduos (que estão centralizados em torno da média 0) e desenhe uma linha de resumo em uma cor diferente.

library(ggplot2)
library(dplyr)
#works with dplyr version 0.4.3.9000 from Github (hadley/dplyr@4f2d7f8), or higher

df1 <- as.data.frame(list(Var = structure(c(1L, 2L, 2L, 2L, 2L, 2L, 2L, 
                                 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 6L, 
                                 6L, 6L, 6L, 6L, 6L, 7L, 7L, 7L, 7L, 7L, 7L, 7L, 8L, 8L, 8L, 8L, 
                                 8L, 8L, 8L, 9L, 9L, 9L, 9L, 9L, 9L, 9L, 11L, 11L, 11L, 11L, 11L, 
                                 11L, 11L, 12L, 12L, 12L, 12L, 12L, 12L, 13L, 14L, 14L, 14L, 14L, 
                                 14L, 14L, 14L, 16L, 16L, 16L, 16L, 16L, 16L, 17L, 17L, 17L, 17L, 
                                 17L, 17L, 17L, 18L, 18L, 18L, 18L, 18L, 18L, 18L), .Label = c("A", 
                                                                                               "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", 
                                                                                               "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"), class = "factor"), 
               Year = c(2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 2011L, 
                        2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 2011L, 2015L, 1991L, 
                        1993L, 1996L, 2000L, 2004L, 2011L, 2015L, 1993L, 1996L, 2000L, 
                        2004L, 2011L, 2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 2011L, 
                        2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 2011L, 2015L, 1991L, 
                        1993L, 1996L, 2000L, 2004L, 2011L, 2015L, 1991L, 1993L, 1996L, 
                        2000L, 2004L, 2011L, 2015L, 1993L, 1996L, 2000L, 2004L, 2011L, 
                        2015L, 2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 2011L, 2015L, 
                        1991L, 1993L, 1996L, 2000L, 2011L, 2015L, 1991L, 1993L, 1996L, 
                        2000L, 2004L, 2011L, 2015L, 1991L, 1993L, 1996L, 2000L, 2004L, 
                        2011L, 2015L), 
               Val = c(25.6, 22.93, 20.82, 24.1, 24.5, 29, 
                       25.55, 24.5, 24.52, 20.73, 25.8, 25.5, 29.5, 27.7, 25.1, 
                       25, 24.55, 26.75, 25, 30.5, 27.25, 25.1, 22.4, 27.07, 26, 
                       29, 27.2, 24.2, 23, 24.27, 27.68, 27, 30.5, 28.1, 24.9, 23.75, 
                       22.75, 27.25, 25, 29, 28.45, 24, 20.25, 17.07, 24.45, 25, 
                       28.5, 26.75, 24.9, 21.25, 20.65, 25.1, 24.5, 26.5, 25.35, 
                       23.5, 21.93, 26.5, 24.5, 29, 29.1, 26.4, 28.1, 23.75, 26.5, 
                       28.05, 27, 30.5, 25.65, 23.3, 23.25, 24.57, 26.07, 27.5, 
                       28.85, 27.7, 22, 23.43, 26.88, 27, 30.5, 29.25, 28.1, 23, 
                       23.8, 28.32, 27, 29.5, 29.15, 27.6)), 
               row.names = c(1L, 4L, 
                           5L, 6L, 7L, 8L, 9L, 10L, 13L, 14L, 15L, 16L, 17L, 18L, 19L, 20L, 
                           21L, 22L, 23L, 24L, 25L, 26L, 27L, 28L, 29L, 30L, 31L, 32L, 35L, 
                           36L, 37L, 38L, 39L, 40L, 41L, 44L, 45L, 46L, 47L, 48L, 49L, 50L, 
                           53L, 54L, 55L, 56L, 57L, 58L, 59L, 62L, 63L, 64L, 65L, 66L, 67L, 
                           68L, 69L, 70L, 71L, 72L, 73L, 74L, 75L, 78L, 79L, 80L, 81L, 82L, 
                           83L, 84L, 87L, 88L, 89L, 90L, 91L, 92L, 95L, 96L, 97L, 98L, 99L, 
                           100L, 101L, 104L, 105L, 106L, 107L, 108L, 109L, 110L), 
               na.action = structure(c(2L, 
                          3L, 11L, 12L, 33L, 34L, 42L, 43L, 51L, 52L, 60L, 61L, 76L, 77L, 
                          85L, 86L, 93L, 94L, 102L, 103L), 
                .Names = c("2", "3", "11", "12","33", "34", "42", "43", "51", "52", "60", 
                           "61", "76", "77", "85", "86", "93", "94", "102", "103"), class = "omit"), 
                class = "data.frame", .Names = c("Var","Year", "Val"))


df1 %>%
        group_by(Var) %>%
        do(mutate(.,resid = resid(lm(Val ~ Year, data=., na.action = na.exclude)))) %>%
        ggplot(aes(Year, resid)) +
        labs(y=paste0("Val "), x="Year") +
        geom_line(aes(group = Var), alpha = 1/5) +
        geom_line(stat = "summary", fun.y = "mean", colour = "red")

insira a descrição da imagem aqui

knb
fonte
11
A principal idéia aqui parece ser que você pode adicionar uma curva resumida de algum tipo para ajudar os olhos e a mente. Concordou, mas em sua resposta, você pode especificar o trade-off da mudança para média (ou nível de referência) 0, em vez de deixar as unidades e valores originais. Especialistas no assunto e / ou clientes podem pensar em termos de 24 ou 28 ou quaisquer que sejam os valores. Naturalmente, os dados aqui são apenas um veículo para a discussão, mas o ponto é altamente geral.
Nick Cox