【问题标题】:ggplots stored in plot list to respect variable values at time of plot generation within for loopggplots 存储在绘图列表中以在 for 循环内生成绘图时尊重变量值
【发布时间】:2020-06-17 07:32:11
【问题描述】:

我有一个精心制作的绘图例程,可以生成带有额外散布层的箱形图,并将它们添加到绘图列表中。

如果在 for 循环期间直接通过 print(current_plot_complete) 创建图,则例程会生成正确的图。

但是,如果在 for 循环期间将它们添加到仅在末尾打印的绘图列表中,则绘图不正确:最终索引用于生成 所有 绘图(而不是生成图时的当前索引)。 这似乎是默认的 ggplot2 行为,我正在寻找在当前用例中规避它的解决方案。

问题似乎出在y = eval(parse(text=(paste0(COL_i)))) 中,其中使用了全局环境(以及最终索引值),而不是循环执行时的当前值。

我尝试了各种方法来使 eval() 使用正确的变量值,例如local(…) 或指定环境 - 但没有成功。

下面提供了一个非常简化的 MWE。

MWE

原来的例程比这个 MWE 复杂得多,因此 for 循环不能轻易地被 apply 家族的成员替换。

# create some random data
data_temp <- data.frame(
"a" = sample(x = 1:100, size  = 50),
"b" = rnorm(n = 50, mean = 45, sd = 1),
"c" = sample(x = 20:70, size  = 50), 
"d" = rnorm(n = 50, mean = 40, sd = 15),
"e" = rnorm(n = 50, mean = 50, sd = 10),
"f" = rnorm(n = 50, mean = 45, sd = 1),
"g" = sample(x = 20:70, size  = 50)
)
COLs_current <- c("a", "b", "c", "d", "e") # define COLs of data to include in box plots
choice_COLs <- c("a", "d")      # define COLs of data to add scatter to

plot_list <- list(NA)
plot_index <- 1

for (COL_i in choice_COLs) {

  COL_i_index <- which(COL_i == COLs_current)

  # Generate "basis boxplot" (to plot scatterplot on top)
  boxplot_scores <- data_temp %>% 
    gather(COL, score, all_of(COLs_current)) %>%
    ggplot(aes(x = COL, y = score)) +
    geom_boxplot() 

  # Get relevant data of COL_i for scattering: data of 4th quartile
  quartile_values <- quantile(data_temp[[COL_i]])
  threshold <- quartile_values["75%"]           # threshold = 3. quartile value
  data_temp_filtered <- data_temp %>%
    filter(data_temp[[COL_i]] > threshold) %>%  # filter the data of the 4th quartile
    dplyr::select(COLs_current)                 

  # Create layer of scatter for 4th quartile of COL_i
  scatter_COL_i <- geom_point(data=data_temp_filtered, mapping = aes(x = COL_i_index, y = eval(parse(text=(paste0(COL_i))))), color= "orange")

  # add geom objects to create final plot for COL_i
  current_plot_complete <- boxplot_scores + scatter_COL_i 

  print(current_plot_complete)

  plot_list[[plot_index]] <- current_plot_complete 
  plot_index <- plot_index + 1
}

plot_list

【问题讨论】:

    标签: r ggplot2 plot eval


    【解决方案1】:

    我提出的这个解决方案并没有告诉你为什么它不像你那样工作:

    l <- lapply(choice_COLs, temporary_function)
    
    temporary_function <- function(COL_i){
        COL_i_index <- which(COL_i == COLs_current)
    
        # Generate "basis boxplot" (to plot scatterplot on top)
        boxplot_scores <- data_temp %>% 
            gather(COL, score, all_of(COLs_current)) %>%
            ggplot(aes(x = COL, y = score)) +
            geom_boxplot() 
    
        # Get relevant data of COL_i for scattering: data of 4th quartile
        quartile_values <- quantile(data_temp[[COL_i]])
        threshold <- quartile_values["75%"]           # threshold = 3. quartile value
        data_temp_filtered <- data_temp %>%
            filter(data_temp[[COL_i]] > threshold) %>%  # filter the data of the 4th quartile
            dplyr::select(COLs_current)                 
    
        # Create layer of scatter for 4th quartile of COL_i
        scatter <- geom_point(data=data_temp_filtered,
                              mapping = aes(x = COL_i_index,
                                            y = eval(parse(text=(paste0(COL_i))))),
                              color= "orange")
    
        # add geom objects to create final plot for COL_i
        current_plot_complete <-  boxplot_scores + scatter
    
        return(current_plot_complete)
        }
    

    当您使用lapply 时,您不会遇到这样的问题。 它的灵感来自this post

    【讨论】:

    • 非常感谢您使用 lapply(...) 提供的功能齐全的解决方案。虽然起初没有预料到,但我能够解决我精心制作的版本,以确认您的散文解决方案 - 效果很好!
    • 将存储在列表l 中的图排列在联合图中需要子集l,即ggpubr::ggarrange(l[[1]], l[[2]])。否则运行ggpubr::ggarrange(l) 会抛出以下错误In as_grob.default(plot) : Cannot convert object of class list into a grob.
    【解决方案2】:

    我认为问题在于ggplot 使用惰性求值。当list 被渲染时,循环索引有它的最终值,也就是用来渲染列表中所有图的那个。

    This post 是相关的。

    【讨论】:

    • 顺便说一句,您的绘图过程的复杂性表明您的数据在您要实现的目标的上下文中可能并不整洁。
    • 确实,“问题”是ggplot 的默认行为的结果,如上所述。我试图说服ggplot 存储或至少引用正确的数据,方法是将data 参数添加到您引用的帖子中提出的geom_point(data=data_temp_filtered,...,但它没有帮助。
    猜你喜欢
    • 2017-06-12
    • 2016-07-14
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多