Visualisation de nombreuses variables dans un seul tracé

25

J'aimerais montrer comment les valeurs de certaines variables (~ 15) changent au fil du temps, mais j'aimerais aussi montrer comment les variables diffèrent les unes des autres chaque année. J'ai donc créé cette intrigue:

entrez la description de l'image ici

Mais même lorsque vous changez le jeu de couleurs ou ajoutez différents types de lignes / formes, cela semble désordonné. Existe-t-il une meilleure façon de visualiser ce type de données?

Testez les données avec le code 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"))
amibe dit réintégrer Monica
la source
2
Pouvez-vous publier les données? Il est assez facile de trouver des exemples à peu près similaires, mais pour garder le fil attaché, les personnes ayant le même bac à sable pourraient y aider. Aussi, quelle est l'importance de la zone verte?
Nick Cox
Voir aussi les suggestions dans stats.stackexchange.com/questions/126480/…
Nick Cox
@NickCox Bien sûr, aurait dû y penser plus tôt! J'ai omis la zone verte car elle n'est pas essentielle (elle ne montre que la plage de valeurs qui est considérée comme "suffisante")

Réponses:

42

Par hasard ou autrement, votre exemple est d'abord de taille optimale (jusqu'à 7 valeurs pour chacun des 15 groupes), pour montrer qu'il y a un problème graphiquement; et deuxièmement, pour permettre d'autres solutions assez simples. Le graphique est d'un type souvent appelé spaghetti par des personnes de différents domaines, bien qu'il ne soit pas toujours clair si ce terme est considéré comme affectueux ou abusif. Le graphique montre le comportement collectif ou familial de tous les groupes, mais il est assez désespéré pour montrer les détails à explorer.

Une alternative standard consiste simplement à montrer les groupes séparés dans des panneaux séparés, mais cela peut à son tour rendre les comparaisons précises de groupe à groupe difficiles; chaque groupe est séparé de son contexte des autres groupes.

Alors pourquoi ne pas combiner les deux idées: un panneau séparé pour chaque groupe, mais aussi montrer les autres groupes en toile de fond? Cela dépend essentiellement de la mise en évidence du groupe qui est au point et de la minimisation des autres, ce qui est assez facile dans cet exemple étant donné une certaine utilisation de la couleur des lignes, de l'épaisseur, etc.

entrez la description de l'image ici

Dans ce cas, les détails d'une éventuelle importance ou intérêt pratique ou scientifique sont mis en évidence:

  1. Nous n'avons qu'une seule valeur pour A et M.

  2. Nous n'avons pas toutes les valeurs pour toutes les années données dans tous les autres cas.

  3. Certains groupes tracent haut, certains bas, etc.

Je ne tenterai pas d'interprétation ici: les données sont anonymes, mais c'est en tout cas la préoccupation du chercheur.

Selon ce qui est facile ou possible dans votre logiciel, il est possible de modifier ici les petits détails, par exemple si les étiquettes et les titres des axes sont répétés (il y a des arguments simples pour et contre).

Le plus gros problème est de savoir dans quelle mesure cette stratégie fonctionnera plus généralement. Le nombre de groupes est le principal moteur, plus encore que le nombre de points dans chaque groupe. En gros, l'approche pourrait fonctionner jusqu'à environ 25 groupes (un affichage 5 x 5, disons): avec plus de groupes, non seulement les graphiques deviennent plus petits et plus difficiles à lire, mais même le chercheur perd l'envie de balayer tous les panneaux. S'il y avait des centaines (des milliers, ...) de groupes, il serait généralement essentiel de sélectionner un petit nombre de groupes à afficher. Une combinaison de critères tels que la sélection de certains panneaux «typiques» et «extrêmes» serait nécessaire; cela devrait être guidé par les objectifs du projet et une idée de ce qui a du sens pour chaque ensemble de données. Une autre approche qui peut être efficace consiste à souligner un petit nombre de séries dans chaque panneau. Alors, s'il y avait 25 grands groupes, chaque grand groupe pourrait être montré avec tous les autres comme toile de fond. Alternativement, il pourrait y avoir une moyenne ou un autre résumé. L'utilisation (par exemple) de composants principaux ou indépendants peut également être une bonne idée.

Bien que l'exemple demande des tracés linéaires, le principe est naturellement très général. Les exemples pourraient être multipliés, les diagrammes de dispersion, les diagrammes de diagnostic de modèle, etc.

Quelques références pour cette approche [d'autres sont les bienvenues]:

Cox, NJ 2010. Représentation graphique des sous-ensembles. Journal Stata 10: 670-681.

Knaflic, CN 2015. Raconter des histoires avec des données: un guide de visualisation des données pour les professionnels. Hoboken, NJ: Wiley.

Koenker, R. 2005. Régression quantile. Cambridge: Cambridge University Press. Voir p.12-13.

Schwabish, JA 2014. Un guide de l'économiste pour visualiser les données. Journal of Economic Perspectives 28: 209-234.

Unwin, A. 2015. Analyse des données graphiques avec R. Boca Raton, FL: CRC Press.

Wallgren, A., B. Wallgren, R. Persson, U. Jorner et J.-A. Haaland. 1996. Représentation graphique des statistiques et des données: création de meilleurs graphiques. Newbury Park, Californie: Sage.

Remarque: Le graphique a été créé dans Stata. subsetplotdoit être installé en premier avec ssc inst subsetplot. Les données ont été copiées et collées à partir de R et les étiquettes de valeur ont été définies pour montrer les années comme 90 95 00 05 10 15. La commande principale est

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

EDIT Extra références mai, septembre, décembre 2016; Avril, juin 2017, décembre 2018, avril 2019:

Le Caire, A. 2016. The Truthful Art: Data, Charts, and Maps for Communication. San Francisco, Californie: nouveaux cavaliers. p.211

Camões, J. 2016. Les données au travail: les meilleures pratiques pour créer des graphiques et des graphiques d'informations efficaces dans Microsoft Excel . San Francisco, Californie: nouveaux cavaliers. p.354

Carr, DB et Pickle, LW 2010. Visualisation des modèles de données avec des micro-cartes. Boca Raton, FL: CRC Press. p.85.

Grant, R. 2019. Visualisation des données: graphiques, cartes et graphiques interactifs. Boca Raton, FL: CRC Press. p.52.

Koponen, J. et Hildén, J. 2019. The Data Visualization Handbook. Espoo: Aalto ARTS Books. Voir p.101.

Kriebel, A. et Murray, E. 2018. #MakeoverMonday: Améliorer la façon dont nous visualisons et analysons les données, un graphique à la fois. Hoboken, NJ: John Wiley. p.303.

Rougier, NP, Droettboom, M. et Bourne, PE 2014. Dix règles simples pour de meilleurs chiffres. PLOS Computational Biology 10 (9): e1003833. doi: 10.1371 / journal.pcbi.1003833 lien ici

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

Wickham, H. 2016. ggplot2: Élégants graphiques pour l'analyse des données. Cham: Springer. Voir p.157.

Nick Cox
la source
+1, merveilleux, existe-t-il une fonction R ou SAS capable de faire ce type de graphique? C'est vraiment super.
prévisionniste
J'aime vraiment cette idée! Je me demande juste la meilleure façon de tracer cela dans R en utilisant ggplot2. Je vais attendre un peu avant d'accepter la réponse, j'espère que ça va.
2
Désolé, je n'ai aucune idée de comment faire quoi que ce soit dans SAS. Certes, tout ce que Stata peut faire, R peut le faire aussi bien ou mieux, ou alors ses utilisateurs me disent toujours ...
Nick Cox
@NickCox Pas un problème du tout, je l'ai compris, il a l'air vraiment bien et est parfait pour mon but.
@NickCox, deux autres références sont 1. The Elements of Graphing Data par WS Cleveland .Un nouveau livre, 2. Storytelling with Data: A Data Visualization Guide for Business Professionals par Cole Nussbaumer Knaflic . Ce livre (# 2) a une étude de cas chapitre intitulé "Stratégies pour éviter le graphique de spaghetti".
prévisionniste
22

En complément de la réponse de Nick, voici un code R pour faire un tracé similaire en utilisant des données simulées:

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)

exemple d'intrigue

Adrian
la source
6

Pour ceux qui souhaitent utiliser une ggplot2approche en R, considérez la facetshadefonction dans le package extracat. Cela offre une approche générale, pas seulement pour les tracés linéaires. Voici un exemple avec des nuages ​​de points (en bas de cette page ):

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

entrez la description de l'image ici


EDIT: En utilisant l'ensemble de données simulé d'Adrian de sa réponse précédente:

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

Une autre approche consiste à dessiner deux couches distinctes, une pour l'arrière-plan et une pour les cas mis en évidence. L'astuce consiste à dessiner la couche d'arrière-plan à l'aide du jeu de données sans la variable de facettage. Pour l'ensemble de données sur l'huile d'olive, le code est:

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
la source
1
Cela semble être une bonne approche générale (+1), mais l'exemple particulier concerne davantage un problème différent. Un tas de diagrammes de dispersion répétés avec des régions surlignées différemment ne fonctionnera pas pour la question qui concerne les séries chronologiques.
Sextus Empiricus
@martin En fait, c'est et c'est aussi la solution d'Adrian. Notez qu'il utilise deux variables d'étiquetage identiques afin qu'une seule puisse être déposée dans le calque d'arrière-plan. L'idée de codage est plus évidente avec la notation inversée ci-dessous et, comme souvent, un formatage élégant des graphiques peut masquer les parties importantes du code. 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

Voici une solution inspirée de Ch. 11.3, la section «Texas Housing Data», dans le livre de Hadley Wickham sur ggplot2 . Ici, j'adapte un modèle linéaire à chaque série chronologique, je prends les résidus (qui sont centrés autour de la moyenne 0) et je trace une ligne de résumé d'une couleur différente.

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

entrez la description de l'image ici

knb
la source
1
L'idée principale ici semble être que vous pourriez ajouter une courbe récapitulative quelconque pour aider l'œil et l'esprit. D'accord, mais dans votre réponse, vous pourriez expliquer le compromis sur le passage à la moyenne (ou au niveau de référence) 0 plutôt que de laisser les unités et les valeurs d'origine. Les experts en la matière et / ou les clients pourraient bien penser en termes de 24 ou 28 ou quelles que soient les valeurs. Naturellement, les données ici ne sont qu'un véhicule pour la discussion, mais le point est très général.
Nick Cox