Ggplot2: неточное заполнение многоуровневого графика

Обратите внимание на следующее. Почему я не получаю точную заливку и маркировку? Почему я получаю только одноцветную заливку? Обратите внимание, что версия без цикла работает отлично

data(iris)
xx <- list(iris, iris)
xx[[1]]$Sepal.Length <- rnorm(150)
xx[[2]]$Sepal.Length <- rnorm(150,mean = 10)

cols <- c("low" = "#e41a1c", "high"= "#377eb8")
p2 <- p <- ggplot() 
for (i in 1:2) {
  p <- p + geom_histogram(data=xx[[i]], aes(x= Sepal.Length, stat(density), fill= factor(names(cols)[i])))
  p2 <- p2 + geom_histogram(data=xx[[i]], aes(x= Sepal.Length, stat(density), fill= factor(cols[i])))
}
p + scale_fill_manual(values= cols)
p2 + scale_fill_manual(values= cols)

iris <- xx[[1]]
iris2 <- xx[[2]]
p3 <- ggplot() +
  geom_histogram(data= iris, aes(x= Sepal.Length, stat(density), fill= factor("low"))) +
  geom_histogram(data= iris2, aes(x= Sepal.Length, stat(density), fill= factor("high"))) +
  scale_fill_manual(values= cols)
p3

Ggplot2: неточное заполнение многоуровневого графикаGgplot2: неточное заполнение многоуровневого графика

Ggplot2: неточное заполнение многоуровневого графика

Это ожидаемое поведение. aes(x= Sepal.Length, stat(density), fill= factor(cols[i])) будет удерживать выражение factor(cols[i]) и оценивать его до тех пор, пока выражение не понадобится. В вашем случае это две строки после цикла for, где i == 2

TC Zhang 17.08.2018 02:48
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
91
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это не способ ggplot.

Вместо этого рассмотрите возможность сделать следующее

data(iris)
xx <- list(iris, iris)
xx[[1]]$Sepal.Length <- rnorm(150)
xx[[2]]$Sepal.Length <- rnorm(150,mean = 10)

cols <- c("low" = "#e41a1c", "high"= "#377eb8")
names(xx) <- names(cols)

library(tidyverse)
bind_rows(xx, .id = "Key") %>%
    ggplot(aes(Sepal.Length, fill = Key)) +
    geom_histogram()

Объяснение: Мы связываем data.frame в ряд в list, сохраняя имя элемента list в Key. Затем у нас есть один data.frame, и мы используем эстетику fill для сопоставления разных Key с разными заливками.

Ответ принят как подходящий

Моя гипотеза заключалась в том, что стек вызовов вычисляется лениво. То есть отображение заливки на names(cols)[i] без вычисления этого выражения. Следовательно, отображение идентично в scale_fill_manual.

В конечном итоге это верно. Как отмечает @maurits выше, ggplot2 не предназначен для этого варианта использования. Ниже работает

# @description Process a set of input data.frame's to extract only the 
# column of interest and assign a group name
dtlist_inputs <- function(dt_list, varname, groups, input= TRUE) {
  dt_list <- do.call('rbind', (mapply(function(l, groupname, varname) {
    return(data.frame(
      group= groupname
      , var= get(varname, as.environment(l))
    ))
    }, l= dt_list, groupname= groups, varname= varname, SIMPLIFY= FALSE))) 
  return(dt_list)
}

# @description Histogram for multiple groups, where each group is input as a separate data.frame.
# @param dt_list a \code{list} of \code{data.frame}s
# @param cols a named vector of colour hexes.
# @varname A \code{character} scalar denoting the variable in each element of \code{dt_list} you wish to plot.
# @binwidth To be passed to `\code{\link[ggplot2]{geom_histogram}}.
# @x_lab To be passed to `\code{\link[ggplot2]{labs}}.
# @grouptitle To be passed to `\code{\link[ggplot2]{guides}} for legend label.
# @x_breaks To be passed to `\code{\link[ggplot2]{scale_x_continuous}} for axis breaks.
GGgroup_histogram <- function(dt_list, cols, varname, binwidth= 0.02,
        grouptitle= "Error Group", 
        x_lab= "Variable", x_breaks= seq(0,1,.1)) {
  if (length(dt_list) != length(cols)) stop("length of dt_list and cols must match.")

  dt_list <- dtlist_inputs(dt_list, varname, groups= names(cols))
  sub_title <- paste("Variable:", varname)

  p <- ggplot() + 
    geom_histogram(data= dt_list, aes(x=var, stat(density), fill= factor(group)),
                   binwidth= binwidth, alpha= 0.5)
  p <- p + labs(y= "Density", x= x_lab,
             title= "Distributional differences in relative abs error between groups",
             subtitle= sub_title) +
    scale_fill_manual(values= cols) +
    scale_x_continuous(breaks= x_breaks) +
    guides(fill= guide_legend(title= grouptitle)) +
    theme(legend.position = "bottom",
          axis.title= element_text(face= "bold", size= 11),
          axis.text= element_text(size= 10),
          plot.title= element_text(face= "bold.italic", size= 13),
          plot.subtitle= element_text(face= "bold.italic", size= 12))
  return(p)
}

Редактировать Мы также можем сравнить идею использования dplyr::bind_rows (предложенную выше) с моей естественной склонностью предпочитать data.table::rbindlist:

set.seed(6231)
d <- data.frame(x1= rnorm(1000), x2= rnorm(1000), x3= rnorm(100))
d <- replicate(n= 10, expr= d, simplify = FALSE)
library(microbenchmark)
microbenchmark(
     base= do.call('rbind', d),
     dplyr= dplyr::bind_rows(d),
     data.table= data.table::rbindlist(d)
)

Unit: microseconds
       expr     min      lq      mean   median       uq        max neval cld
       base 697.373 783.108 2484.0814 842.7745 923.9245 121350.787   100   a
      dplyr 161.385 186.140  279.2899 211.4355 229.4350   3488.347   100   a
 data.table 108.853 137.612  247.6596 159.0105 181.3150   5874.138   100   a

## with replicate(... n= 1000)
Unit: milliseconds
   expr        min         lq      mean     median        uq       max neval cld
      base 211.718863 480.355404 707.60993 722.989094 948.77399 1020.6656   100   b
     dplyr  16.597688  18.184643  72.09627  21.374848  25.19133  293.0039   100  a 
data.table   3.122027   3.480451  49.24004   6.545908   9.54836  270.4440   100  a 

Другие вопросы по теме