Visualizzazione di molte variabili in un solo diagramma


25

Vorrei mostrare come i valori di alcune variabili (~ 15) cambiano nel tempo, ma vorrei anche mostrare come le variabili differiscono l'una dall'altra ogni anno. Quindi ho creato questa trama:

inserisci qui la descrizione dell'immagine

Ma anche quando si cambia la combinazione di colori o si aggiungono diversi tipi di linea / forma, questo sembra disordinato. Esiste un modo migliore per visualizzare questo tipo di dati?

Dati di prova con codice 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"))

2
Puoi pubblicare i dati? È abbastanza facile trovare esempi approssimativamente simili, ma per mantenere il filo unito, le persone con lo stesso sandbox in cui giocare potrebbero aiutare. Inoltre, qual è il significato della zona verde?
Nick Cox,


@NickCox Certo, avrei dovuto pensarci prima! Ho lasciato fuori la zona verde in quanto non è essenziale (mostra solo l'intervallo di valori che è considerato "sufficiente")

Risposte:


42

Fortunatamente o meno, il tuo esempio è di dimensioni ottimali (fino a 7 valori per ciascuno di 15 gruppi) per primo, per mostrare che c'è un problema graficamente; e in secondo luogo, per consentire altre soluzioni abbastanza semplici. Il grafico è di un tipo spesso chiamato spaghetti da persone in diversi campi, anche se non è sempre chiaro se quel termine sia inteso come affettuoso o offensivo. Il grafico mostra il comportamento collettivo o familiare di tutti i gruppi, ma è abbastanza disperato nel mostrare i dettagli da esplorare.

Un'alternativa standard è solo quella di mostrare i gruppi separati in pannelli separati, ma ciò a sua volta può rendere difficili i precisi confronti da gruppo a gruppo; ogni gruppo è separato dal suo contesto rispetto agli altri gruppi.

Quindi perché non combinare entrambe le idee: un pannello separato per ciascun gruppo, ma anche mostrare gli altri gruppi come sfondo? Ciò dipende essenzialmente dall'evidenziare il gruppo che è a fuoco e dal minimizzare gli altri, il che è abbastanza facile in questo esempio dato un certo uso del colore della linea, dello spessore, ecc. In altri esempi, le scelte di marcatori o simboli di punti potrebbero essere invece naturali.

inserisci qui la descrizione dell'immagine

In questo caso, vengono evidenziati i dettagli di una possibile importanza o interesse pratico o scientifico:

  1. Abbiamo un solo valore per A e M.

  2. Non abbiamo tutti i valori per tutti gli anni dati in tutti gli altri casi.

  3. Alcuni gruppi tracciano alti, altri bassi e così via.

Non tenterò un'interpretazione qui: i dati sono anonimi, ma questa è la preoccupazione del ricercatore in ogni caso.

A seconda di ciò che è facile o possibile nel tuo software, qui c'è spazio per modificare piccoli dettagli, come ad esempio se le etichette degli assi e i titoli vengono ripetuti (ci sono argomenti semplici sia a favore che contro).

Il problema più grande è quanto questa strategia funzionerà più in generale. Il numero di gruppi è il driver principale, più che il numero di punti in ciascun gruppo. In parole povere, l'approccio potrebbe funzionare fino a circa 25 gruppi (un display 5 x 5, diciamo): con più gruppi, non solo i grafici diventano più piccoli e più difficili da leggere, ma anche il ricercatore perde l'inclinazione a scansionare tutti i pannelli. Se ci fossero centinaia (migliaia, ...) di gruppi, di solito sarebbe essenziale selezionare un piccolo numero di gruppi da mostrare. Sarebbe necessario un mix di criteri come la selezione di alcuni pannelli "tipici" e alcuni "estremi"; ciò dovrebbe essere guidato dagli obiettivi del progetto e da un'idea di ciò che ha senso per ogni set di dati. Un altro approccio che può essere efficiente è quello di enfatizzare un piccolo numero di serie in ciascun pannello. Così, se ci fossero 25 grandi gruppi, ogni ampio gruppo potrebbe essere mostrato con tutti gli altri come sfondo. In alternativa, potrebbe esserci una media o un altro riepilogo. L'uso (ad esempio) di componenti principali o indipendenti potrebbe anche essere una buona idea.

Sebbene l'esempio richieda grafici a linee, il principio è naturalmente molto generale. Esempi potrebbero essere moltiplicati, grafici a dispersione, grafici diagnostici del modello, ecc.

Alcuni riferimenti per questo approccio [altri sono i benvenuti]:

Cox, New Jersey 2010. Sottoinsiemi grafici. Stata Journal 10: 670-681.

Knaflic, CN 2015. Storytelling with Data: una guida alla visualizzazione dei dati per professionisti. Hoboken, NJ: Wiley.

Koenker, R. 2005. Regressione quantile. Cambridge: Cambridge University Press. Vedi pp.12-13.

Schwabish, JA 2014. Una guida per economisti alla visualizzazione dei dati. Journal of Economic Perspectives 28: 209-234.

Unwin, A. 2015. Analisi grafica dei dati con R. Boca Raton, FL: CRC Press.

Wallgren, A., B. Wallgren, R. Persson, U. Jorner e J.-A. Haaland. 1996. Statistiche e dati grafici: creazione di grafici migliori. Newbury Park, CA: Sage.

Nota: il grafico è stato creato in Stata. subsetplotdeve essere installato prima con ssc inst subsetplot. I dati sono stati copiati e incollati da R e le etichette dei valori sono state definite per mostrare anni come 90 95 00 05 10 15. Il comando principale è

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

EDIT Riferimenti extra Maggio, Settembre, Dicembre 2016; Aprile, giugno 2017, dicembre 2018, aprile 2019:

Cairo, A. 2016. The Truthful Art: Data, Charts, and Maps for Communication. San Francisco, California: New Riders. p.211

Camões, J. 2016. Data at Work: Best practice per la creazione di grafici e informazioni grafiche efficaci in Microsoft Excel . San Francisco, California: New Riders. p.354

Carr, DB and Pickle, LW 2010. Visualizzazione di modelli di dati con Micromaps. Boca Raton, FL: CRC Press. p.85.

Grant, R. 2019. Visualizzazione dei dati: grafici, mappe e grafica interattiva. Boca Raton, FL: CRC Press. p.52.

Koponen, J. e Hildén, J. 2019. Manuale di visualizzazione dei dati. Espoo: Aalto ARTS Books. Vedi p.101.

Kriebel, A. e Murray, E. 2018. #MakeoverMonday: migliorare il modo in cui visualizziamo e analizziamo i dati, un grafico alla volta. Hoboken, NJ: John Wiley. p.303.

Rougier, NP, Droettboom, M. e Bourne, PE 2014. Dieci semplici regole per figure migliori. PLOS Computational Biology 10 (9): e1003833. doi: 10.1371 / journal.pcbi.1003833 link qui

Schwabish, J. 2017. Presentazioni migliori: una guida per studiosi, ricercatori e Wonks. New York: Columbia University Press. Vedi p.98.

Wickham, H. 2016. ggplot2: Grafica elegante per analisi dei dati. Cham: Springer. Vedi p.157.


+1, meraviglioso, esiste una funzione R o SAS in grado di eseguire questo tipo di grafico? E 'davvero grandioso.
meteorologo

Mi piace davvero questa idea! Mi chiedo solo il modo migliore per tracciare questo in R usando ggplot2. Aspetterò un po 'prima di accettare la risposta, spero che vada bene.

2
Spiacente, non ho idea di come fare qualcosa in SAS. Sicuramente tutto ciò che Stata può fare, R può fare altrettanto o meglio, o così i suoi utenti continuano a dirmelo ....
Nick Cox,

@NickCox Non è un problema, l'ho capito, sembra davvero buono ed è perfetto per il mio scopo.

@NickCox, altri due riferimenti sono 1. The Elements of Graphing Data di WS Cleveland . Un nuovo libro, 2. Storytelling with Data: A Data Visualization Guide for Business Professionals by Cole Nussbaumer Knaflic . Questo libro (# 2) ha un case study capitolo intitolato "Strategie per evitare il grafico degli spaghetti".
meteorologo

22

A complemento della risposta di Nick, ecco un codice R per creare un grafico simile usando dati simulati:

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)

trama di esempio


6

Per coloro che desiderano utilizzare un ggplot2approccio in R, considerare la facetshadefunzione nel pacchetto extracat. Questo offre un approccio generale, non solo per i grafici a linee. Ecco un esempio con grafici a dispersione (dal fondo di questa pagina ):

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")

inserisci qui la descrizione dell'immagine


EDIT: utilizzando il set di dati simulato di Adrian dalla sua risposta precedente:

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("")

Un altro approccio è disegnare due livelli separati, uno per lo sfondo e uno per i casi evidenziati. Il trucco è disegnare il livello di sfondo usando il set di dati senza la variabile di sfaccettatura. Per il set di dati dell'olio d'oliva il codice è:

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")

1
Questo sembra un buon approccio generale (+1), ma l'esempio particolare è più relativo a un problema diverso. Un gruppo di grafici a dispersione ripetuti con regioni evidenziate in modo diverso non funzionerà per la domanda che riguarda le serie temporali.
Sesto Empirico

@martin In realtà lo è e questa è anche la soluzione di Adrian. Nota che usa due variabili di etichettatura identiche in modo che una possa essere rilasciata nel livello di sfondo. L'idea di codifica è più ovvia con la notazione ordinata di seguito e, come spesso accade, l'elegante formattazione della grafica può mascherare le parti importanti del codice. 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

Ecco una soluzione ispirata al cap. 11.3, la sezione "Texas Housing Data", nel libro di Hadley Wickham su ggplot2 . Qui inserisco un modello lineare per ogni serie temporale, prendo i residui (che sono centrati attorno alla media 0) e traccia una linea di riepilogo con un colore diverso.

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")

inserisci qui la descrizione dell'immagine


1
L'idea principale qui sembra essere che potresti aggiungere una curva di riepilogo di qualche tipo per aiutare l'occhio e la mente. D'accordo, ma nella tua risposta potresti precisare il compromesso spostandoti su media (o livello di riferimento) 0 anziché lasciare le unità e i valori originali. Esperti in materia e / o clienti potrebbero ben pensare in termini di 24 o 28 o di qualunque valore siano. Naturalmente i dati qui sono solo un veicolo per la discussione, ma il punto è molto generale.
Nick Cox,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.