首页 > 解决方案 > 在 for 循环中添加 geom_*

问题描述

我想在一张图中将真实世界的数据与模拟数据进行比较。代码应该接受任意数量的线来绘制。我想出了这个:

simulationRuns <- 5 #Variable to be changed depending on how many simulations were made

plotLoop <- ggplot() + 
  geom_line(data = relWorldData, 
            mapping = aes(x = DateTime, y = VALUE, color = "realWorldData"))

for (i in 1:simulationRuns){
    plotLoop <- plotLoop +
      geom_line(data = listOfSimResults[[i]], 
                mapping = aes(x = DateTime, y = VALUE, color = paste0("simRun-", i)))
  }

figureLoop <- ggplotly(plotLoop)

问题是,所有行都显示为 simRun-5,因此不是独立的 -

看图片

我是 R 新手,所以请怜悯;)提前谢谢,帕特里克


后续问题 bc。在评论中阅读代码很糟糕:

我阅读了 Lapply 并将代码重写为:

plotLoop <- ggplot() + geom_line(data = relWorldData, mapping = aes(x = DateTime, y = VALUE, color = "RealWorldData"))

  addGeomLine <- function (i, obj){
    obj <- obj +
      geom_line(data = listOfSimResults[[i]], mapping = aes(x = DateTime, y = VALUE, color = paste0("simRun-", i)))
  }
  lapply(1:runs, addGeomLine, plotLoop)

  figureLoop <- ggplotly(plotLoop)

这一次,只显示 RealWorldData,但没有显示任何 Simulations。你能告诉我我错过了什么吗?

标签: rfor-loopggplot2

解决方案


欢迎来到 SO!

你遇到了一个微妙的问题,让很多比你有更多经验的人感到困惑。问题是懒惰地ggplot2评估。简而言之,这意味着当你告诉它你想要什么时,它会“记下”它需要做什么,但实际上直到最后一刻才做任何事情。

在这里,你告诉 ggplot 你想geom在你的for循环中添加一个。ggplot 记下geom' 的定义,但不评估它。“在最后一刻”是你打电话的时候ggplotly。现在ggplot意识到它有一些工作要做。对于每一个geom,它注意到它需要知道 的值i。所以它查找它并找到 value 5。因此你的问题。

有几种方法可以解决这个问题。使用您的代码,我首选的选择是forlapply. 与for循环不同,lapply强制在执行时评估变量。

我相信您也可以保留for循环并将每个引用包装i在 in 中force(),尽管我没有亲自尝试过。

在我看来,从长远来看,最好的方法是让你的工作流程整洁,避免for循环或lapply完全不需要。这还将为您带来更紧凑、更健壮和可读的代码的好处,这些代码几乎肯定会运行得更快。[前几天我做了一些工作,将类似于你的循环转换为一个整洁的解决方案,运行时间从近 40 秒减少到 2 秒以下。]

另外,请阅读这篇文章以获取有关如何创建最小工作示例的建议。提供 MWE 将最大限度地提高您获得有用答案的机会。

更新

为了扩展我对使用整洁数据方法的优势的评论......

首先合成一些数据,因为您没有提供任何数据。我将尝试匹配您的数据结构,但不匹配您的值。您的数据集的唯一区别是我添加了一个ID变量来识别每个观察来自的模拟运行/真实世界数据集。

library(lubridate)
library(tidyverse)

inVivoBG <- tibble(
              ID="Real-world data",
              DateTime2=seq(as_date("2006-03-01"), as_date("2015-03-01"), "3 months"),
              VALUE=100 + rnorm(37, mean=150, sd=20)
            ) 

listOfSimResults <- lapply(
                      1:5, 
                      function(x) {
                        tibble(
                          ID=paste0("simRun-", x),
                          DateTime2=seq(as_date("2006-03-01"), as_date("2015-03-01"), "3 months"),
                          VALUE=100 + rnorm(37, mean=150, sd=20)
                        )
                      }
                    )

现在将各种数据框组合成一个。

data <- bind_rows(inVivoBG, listOfSimResults)

此时,您的情节的构建是单行调用。

data %>% 
  ggplot() + 
    geom_line(mapping = aes(x = DateTime2, y = VALUE, color = ID)) 

给予

使用整洁的方法输出

这种方法避免了对自定义函数的需要或对lapply. 它在所需的行数及其标签方面也很健壮。就个人而言,我也认为它更容易理解。


推荐阅读