【问题标题】:R: geom_smooth + geom_hline makes memory usage explodeR:geom_smooth + geom_hline 使内存使用量爆炸式增长
【发布时间】:2020-11-23 22:09:19
【问题描述】:

我正在处理 >1 GB 的数据集,并在 ggplot2 图形中遇到内存不足(“无法分配...”)错误。在尝试研究我所有内存的去向时(借助 this 和 this 和 this 等来源,我发现以下带有虚拟数据的代码会导致大量内存使用,而这似乎在即使在多次调用gc() 之后,Windows 任务管理器也是如此。

print(begMemSize <- memory.size())

library(ggplot2)
numRows <- 1e6
df <- data.frame( x1 = runif(numRows), x2 = runif(numRows), xGroup = factor(trunc(runif(numRows, 1, 6))) )
df$y = df$x1 + df$x2

gc()
print(mid1MemSize <- memory.size())

# This is fine
ggplot( data = df, mapping = aes( x = x1)) +
  geom_smooth( mapping = aes( y = y))

gc()
print(mid2MemSize <- memory.size())

# This makes memory.size() explode
ggplot( data = df, mapping = aes( x = x1)) +
  geom_smooth( mapping = aes( y = y)) +
  geom_hline( mapping = aes( yintercept = 0.25))

gc()
print(endMemSize <- memory.size())

表达式c( begMemSize, mid1MemSize, mid2MemSize, endMemSize) 返回:

[1]   50.62  102.30  199.22 1208.39

注意最后一个数字的巨大跳跃。最后一个数字与 Windows 任务管理器中的读数匹配(非常接近“内存(活动工作集)”,仅略低于“详细信息”选项卡中的“提交大小”)。有时,通过反复调用gc(),我可以让memory.size() 在R 中下降,但不能在Windows 任务管理器中显示读数。我担心我的内存不足错误与此有关,但我的直接问题是:

  1. 为什么会这样?
  2. 在这种情况下,有什么方法可以降低 Windows 任务管理器的内存读数(显然不会关闭 R 并丢失内存中的所有数据处理)?

sessionInfo() 输出(使用 RStudio 1.3.1056):

R version 4.0.2 (2020-06-22)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19041)

Matrix products: default

Random number generation:
 RNG:     Mersenne-Twister 
 Normal:  Inversion 
 Sample:  Rounding 
 
locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252    LC_MONETARY=English_United States.1252 LC_NUMERIC=C                           LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] ggplot2_3.3.2

loaded via a namespace (and not attached):
 [1] rstudioapi_0.11  magrittr_1.5     splines_4.0.2    tidyselect_1.1.0 munsell_0.5.0    colorspace_1.4-1 lattice_0.20-41  R6_2.4.1         rlang_0.4.6      dplyr_1.0.0      tools_4.0.2      grid_4.0.2      
[13] gtable_0.3.0     nlme_3.1-148     mgcv_1.8-31      withr_2.2.0      ellipsis_0.3.1   digest_0.6.25    tibble_3.0.1     lifecycle_0.2.0  crayon_1.3.4     Matrix_1.2-18    farver_2.0.3     purrr_0.3.4     
[25] vctrs_0.3.1      glue_1.4.1       labeling_0.3     compiler_4.0.2   pillar_1.4.4     generics_0.0.2   scales_1.1.1     pkgconfig_2.0.3 

【问题讨论】:

  • 这个例子的另一个奇怪的地方是endMemSize返回的值比最后调用gc()的输出高很多。
  • 你能把sessionInfo()的输出也贴出来吗?
  • 您的代码可以在我的 PC 上运行:Windows 10、16GB RAM、R 4.0.2 64 位和 ggplot 3.3.2
  • 感谢您的回复。我添加了sessionInfo() 输出。看起来我正在使用与您相同版本的 R & ggplot2。当您说我的代码在您的 PC 上“工作”时,您是说您最终没有看到内存使用量的大幅飙升,您无法停止?

标签: r ggplot2 memory memory-management


【解决方案1】:

这只是问题的一个方面的部分答案。

当您将yintercept 放入aes() 函数中时,您将指示ggplot2 将yintercept 美学映射到data 参数中的每一行。因此,geom_hline() 层将其数据转换为包含许多行的大型 data.frame。如果您不将其放在aes() 函数中,而是将其用作图层的普通参数,则图层数据将保持较小。请参阅下面的示例。

library(ggplot2)
numRows <- 1e6
df <- data.frame( x1 = runif(numRows), x2 = runif(numRows), xGroup = factor(trunc(runif(numRows, 1, 6))) )
df$y = df$x1 + df$x2

p <- ggplot( data = df, mapping = aes( x = x1)) +
  geom_smooth( mapping = aes( y = y))

p_mapped <- p + geom_hline(mapping = aes(yintercept = 0.25))
p_unmapped <- p + geom_hline(yintercept = 0.25)

layer_mapped <- layer_data(p_mapped, 2)
#> `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
layer_unmapped <- layer_data(p_unmapped, 2)
#> `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

(format(object.size(layer_mapped), units = "Mb"))
#> [1] "42 Mb"
(format(object.size(layer_unmapped), units = "Kb"))
#> [1] "2.2 Kb"

请注意,虽然图层数据的大小不会占用大量已用内存,但必须记住,许多事情都是在数据上计算的,从计算轴限制到将 alpha 应用于颜色。

此外,通过运行您的示例并将定义 yintercept 的位置从映射参数更改为层的常规参数,endMemSize 对我来说约为 200Mb。

最后,ggplot2 在其命名空间中保留了最后一个绘图的副本,用户看不到该副本。但是,您可以使用set_last_plot(NULL) 来释放一些额外的内存。

【讨论】:

  • 这是正确的。此外,映射版本不只绘制一条线,它在一条线之上绘制了一百万条线。您可能需要修改您的答案以澄清这一点。
  • 我查看了GeomHline$draw_panel(),它似乎通过调用unique() 减少了数据。派生自p_mapped 和p_unmapped 的gtable 对象的大小非常相似。
  • 啊,这可能最近变了。过去,我们在多次绘制线条时遇到了很多问题。
  • 感谢您的解释!看起来数据参数可以确定geom_hline 的x 轴标签,但这在我的reprex b/c 中显然是不必要的,我可以只将df 传递给geom_smooth。所以这是一个很好的解决方法。尽管如此,即使在set_last_plot(NULL) 调用之后,reprex 仍然使任务管理器的内存使用量> 1GB,这对我来说很奇怪。相关 GitHub 问题:github.com/tidyverse/ggplot2/issues/4167.
  • 我同意这很奇怪。我还得到 R 报告的内存使用量比任务管理器报告的要小,我不知道是什么原因造成的。
猜你喜欢
  • 1970-01-01
  • 2018-09-15
  • 1970-01-01
  • 2020-06-17
  • 1970-01-01
  • 2018-08-07
  • 1970-01-01
  • 2017-12-10
  • 2017-09-27
相关资源
最近更新 更多