панель ggplot2 заполняется неправильными значениями, когда находится внутри цикла for

#r #ggplot2

#r #ggplot2

Вопрос:

Я хочу создать матрицу диаграммы рассеяния, используя вложенные циклы for, ggplot2 и grid.arrange. Циклы повторяются по столбцам для создания различных подзаголовков. Это, очевидно, работает, потому что лаборатории подзаголовков различны для каждого участка. Однако ось графика и панель одинаковы для каждого подзаголовка. Теперь становится интересно: используемые столбцы относятся к случаю i = j = 4, который даже находится в другой ветви if-else .

Моей первой попыткой было принудительно выполнить глубокое копирование с помощью функции duplicate() rlang.
Единственный другой намек, который я нашел, состоял в том, чтобы явно использовать print() для отображения графика. смотрите: https://cran.r-project.org/doc/FAQ/R-FAQ.html#Why-do-lattice_002ftrellis-graphics-not-work_003f
Ни один из вариантов ничего не изменил в конечном графике.
Удаление элемента if-else, чтобы всегда генерировать ветвь i<j, создает «тот же» график. Создание 16 графиков вручную (жестко запрограммированных, без циклов) приводит к желаемому поведению.

Пример кода:

 library(tidyverse)
library(rlang)
library(egg)

#species is in last column
n = ncol(iris) - 1
plot_list = list()

for (i in 1:n) {
  for (j in 1:n) {
    #diagonal
    if (i==j) {
      new_plot = ggplot()   annotate("text", x = 4, y = 25, size = 5, label = colnames(iris)[i])   theme_void()
    }
    #upper triangle
    else if (i < j) {
      #assign to new plot nested into a print(), doesn't help
      new_plot = print(
        ggplot()  
        geom_point(aes(x = iris[,j], y = iris[,i], colour=iris[,5]), shape=18, size=3.5)  
        labs(x = colnames(iris)[j], y = colnames(iris)[i])  
        theme_light()  
        theme(legend.position="none")
      )
    }
    #lower triangle
    else {
      new_plot = ggplot()   annotate("text", x = 4, y = 25, size = 5, label = "lower triangle")   theme_void()
    }

    #this doesn't help
    print(new_plot)
    #this doesn't help either
    plot_buffer = duplicate(new_plot, shallow=FALSE)
    #append new plot to plot list
    plot_list = append(plot_list, list(plot_buffer))
  }
}

grid.arrange(grobs = plot_list, top = "grand title")

 

Вот неисправный график. Лепесток.Ширина используется в качестве столбца значений x и y everythyme:
ошибочный сюжет

PS: Я использую R только для визуализации своих данных, поэтому мое внимание сосредоточено на ggplot2. Этот код выполняется внутри узла R view в KNIME.

Ответ №1:

Вы можете использовать aes_string так:

 ggplot(iris)  
  geom_point(aes_string(colnames(iris)[j], colnames(iris)[i], color = "Species"), shape=18, size=3.5)  
  theme_light()  
  theme(legend.position="none")
 

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

Это дает

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

Комментарии:

1. Потрясающе, спасибо. У вас также есть объяснение, почему это работает? В документации указано, что aes() не вычисляется немедленно, в то время как aes_string() выполняется. Правильно ли я это понимаю? 🙂

2. Обычно вы используете ggplot(iris) geom_point(aes(Sepal.Width, Sepal.Height)) . Если вы хотите сделать это программно, как вы делаете, вместо Sepal.Width у вас есть переменная, содержащая строку, скажем x <- "Sepal.Width" . Чтобы иметь возможность использовать это, вам нужно aes_string(x) , поскольку aes(x) будет искать столбец с именем x , которого не существует. Почему ваш код не работает, я не совсем уверен.

3. Этого не может быть, потому что минимальное изменение, необходимое для работы моего кода, — это заменить «aes» на «aes_string». Я по-прежнему передаю весь столбец с помощью x = iris[,i], потому что в реальном наборе данных в именах столбцов есть пробелы (иначе это не сработает, «x =» является необязательным). Однако в будущем мне, вероятно, следует ссылаться на столбцы по их имени.

4. Да, если вы посмотрите plot_list[[2]]$layers[[1]]$mapping после запуска любой из двух версий, вы можете увидеть, что в одной из них была оценена x и y , а в другой — нет, поэтому на заключительном этапе она заменит i и j , которые в этот момент равны 4, так что «Лепесток. Ширина».