Визуализация

Статичная визуализация

Задание 1

С помощью пакета ggplot2 отрисуйте график рассеяния, отражающий связь таких параметров, как carat и price. Используйте датасет diamonds, сделайте сабсет на 10000 строк (используйте set.seed(1234) для генерации зерна генератора случайных цифр). Удалите строки, в которых carat > 3. Сабсет назовите diamonds_sample.

library(ggplot2)
library(data.table)

set.seed(1234)
# base R
# diamonds_sample <- diamonds[sample(nrow(diamonds), 10000), ]
# diamonds_sample <- diamonds_sample[diamonds_sample$carat <= 3, ]

# data.table
diamonds_sample <- as.data.table(diamonds)
diamonds_sample <- diamonds_sample[sample(.N, 1000)]
diamonds_sample <- diamonds_sample[carat <= 3]


ggplot(diamonds_sample, aes(x = carat, y = price)) +
  geom_point()

Задание 2

Повторите предыдущий график, добавьте выделение цветом бриллиантов разного качества (cut).

ggplot(diamonds_sample, aes(x = carat, y = price, color = cut)) +
  geom_point()

Задание 3

Повторите предыдущий график, добавьте выделение цветом бриллиантов разного качества (cut), а также задайте цветовую палитру, используйте палитру Accent.

ggplot(diamonds_sample, aes(x = carat, y = price, color = cut)) +
  geom_point() + 
  scale_colour_brewer(palette = 'Accent')

Задание 4

Повторите предыдущий график, добавьте выделение цветом бриллиантов в зависимости от их стоимости, используйте градиент от steelblue до magenta.

ggplot(diamonds_sample, aes(x = carat, y = price, color = price)) +
  geom_point() +
  scale_colour_gradient(low = 'steelblue', high = 'magenta')

Задание 5

Добавьте на график из задания 2 вертикальную линию (OX = 2) и горизонтальную линию (OY = 15000).

ggplot(diamonds_sample, aes(x = carat, y = price, color = cut)) +
  geom_point() + 
  geom_vline(xintercept = 2) + 
  geom_hline(yintercept = 15000)

Задание 6

Выделите из образованных вертикальной и горизонтальной линиями секторов первый и третий секторы. Используйте зеленый и розовый цвета соответственно, с параметром прозрачности opacity = 0.1. Сами линии можно не рисовать.

Несмотря на то, что geom_rect() кажется более подходящим, можно использовать annotate() - этот слой добавляет информацию на предыдущие слои, при этом не перекрывает их цветом при наслаивании на основной.

ggplot(diamonds_sample, aes(x = carat, y = price, color = cut)) +
  geom_point() +
  annotate("rect", xmin = 2, xmax = 3, ymin = 15000, ymax = Inf, alpha = 0.1, fill= 'red') + 
  annotate("rect", xmin = 0, xmax = 2, ymin = 0, ymax = 15000, alpha = 0.1, fill= 'green') 

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

ggplot() +
   geom_point(mapping = aes(x = carat, y = price, color = cut), data = diamonds_sample) +
   geom_rect(mapping = aes(xmin = 2, xmax = 3, ymin = 15000, ymax = Inf), fill = 'red', alpha = 0.1) + 
  geom_rect(mapping = aes(xmin = 0, xmax = 2, ymin = 0, ymax = 15000), fill = 'green', alpha = 0.1)

Задание 7

Наложите с помощью функций fitted() и loess() линию тренда, демонстрирующую взаимосвязь размера камня и его цены (price ~ carat).

Для того, чтобы при построении регрессии не учитывалась группировка по цвету, ее лучше вынести в соответствующий геом (geom_point()). `r unhide()

ggplot(diamonds_sample, aes(x = carat, y = price)) +
  geom_point(aes(color = cut)) + 
  geom_smooth(method =  'loess')

Задание 8

Полученную линию тренда сделайте красной, размера 1.5 и пунктирной. Вывод диапазон доверительного интервала сделайте красным с прозрачностью 0.1

Аргумент color задает цвет линии, аргумент fill задает цвет доверительного интервала. Типы линий можно задавать численными значениями, можно названием.

ggplot(diamonds_sample, aes(x = carat, y = price)) +
  geom_point(aes(color = cut)) + 
  geom_smooth(method =  'loess', color = 'red', size = 1.5, linetype = 2, fill = 'red', alpha = 0.1)

Задание 9

Сохраните график из предыдущего задания как объект d_loess. Подпишите график и оси. В заголовке графика сделайте перенос строки.

Для переноса строки можно воспользоваться стандартным знаком переноса строки - \n. Заголовок будет прочитан и обработан как текст, соответственно, подобные управляющие символы вполне применимы.

d_loess <- ggplot(diamonds_sample, aes(x = carat, y = price)) +
  geom_point(aes(color = cut)) + 
  geom_smooth(method =  'loess', color = 'red', size = 1.5, linetype = 2, fill = 'red', alpha = 0.1)

d_loess <- d_loess + labs(
    title = 'Взаимосвязь стоимости бриллиантов и их размера\n + loess-линия тренда',
    x = 'размер в каратах',
    y = 'стоимость в у.е.')

d_loess

Задание 10

Для графика d_loess примените тему theme_classic(). Название графика выровняйте по центру.

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

d_loess + 
  theme_classic() + 
  theme(plot.title = element_text(hjust = 0.5))

Задание 11

Постройте график распределения стоимости бриллиантов в зависимости от качества камня, используйте минималистичную тему:

ggplot(diamonds_sample, aes(x = cut, y = price)) +
  geom_boxplot() + 
  theme_minimal()

Задание 12

Постройте график распределения (в данномс лучае боксплоты) стоимости брилииантов в зависимости от чистоты камня (clarity) и добавьте группировку по качеству огранки камня (cut), используйте минималистичную тему.

ggplot(diamonds_sample, aes(x = clarity, y = price, color = cut)) +
  geom_boxplot() + 
  theme_minimal()

Задание 13

У вас есть датасет, который описывает прибыль компании за последние несколько лет, прибыль складывается из доходов по нескольким продуктам. Нарисуйте столбчатую диаграмму, которая одновременно позволит понять, сколько прибыли компания получает от каждого продукта, и как соотносятся продукты по прибыльности в рамках одного года. Используйте классическую тему.

Аргумент color для столбчатых диаграмм (и боксплотов) задает только цвет линии, для заливки самой фигуры необходимо использовать аргумент fill. Так как датасет не требует дополнительной агрегации, то в аргументе stat необходимо указать значение identity, то есть, брать данные как есть, без дополнительных вычислений.

# создаем датасет
income <- data.frame(
  year = c(2019, 2018, 2017, 2019, 2018, 2017, 2016, 2019, 2018, 2018, 2017, 2016, 2018, 2017, 2016),
  product = c("product_a", "product_a", "product_a", "product_b", "product_b", "product_b", "product_b", "product_c", "product_c", "product_d", "product_d", "product_d", "product_e", "product_e", "product_e"),
  gross = c(1.70, 1.20, 0.30, 0.70, 0.50, 0.41, 0.30, 3.50, 2.90, 2.10, 1.31, 0.69, 0.30, 0.60, 1.10)
)

ggplot(income, aes(x = year, y = gross, color = product, fill = product)) + 
  geom_bar(stat = 'identity') + 
  theme_classic()

Задание 14

Нарисуйте столбчатую диаграмму количества наблюдений в датасете diamonds_sample для каждого уровня качества огранки (cut). Дополнительные агрегации делать не нужно. При визуализации используйте классическую тему оформления.

В геоме geom_bar() значение аргумента stat по умолчанию - count, то есть, вычисляются количество наблюдений для каждого значения по оси OX. Соответственно, достаточно указать, какая переменная используется для оси OX, ось OY задавать не надо - она будет создана автоматически в результате применения геома.

ggplot(diamonds_sample, aes(x = cut, color = cut, fill = cut)) + 
  # geom_bar() +
  geom_bar(stat = 'count') + 
  theme_classic()

Задание 15

Нарисуйте на одном графике несколько подграфиков (фасет), в каждой фасете - график рассеяния, как в задании 2, подграфики сделаны для каждого типа чистоты камня (clarity). Сохраните масштаб по оси OY для всех фасет. Попробуйте разные варианты организации фасет. При визуализации используйте минималистичную тему оформления.

Для размещения фасет можно воспользоваться функциями facet_wrap() или facet_grid(). По умолчанию первая функция организует все подграфики в квадратную матрицу, вторая - в линию или же в вертикальную стопку графиков (в зависимости от формулы). Зафиксировать единый масштаб для всех фасет можно с помощью аргумента scales = 'fixed'.

ggplot(diamonds_sample, aes(x = carat, y = price, color = cut)) +
  geom_point() + 
  facet_wrap(clarity ~ ., scales = 'fixed') +
  labs(title = 'Размещение равномерной решеткой') + 
  theme_minimal()
ggplot(diamonds_sample, aes(x = carat, y = price, color = cut)) +
  geom_point() + 
  facet_grid(. ~ clarity, scales = 'fixed') +
  labs(title = 'Размещение по одной оси') + 
  theme_minimal()

Интерактивная визуализация

Задание 1

С помощью пакета plotly отрисуйте график рассеяния, отражающий связь таких параметров, как carat и price. Используйте датасет ggplot2::diamonds, сделайте сабсет на 10000 строк (используйте set.seed(1234) для генерации зерна генератора случайных цифр). Удалите строки, в которых carat > 3

library(ggplot2)
library(plotly)
library(data.table)

set.seed(1234)
# base R
# diamonds_sample <- diamonds[sample(nrow(diamonds), 1000), ]
# diamonds_sample <- diamonds_sample[diamonds_sample$carat <= 3, ]

# data.table
diamonds_sample <- as.data.table(diamonds)
diamonds_sample <- diamonds_sample[sample(.N, 1000)]
diamonds_sample <- diamonds_sample[carat <= 3]


plot_ly(diamonds_sample, x = ~carat, y = ~price, type = 'scatter', mode = 'markers')

Задание 2

Повторите предыдущий график, добавьте выделение цветом бриллиантов разного качества (cut)

plot_ly(diamonds_sample, x = ~carat, y = ~price, color = ~cut, type = 'scatter', mode = 'markers')

Задание 3

Добавьте на график вертикальную линию (OX = 2) и горизонтальную линию (OY = 15000). Заблокируйте вывод легенды (наследование от основного графика) для этих линий.

Решение использует логику добавления новых слоев на график (add_lines()), аналогично ggplot2. При этом наследуются основные параметры, указанные в plot_ly(). Соответственно, наложенные линии зависят от диапазона данных, по которым рисуется основной график.

plot_ly(diamonds_sample, x = ~carat, y = ~price, color = ~cut, type = 'scatter', mode = 'markers') %>%
  add_lines(x = 2, showlegend = FALSE) %>%
  add_lines(y = 15000, showlegend = FALSE)

Здесь используется механизм кастомизации графика с помощью слоя дополнительных параметров всего графика. В этом слое, помимо заголовка, формата осей и способа сочетание элементов графика, можно указать еще и дополнительные фигуры на коорд.плоскости (shapes).

plot_ly(diamonds_sample, x = ~carat, y = ~price, color = ~cut, type = 'scatter', mode = 'markers') %>%
  layout(
    shapes = list(
      list(type = "line", 
           y0 = 0, y1 = max(diamonds_sample$price), 
           x0 = 2, x1 = 2),
      list(type = "line", 
           y0 = 15000, y1 = 15000, 
           x0 = 0, x1 = 3)
      )
    )

Задание 4

Выделите из образованных вертикальной и горизонтальной линиями секторов первый и третий секторы. Используйте зеленый и розовый цвета соответственно, с параметром прозрачности opacity = 0.1. Сами линии можно не рисовать.

plot_ly(diamonds_sample, x = ~carat, y = ~price, color = ~cut, 
        type = 'scatter', mode = 'markers') %>%
  layout(
    shapes = list(
      list(type = "rect", 
           y0 = 15000, y1 = max(diamonds_sample$price), 
           x0 = 2, x1 = 3, 
           fillcolor = 'red', opacity = 0.1),
      list(type = "rect", 
           y0 = 0, y1 = 15000, 
           x0 = 0, x1 = 2, 
           fillcolor = 'green', opacity = 0.1)
      )
    )

Задание 5

Наложите с помощью функций fitted() и loess() линию тренда, демонстрирующую взаимосвязь размера камня и его цены (price ~ carat).

Так как наследуется не только параметры осей, но и группировка по цветам, то при использовании add_lines() следует заблокировать наследование параметров от plot_ly(), и указать в y функцию фита регрессии.

plot_ly(diamonds_sample, x = ~carat, y = ~price, color = ~cut, 
        type = 'scatter', mode = 'markers') %>%
  add_lines(data = diamonds_sample, x = ~carat, y = ~fitted((loess(price ~ carat))), inherit = FALSE)

Задание 6

Полученную линию тренда сделайте красной, размера 4 и пунктирной. Линию в легенде назовите как loess trend.

plot_ly(diamonds_sample, x = ~carat, y = ~price, color = ~cut, 
        type = 'scatter', mode = 'markers') %>%
  add_lines(data = diamonds_sample, x = ~carat, y = ~fitted((loess(price ~ carat))),
            inherit = FALSE, name = 'loess trend', 
            line = list(color = 'red', width = 4, dash = "dash"))

Задание 7

Подпишите график из предыдущего задания и оси. В заголовке графика сделайте перенос строки.

plot_ly(diamonds_sample, x = ~carat, y = ~price, color = ~cut, 
        type = 'scatter', mode = 'markers') %>%
  add_lines(data = diamonds_sample, x = ~carat, y = ~fitted((loess(price ~ carat))),
            inherit = FALSE, name = 'loess trend', 
            line = list(color = 'red', width = 4, dash = "dash")) %>%
  layout(
    title = 'Взаимосвязь стоимости бриллиантов и их размера<br> + loess-линия тренда',
    xaxis = list(title = 'размер в каратах'),
    yaxis = list(title = 'стоимость в у.е.')
  )

Задание 8

Постройте график распределения стоимости бриллиантов в зависимости от качества камня:

plot_ly(diamonds_sample, x = ~cut, y = ~price, type = 'box')

Задание 9

Постройте график распределения стоимости брилииантов в зависимости от чистоты камня (clarity) и добавьте группировку по качеству огранки камня (cut):

plot_ly(diamonds_sample, x = ~clarity, y = ~price, type = 'box', color = ~cut) %>%
  layout(boxmode = 'group')

Задание 10

У вас есть датасет, который описывает прибыль компании за последние несколько лет, прибыль складывается из доходов по нескольким продуктам. Нарисуйте столбчатую диаграмму, которая одновременно позволит понять, сколько прибыли компания получает от каждого продукта, и как соотносятся продукты по прибыльности в рамках одного года.

Для того, чтобы не было странных подписей по оси OX, надо значения года перевести из численного типа в строковый. Отображение прибыли сразу по нескольким продуктам в рамках одного года нагляднее делать с помощью "составления баров стопкой", аргументом barmode = 'stack' в параметрах всего графика.

# создаем датасет
income <- data.frame(
  year = c(2019, 2018, 2017, 2019, 2018, 2017, 2016, 2019, 2018, 2018, 2017, 2016, 2018, 2017, 2016),
  product = c("product_a", "product_a", "product_a", "product_b", "product_b", "product_b", "product_b", "product_c", "product_c", "product_d", "product_d", "product_d", "product_e", "product_e", "product_e"),
  gross = c(1.70, 1.20, 0.30, 0.70, 0.50, 0.41, 0.30, 3.50, 2.90, 2.10, 1.31, 0.69, 0.30, 0.60, 1.10)
)

plot_ly(income, x = ~as.character(year), y = ~gross, color = ~product, type = 'bar') %>%
  layout(barmode = 'stack')

Задание 11

Нарисуйте два графика, каждый в отдельном слое - обычную столбиковую диаграмму прибыли по продуктам в год, и стакнутую (из предыдущего задания). Подпишите графики по оси OX как 'group' и 'stack'

subplot(
  plot_ly(income, x = ~as.character(year), y = ~gross, color = ~product, type = 'bar'),
  plot_ly(income, x = ~as.character(year), y = ~gross, color = ~product, type = 'scatter', mode = 'none', stackgroup = 'one')
) %>%
  layout(
    barmode = 'stack',
    xaxis = list(title = 'group'),
    xaxis2 = list(title = 'area')
  )

Задание 12

Улучшите предыдущий график - оставьте только одну подпись оси OY, уберите повторы легенды (так, чтобы клик по значку группы в легенде скрывал/отображал соответствующие группы на обоих подграфиках)

Совмещение графиков по оси OY делает с помощью аргумента shareY = TRUE функции subplot(). Синхронизация легенд осуществляется с помощью параметра legendgroup, в который нужно передать группирующую переменную (product).

subplot(
  plot_ly(income, x = ~as.character(year), y = ~gross, color = ~product, type = 'bar', legendgroup = ~product),
  plot_ly(income, x = ~as.character(year), y = ~gross, color = ~product, type = 'scatter', mode = 'none', stackgroup = 'one', legendgroup = ~product, showlegend = FALSE),
  shareY = TRUE
) %>%
  layout(
    barmode = 'stack',
    xaxis = list(title = 'group'),
    xaxis2 = list(title = 'area')
  )

Задание 13

Измените на графике из задания 7 информацию, которая дается в ховере (hover), сделайте более понятными обозначения и округлите значения до сотен, а также добавьте информацию о качестве огранки и чистоте камня. Пример ховера: "carat = 3, price = 8k, cut = Fair, clarity = I1", каждый параметр камня с новой строки. Для линии тренда менять ховер не нужно.

Так как в ховер подается строка, то проще создать отдельную колонку в датасете, в которую записать всю требующуюся информацию.

# data.frame
# diamonds_sample$var_hover <- 
#   paste0('carat = ', diamonds_sample$carat, '<br>',
#          'price = ' , round(diamonds_sample$price, -3), 'k<br>',
#          'cut = ', diamonds_sample$cut, '<br>',
#          'clarity = ', diamonds_sample$clarity)]


# data.table 
setDT(diamonds_sample)
diamonds_sample[, var_hover := paste0('carat = ', carat, '<br>', 
                             'price = ' , round(price, -3), 'k<br>',
                             'cut = ', cut, '<br>',
                             'clarity = ', clarity)]

plot_ly(diamonds_sample, x = ~carat, y = ~price, color = ~cut, 
        type = 'scatter', mode = 'markers',
        text = ~var_hover, hoverinfo = 'text') %>%
  add_lines(data = diamonds_sample, x = ~carat, y = ~fitted((loess(price ~ carat))),
            inherit = FALSE, name = 'loess trend', 
            line = list(color = 'red', width = 4, dash = "dash")) %>%
  layout(
    title = 'Взаимосвязь стоимости бриллиантов и их размера<br> + loess-линия тренда',
    xaxis = list(title = 'размер в каратах'),
    yaxis = list(title = 'стоимость в у.е.')
  )