Визуализация многих переменных в одном графике

25

Я хотел бы показать, как значения определенных переменных (~ 15) меняются со временем, но я также хотел бы показать, как переменные отличаются друг от друга в каждом году. Итак, я создал этот сюжет:

введите описание изображения здесь

Но даже при изменении цветовой схемы или добавлении различных типов линий / форм это выглядит грязно. Есть ли лучший способ визуализировать такие данные?

Тестовые данные с кодом 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
Вы можете опубликовать данные? Достаточно легко найти примерно одинаковые примеры, но, если связать нить, люди, имеющие одинаковую «песочницу» для игры, помогут. Кроме того, каково значение зеленой зоны?
Ник Кокс
См. Также предложения в stats.stackexchange.com/questions/126480/…
Ник Кокс
@NickCox Конечно, должен был подумать об этом раньше! Я пропустил зеленую зону, так как она не обязательна (она показывает только диапазон значений, который считается "достаточным")

Ответы:

42

Случайно или нет, ваш пример имеет оптимальный размер (до 7 значений для каждой из 15 групп), чтобы показать, что существует проблема графически; и во-вторых, чтобы позволить другие и довольно простые решения. График такого типа часто называют спагетти людьми в разных областях, хотя не всегда понятно, подразумевается ли этот термин как ласковый или оскорбительный. График показывает коллективное или семейное поведение всех групп, но он довольно безнадежен, показывая детали для изучения.

Одна стандартная альтернатива - просто показать отдельные группы на отдельных панелях, но это, в свою очередь, может затруднить точное сравнение между группами; каждая группа отделена от контекста других групп.

Так почему бы не объединить обе идеи: отдельную панель для каждой группы, а также показать другие группы в качестве фона? Это зависит главным образом от выделения группы, которая находится в фокусе, и от недооценки других, что достаточно просто в этом примере, учитывая некоторое использование цвета линии, толщины и т. Д. В других примерах выбор маркера или точечного символа может быть естественным.

введите описание изображения здесь

В этом случае выделяются детали возможного практического или научного значения или интереса:

  1. У нас есть только одно значение для A и M.

  2. У нас нет всех значений для всех данных лет во всех других случаях.

  3. Некоторые группы строят высокие, некоторые низкие и так далее.

Я не буду пытаться интерпретировать здесь: данные анонимны, но в любом случае это проблема исследователя.

В зависимости от того, что легко или возможно в вашем программном обеспечении, здесь есть возможность изменить мелкие детали, например, повторяются ли метки оси и заголовки (существуют простые аргументы как за, так и против).

Большая проблема заключается в том, насколько эта стратегия будет работать в целом. Количество групп является основным фактором, больше, чем количество баллов в каждой группе. Грубо говоря, этот подход может работать примерно до 25 групп (скажем, с дисплеем 5 x 5): с увеличением числа групп графики не только становятся меньше и труднее для чтения, но даже исследователь теряет склонность сканировать все панели. Если бы были сотни (тысячи, ...) групп, обычно было бы необходимо выбрать небольшое количество групп для показа. Потребуется некоторое сочетание критериев, таких как выбор «типичных» и «экстремальных» панелей; это должно зависеть от целей проекта и некоторого представления о том, что имеет смысл для каждого набора данных. Другой подход, который может быть эффективным, состоит в том, чтобы выделить небольшое количество серий в каждой панели. Так, если бы было 25 широких групп, каждая широкая группа могла бы быть показана со всеми другими в качестве фона. В качестве альтернативы может быть некоторое усреднение или другое суммирование. Использование (например) главных или независимых компонентов также может быть хорошей идеей.

Хотя этот пример требует линейных графиков, этот принцип, естественно, носит гораздо более общий характер. Примеры могут быть умножены, точечные диаграммы, модельные диагностические диаграммы и т. Д.

Некоторые ссылки на этот подход [другие приветствуются]:

Кокс, Нью-Джерси 2010. Графические подмножества. Stata Journal 10: 670-681.

Knaflic, CN 2015. Рассказывание историй с данными: Руководство по визуализации данных для бизнес-профессионалов. Хобокен, Нью-Джерси: Wiley.

Кенкер Р. 2005. Квантильная регрессия. Кембридж: издательство Кембриджского университета. Смотри стр.12-13.

Швабиш, JA 2014. Руководство экономиста по визуализации данных. Журнал экономических перспектив 28: 209-234.

Unwin, A. 2015. Графический анализ данных с R. Boca Raton, FL: CRC Press.

Уоллгрен А., Б. Уоллгрен, Р. Перссон, У. Йорнер и Ж.-А. Haaland. 1996. Графическая статистика и данные: создание лучших графиков. Ньюбери Парк, Калифорния: Мудрец.

Примечание. График был создан в Stata. subsetplotдолжен быть установлен первым с ssc inst subsetplot. Данные были скопированы и вставлены из R и метки значений были определены, чтобы показать годы как 90 95 00 05 10 15. Основная команда

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

РЕДАКТИРОВАТЬ Дополнительные ссылки май, сентябрь, декабрь 2016 года; Апрель, июнь 2017 года, декабрь 2018 года, апрель 2019 года:

Каир, а. 2016. Истинное искусство: данные, диаграммы и карты для общения. Сан-Франциско, Калифорния: Новые Райдеры. p.211

Camões, J. 2016. Данные на работе: лучшие практики для создания эффективных диаграмм и информационной графики в Microsoft Excel . Сан-Франциско, Калифорния: Новые Райдеры. p.354

Карр, Д.Б. и Пикл, Л.В. 2010. Визуализация шаблонов данных с помощью микрокарт. Бока-Ратон, Флорида: CRC Press. с.85.

Грант, р. 2019. Визуализация данных: диаграммы, карты и интерактивная графика. Бока-Ратон, Флорида: CRC Press. с.52.

Копонен, Дж. И Хильден, Дж. 2019. Справочник по визуализации данных. Эспоо: Aalto ARTS Books. Смотри с.101.

Крибель, А. и Мюррей, Е. 2018. #MakeoverMonday: Улучшение того, как мы визуализируем и анализируем данные, по одной диаграмме за раз. Хобокен, Нью-Джерси: Джон Уайли. с.303.

Rougier, NP, Droettboom, M. and Bourne, PE 2014. Десять простых правил для улучшения показателей. PLOS Computational Biology 10 (9): e1003833. doi: 10.1371 / journal.pcbi.1003833 ссылка здесь

Швабиш, J. 2017. Лучшие презентации: руководство для ученых, исследователей и Wonks. Нью-Йорк: издательство Колумбийского университета. Смотри с.98.

Wickham, H. 2016. ggplot2: Элегантная графика для анализа данных. Чам: Спрингер. Смотри с.157.

Ник Кокс
источник
+1, замечательно, есть ли функция R или SAS, которая способна делать диаграммы такого типа? Это действительно здорово.
синоптик
Мне очень нравится эта идея! Просто интересно, как лучше построить это в R, используя ggplot2. Я просто подожду немного, прежде чем приму ответ, надеюсь, что все в порядке.
2
Извините, я понятия не имею, как что-то делать в SAS. Конечно, все, что может сделать Stata, R может делать так же или лучше, или так его пользователи постоянно говорят мне ...
Ник Кокс
@NickCox Ничего страшного, я понял, это выглядит очень хорошо и идеально подходит для моих целей.
@NickCox, еще две ссылки: 1. Элементы графического представления данных У.С. Кливленда . Новая книга, 2. Рассказывание историй с данными: Руководство по визуализации данных для бизнес-профессионалов Коул Нуссбаумер Knaflic. В этой книге (# 2) есть тематическое исследование Глава под названием «Стратегии избегания графа спагетти».
синоптик
22

В качестве дополнения к ответу Ника вот код R для создания аналогичного графика с использованием смоделированных данных:

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)

пример сюжета

Адриан
источник
6

Для тех, кто хочет использовать ggplot2подход в R, рассмотрим facetshadeфункцию в пакете extracat. Это предлагает общий подход, а не только для линейных участков. Вот пример с диаграммами рассеяния (из подножия этой страницы ):

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

введите описание изображения здесь


РЕДАКТИРОВАТЬ: Используя смоделированный набор данных Адриана из его предыдущего ответа:

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

Другой подход - нарисовать два отдельных слоя, один для фона и один для выделенных случаев. Хитрость заключается в том, чтобы нарисовать фоновый слой с использованием набора данных без переменной фасетирования. Для набора данных оливкового масла код:

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
Это похоже на хороший общий подход (+1), но конкретный пример больше относится к другой проблеме. Группа повторяющихся диаграмм рассеяния с по-разному выделенными областями не подойдет для вопроса о временных рядах.
Секст Эмпирик
@martin На самом деле это так, и это тоже решение Адриана. Обратите внимание, что он использует две идентичные переменные маркировки, чтобы одну можно было удалить в фоновом слое. Идея кодирования более очевидна с приведенной ниже нотацией, и, как это часто бывает, элегантное форматирование графики может маскировать важные части кода. 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)
Энтони Анвин
5

Вот решение, вдохновленное гл. 11.3, раздел «Данные по жилью в Техасе», в книге Хэдли Уикхем на ggplot2 . Здесь я подгоняю линейную модель к каждому временному ряду, беру остатки (которые сосредоточены вокруг среднего 0) и рисую итоговую линию другим цветом.

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

введите описание изображения здесь

KNB
источник
1
Основная идея здесь заключается в том, что вы могли бы добавить сводную кривую некоторого вида, чтобы помочь глазу и уму. Согласен, но в своем ответе вы могли бы изложить компромисс о переходе на среднее (или опорный уровень) 0 , а не оставляя исходные единицы и значения. Эксперты в данной области и / или клиенты вполне могут мыслить с точки зрения 24 или 28 или любых других ценностей. Естественно, приведенные здесь данные являются лишь средством обсуждения, но этот вопрос носит весьма общий характер.
Ник Кокс