Wizualizacja wielu zmiennych na jednym wykresie

25

Chciałbym pokazać, w jaki sposób wartości niektórych zmiennych (~ 15) zmieniają się w czasie, ale chciałbym również pokazać, w jaki sposób zmienne różnią się między sobą każdego roku. Więc stworzyłem ten wątek:

wprowadź opis zdjęcia tutaj

Ale nawet przy zmianie schematu kolorów lub dodawaniu różnych typów linii / kształtów wygląda to niechlujnie. Czy istnieje lepszy sposób na wizualizację tego rodzaju danych?

Dane testowe z kodem 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 mówi Przywróć Monikę
źródło
2
Czy możesz opublikować dane? Łatwo jest znaleźć z grubsza podobne przykłady, ale aby utrzymać wątek razem, ludzie mający tę samą piaskownicę do zabawy pomogliby. Jakie jest znaczenie zielonej strefy?
Nick Cox
Zobacz także sugestie w stats.stackexchange.com/questions/126480/…
Nick Cox
@NickCox Sure, powinienem o tym pomyśleć wcześniej!

Odpowiedzi:

42

Na szczęście lub nie, twój przykład ma najpierw optymalny rozmiar (do 7 wartości dla każdej z 15 grup), aby pokazać, że występuje problem graficzny; po drugie, aby umożliwić inne i dość proste rozwiązania. Wykres jest rodzajem często nazywanym spaghetti przez ludzi z różnych dziedzin, chociaż nie zawsze jest jasne, czy termin ten jest uważany za czuły, czy obraźliwy. Wykres pokazuje zbiorowe lub rodzinne zachowanie wszystkich grup, ale beznadziejnie pokazuje szczegóły, które należy zbadać.

Jedną ze standardowych alternatyw jest po prostu pokazanie oddzielnych grup w osobnych panelach, ale to z kolei może utrudnić precyzyjne porównania między grupami; każda grupa jest oddzielona od kontekstu innych grup.

Dlaczego więc nie połączyć obu pomysłów: osobnego panelu dla każdej grupy, ale także pokazać pozostałe grupy jako tło? Zależy to przede wszystkim od podświetlenia grupy, na której skupia się ostrość, i do lekceważenia pozostałych, co w tym przykładzie jest dość łatwe, biorąc pod uwagę użycie koloru linii, grubości itp. W innych przykładach wybory znaczników lub symboli punktowych mogą być naturalne.

wprowadź opis zdjęcia tutaj

W takim przypadku wyróżniono szczegóły dotyczące potencjalnego znaczenia lub zainteresowania praktycznego lub naukowego:

  1. Mamy tylko jedną wartość dla A i M.

  2. Nie mamy wszystkich wartości dla wszystkich podanych lat we wszystkich innych przypadkach.

  3. Niektóre grupy knują wysokie, niektóre niskie i tak dalej.

Nie będę tutaj próbował interpretacji: dane są anonimowe, ale w każdym razie jest to problem badacza.

W zależności od tego, co jest łatwe lub możliwe w twoim oprogramowaniu, możesz tu zmienić małe szczegóły, takie jak powtarzanie etykiet osi i tytułów (istnieją proste argumenty zarówno za, jak i przeciw).

Większy problem dotyczy tego, jak dalece ta strategia będzie działać bardziej ogólnie. Liczba grup jest głównym motorem, bardziej niż liczba punktów w każdej grupie. Z grubsza mówiąc, podejście może działać do około 25 grup (powiedzmy, wyświetlacz 5 x 5): przy większej liczbie grup nie tylko wykresy stają się mniejsze i trudniejsze do odczytania, ale nawet badacz traci skłonność do skanowania wszystkich panele. Gdyby istniały setki (tysiące, ...) grup, zwykle konieczne byłoby wybranie małej liczby grup do wyświetlenia. Potrzebna byłaby mieszanka kryteriów, takich jak wybór niektórych „typowych” i niektórych „ekstremalnych” paneli; powinny wynikać z celów projektu i pewnego wyobrażenia o tym, co ma sens dla każdego zestawu danych. Innym podejściem, które może być skuteczne, jest podkreślenie niewielkiej liczby serii w każdym panelu. Więc, gdyby było 25 szerokich grup, każda szeroka grupa mogłaby być pokazana wraz ze wszystkimi innymi jako tło. Alternatywnie może być jakieś uśrednienie lub inne podsumowanie. Dobrym pomysłem może być również użycie (np.) Głównych lub niezależnych komponentów.

Chociaż przykład wymaga wykresów liniowych, zasada jest oczywiście bardzo ogólna. Przykłady mogą być pomnożone, wykresy rozrzutu, modele wykresów diagnostycznych itp.

Niektóre odniesienia do tego podejścia [inne są mile widziane]:

Cox, NJ 2010. Podzbiory graficzne. Stata Journal 10: 670-681.

Knaflic, CN 2015. Opowiadanie historii z danymi: Przewodnik wizualizacji danych dla profesjonalistów. Hoboken, NJ: Wiley.

Koenker, R. 2005. Regresja kwantowa. Cambridge: Cambridge University Press. Zobacz s. 12–13.

Schwabish, JA 2014. Przewodnik ekonomisty dotyczący wizualizacji danych. Journal of Economic Perspectives 28: 209-234.

Unwin, A. 2015. Graficzna analiza danych z R. Boca Raton, Floryda: CRC Press.

Wallgren, A., B. Wallgren, R. Persson, U. Jorner i J.-A. Haaland. 1996. Wykresy statystyk i danych: tworzenie lepszych wykresów. Newbury Park, Kalifornia: Sage.

Uwaga: wykres został utworzony w Stata. subsetplotnależy najpierw zainstalować za pomocą ssc inst subsetplot. Dane zostały skopiowane i wklejone z R, a etykiety wartości zostały zdefiniowane, aby pokazać lata jako 90 95 00 05 10 15. Główne polecenie to

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

EDIT Dodatkowe odniesienia Maj, wrzesień, grudzień 2016; Kwiecień, czerwiec 2017 r., Grudzień 2018 r., Kwiecień 2019 r .:

Cairo, A. 2016. The Truthful Art: Data, Charts and Maps for Communication. San Francisco, Kalifornia: Nowi jeźdźcy. str.211

Camões, J. 2016. Dane w pracy: najlepsze praktyki tworzenia skutecznych wykresów i grafiki informacyjnej w programie Microsoft Excel . San Francisco, Kalifornia: Nowi jeźdźcy. str. 354

Carr, DB i Pickle, LW 2010. Wizualizacja wzorców danych za pomocą Micromap. Boca Raton, Floryda: CRC Press. s.85.

Grant, R. 2019. Wizualizacja danych: wykresy, mapy i grafika interaktywna. Boca Raton, Floryda: CRC Press. str. 52.

Koponen, J. and Hildén, J. 2019. Podręcznik wizualizacji danych. Espoo: Aalto ARTS Books. Patrz str. 101.

Kriebel, A. i Murray, E. 2018. # Makeover Poniedziałek: Poprawa sposobu wizualizacji i analizy danych, jeden wykres naraz. Hoboken, NJ: John Wiley. s. 303.

Rougier, NP, Droettboom, M. and Bourne, PE 2014. Dziesięć prostych zasad dla lepszych liczb. PLOS Computational Biology 10 (9): e1003833. doi: 10.1371 / journal.pcbi.1003833 link tutaj

Schwabish, J. 2017. Lepsze prezentacje: przewodnik dla uczonych, badaczy i Wonks. Nowy Jork: Columbia University Press. Zobacz str. 98.

Wickham, H. 2016. ggplot2: Elegancka grafika do analizy danych. Cham: Springer. Zobacz str. 157.

Nick Cox
źródło
+1, wspaniale, czy istnieje funkcja R lub SAS, która jest w stanie wykonać ten typ wykresu? To naprawdę świetne.
prezenter
Naprawdę podoba mi się ten pomysł! Zastanawiam się tylko nad najlepszym sposobem wykreślenia tego w R za pomocą ggplot2. Poczekam tylko chwilę, zanim zaakceptuję odpowiedź, mam nadzieję, że to w porządku.
2
Niestety nie mam pojęcia, jak zrobić cokolwiek w SAS. Z pewnością wszystko, co Stata może zrobić, R może zrobić równie dobrze lub lepiej, a przynajmniej tak mówią jej użytkownicy ...
Nick Cox
@NickCox Zupełnie nie stanowi problemu, zrozumiałem, że wygląda naprawdę dobrze i jest idealny do moich celów.
@NickCox, jeszcze dwa odniesienia: 1. Elementy grafowania danych przez WS Cleveland . Nowa książka, 2. Opowiadanie z danymi: Przewodnik wizualizacji danych dla profesjonalistów Cole Nussbaumer Knaflic. Ta książka (nr 2) zawiera studium przypadku rozdział zatytułowany „Strategie unikania wykresu spaghetti”.
prezenter
22

Jako uzupełnienie odpowiedzi Nicka, oto kod R do tworzenia podobnego wykresu przy użyciu danych symulowanych:

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)

przykładowa fabuła

Adrian
źródło
6

Dla tych, którzy chcą zastosować ggplot2podejście w R, rozważ facetshadefunkcję w pakiecie extracat. Jest to ogólne podejście, nie tylko dla wykresów liniowych. Oto przykład z wykresami rozrzutu (u dołu tej strony ):

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

wprowadź opis zdjęcia tutaj


EDYCJA: Używając symulowanego zestawu danych Adriana z jego wcześniejszej odpowiedzi:

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

Innym podejściem jest narysowanie dwóch osobnych warstw, jednej dla tła i jednej dla wyróżnionych przypadków. Sztuką jest narysowanie warstwy tła przy użyciu zestawu danych bez zmiennej aspektowej. W przypadku zestawu danych oliwy z oliwek kod to:

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
źródło
1
Wydaje się to być dobrym ogólnym podejściem (+1), ale konkretny przykład dotyczy bardziej innego problemu. Kilka powtarzających się wykresów rozrzutu z różnie podświetlonymi regionami nie zadziała w przypadku pytania dotyczącego szeregów czasowych.
Sextus Empiricus,
@martin Właściwie to jest i to jest również rozwiązanie Adriana. Zauważ, że używa dwóch identycznych zmiennych etykietowania, aby można było upuścić jedną z nich w warstwie tła. Idea kodowania jest bardziej oczywista z poniższym zapisem tidyverse i, jak często, eleganckie formatowanie grafiki może maskować ważne części kodu. 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

Oto rozwiązanie zainspirowane Ch. 11.3, sekcja „Texas Housing Data”, w książce Hadleya Wickhama na ggplot2 . Tutaj dopasowuję model liniowy do każdej serii czasowej, biorę reszty (które są wyśrodkowane wokół średniej 0) i rysuję linię podsumowującą w innym kolorze.

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

wprowadź opis zdjęcia tutaj

knb
źródło
1
Wydaje się, że główną ideą jest dodanie krzywej podsumowującej, aby pomóc oku i umysłowi. Zgadzam się, ale w swojej odpowiedzi możesz określić kompromis dotyczący przejścia na średnią (lub poziom odniesienia) 0 zamiast pozostawiać oryginalne jednostki i wartości. Specjaliści merytoryczni i / lub klienci mogliby pomyśleć w kategoriach 24 lub 28 lub dowolnych wartości. Oczywiście dane tutaj są tylko narzędziem do dyskusji, ale sprawa jest bardzo ogólna.
Nick Cox