Неправильный цветовой градиент при использовании ковшплота для объединения графиков.

Скажем, у меня есть набор данных со значениями x и y, сгруппированными по двум переменным: grp — это a, b или c, а subgrp — это E, F или G.

  • a имеет y значений в [0, 1]
  • b имеет y значений в [10, 11]
  • c имеет y значений в [100, 101].

Я хотел бы построить график y против x с цветом точки, определяемой y для всех комбинаций grp и subgrp. Поскольку у каждого grp очень разные значения y, я не могу использовать только facet_grid, так как цветовые шкалы были бы бесполезны. Итак, я рисую каждый grp в своем собственном масштабе, а затем соединяю их вместе с plot_grid из cowplot. Я также хочу использовать трехточечный градиент, заданный scale_colour_gradient2. Мой код выглядит так:

# Set RNG seed
set.seed(42)

# Toy data frame
df <- data.frame(x = runif(270), y = runif(270) + rep(c(0, 10, 100), each = 90),
                 grp = rep(letters[1:3], each = 90), subgrp = rep(LETTERS[4:6], 90))

head(df)
#>           x         y grp subgrp
#> 1 0.9148060 0.1362958   a      D
#> 2 0.9370754 0.7853494   a      E
#> 3 0.2861395 0.4533034   a      F
#> 4 0.8304476 0.1357424   a      D
#> 5 0.6417455 0.8852210   a      E
#> 6 0.5190959 0.3367135   a      F

# Load libraries
library(cowplot)
library(ggplot2)
library(dplyr)

# Plotting list
g_list <- list()

# Loop through groups 'grp'
for(i in levels(df$grp)){
  # Subset the data
  df_subset <- df %>% filter(grp == i)
  
  # Calculate the midpoint
  mp <- mean(df_subset$y)
  
  # Print midpoint
  message("Midpoint: ", mp)
  
  g <- ggplot(df_subset) + geom_point(aes(x = x, y = y, colour = y))
  g <- g + facet_grid(. ~ subgrp) + ggtitle(i)
  g <- g + scale_colour_gradient2(low = "blue", high = "red", mid = "yellow", midpoint = mp)
  g_list[[i]] <- g
}
#> Midpoint: 0.460748857570191
#> Midpoint: 10.4696476330981
#> Midpoint: 100.471083269571

plot_grid(plotlist = g_list, ncol = 1)

Создано 17 апреля 2019 г. с помощью пакета reprex (v0.2.1)

В этом коде я указываю среднюю точку цветового градиента как среднее значение y для каждого grp. Я распечатываю это и проверяю правильность. Это.

Мой вопрос: почему мои цветовые шкалы неверны для первых двух графиков?

Похоже, что один и тот же диапазон применяется к каждому grp, несмотря на подгруппу данных. Если я заменю for(i in levels(df$grp)){ на for(i in levels(df$grp)[1]){, цветовая шкала будет правильной для одного полученного графика.


Обновлять

Ладно, это странно. Вставка ggplot_build(g)$data[[1]]$colour непосредственно перед g_list[[i]] <- g решает проблему. Но почему?

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


person Lyngbakr    schedule 17.04.2019    source источник
comment
Обычно это приводит к зацикливанию и ggplot2. Я не уверен в вашем конкретном случае, но, вероятно, это как-то связано с оценкой переменных на графике. См. объяснение здесь и информацию здесь   -  person aosmith    schedule 17.04.2019
comment
@aosmith Это действительно интересно. Итак, предположительно ggplot_build(g)$data[[1]]$colour вызывает оценку и, таким образом, сохраняет цвета такими, какими они должны быть? Кажется, альтернативой является print графики невидимо: invisible(print(g)) непосредственно перед g_list[[i]] <- g.   -  person Lyngbakr    schedule 17.04.2019
comment
Это мое предположение. Одна из вещей, которые мне нравятся в подходе к разбиению набора данных на список по группам, а затем циклическому просмотру наборов данных для создания множества графиков ggplot2 с помощью lapply()/purrr::map(), заключается в том, что он позволяет избежать некоторых из этих проблем.   -  person aosmith    schedule 17.04.2019
comment
@aosmith Хороший материал. Я попробую это. Спасибо за вашу помощь.   -  person Lyngbakr    schedule 17.04.2019
comment
у меня был похожий случай с зацикливанием и ggplot, ответ был, у ggplot проблемы с локальными переменными. Я думаю, здесь примерно так же. Все еще не совсем уверен, почему ggplot ведет себя так. мой старый вопрос   -  person mischva11    schedule 17.04.2019


Ответы (1)


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

set.seed(42)

# Toy data frame
df <- data.frame(x = runif(270), y = runif(270) + rep(c(0, 10, 100), each = 90),
                 grp = rep(letters[1:3], each = 90), subgrp = rep(LETTERS[4:6], 90))

library(cowplot)
library(ggplot2)
library(dplyr)

# Loop through groups 'grp'
g_list <- lapply(
  levels(df$grp), 
  function(i) {
    # Subset the data
    df_subset <- df %>% filter(grp == i)

    # Calculate the midpoint
    mp <- mean(df_subset$y)

    # Print midpoint
    message("Midpoint: ", mp)

    g <- ggplot(df_subset) + geom_point(aes(x = x, y = y, colour = y))
    g <- g + facet_grid(. ~ subgrp) + ggtitle(i)
    g <- g + scale_colour_gradient2(low = "blue", high = "red", mid = "yellow", midpoint = mp)
    g
  }
)
#> Midpoint: 0.460748857570191
#> Midpoint: 10.4696476330981
#> Midpoint: 100.471083269571

plot_grid(plotlist = g_list, ncol = 1)

Создано 17 апреля 2019 г. с помощью пакета reprex (v0.2.1)

person Claus Wilke    schedule 17.04.2019
comment
Рассмотрим by, более обтекаемый, чем вложенные lapply + split или lapply + levels или lapply + unique. - person Parfait; 17.04.2019