首页 > 解决方案 > 复制的 ggplot-object 与原始对象部分链接

问题描述

为了改变用 ggplot 制作的绘图的一些布局方面,我首先将原始 ggplot-object 复制到一个新变量中,以保持原始不变。当我更新副本时,我注意到原始对象中的某些内容也已更新,就好像它们是链接的一样。但并非所有更改都发生这种情况,好像它取决于更改的参数类型。

有没有人对此行为有解释和解决方案?

这是一个例子:

library(tidyverse)

# Create ggplot-object
irisplot <- ggplot(iris, aes(x=Species, y=Sepal.Width, color=Species)) +
  geom_boxplot(outlier.alpha=0) +
  geom_jitter(width=0.5)

# Copy into new variable
newvariable <- irisplot
tracemem(irisplot)==tracemem(newvariable) # prints FALSE with rlang::duplicate(irisplot) and TRUE without
untracemem(irisplot)
untracemem(newvariable)

##### Linked: layers
# Check original outlier.alpha
irisplot$layers[[1]]$geom_params$outlier.alpha # prints 0 for me
newvariable$layers[[1]]$geom_params$outlier.alpha # prints 0 for me

# Update outlier.alpha in copy
newvariable$layers[[1]]$geom_params$outlier.alpha <- 1
tracemem(irisplot)==tracemem(newvariable)

# Check updated outlier.alpha
newvariable$layers[[1]]$geom_params$outlier.alpha # prints 1 for me
irisplot$layers[[1]]$geom_params$outlier.alpha # prints 1 for me

untracemem(irisplot)
untracemem(newvariable)

##### Not linked: labels
# Check original x-label
irisplot$labels$x # prints "Species" for me
newvariable$labels$x # prints "Species" for me

# Updated x-label
newvariable$labels$x <- "New label"

# Check updated x-label
newvariable$labels$x # prints "New label" for me
irisplot$labels$x # prints "Species" for me

我包括tracemem检查它是否是由于最初共享内存地址,但替换复制行newvariable <- rlang::duplicate(irisplot)并不会改变链接行为。

一个稍微相关的问题:通过覆盖特定参数,以这种方式修改现有的 ggplot-object 似乎可以解决问题,但是是否有更清洁/官方的方式来处理它?例如。标签我可以使用主题,但是对于特定于几何的东西呢?

编辑:

我的设置:Windows 10,R 版本 3.6.1,ggplot2 3.3.2,rlang 0.4.7

得到相同结果的同事:windows 10,R 版本 4.0.3,ggplot2 3.3.2,rlang 0.4.7

标签: rggplot2

解决方案


ggplot2使用环境作为其ggproto对象的基础。在 R 中,环境是通过引用传递的,因此在复制 ggplot 时不会得到深层副本。有几种方法可以获得深层副本,但也许最简单的方法是unserialize(serialize(ggplot))

library(ggplot2)

irisplot <- ggplot(iris, aes(x=Species, y=Sepal.Width, color=Species)) +
  geom_boxplot(outlier.alpha=0) +
  geom_jitter(width=0.5)

# Make a deep copy
newvariable <- unserialize(serialize(irisplot, NULL))

# objects are currently the same
irisplot$layers[[1]]$geom_params$outlier.alpha
#> [1] 0
newvariable$layers[[1]]$geom_params$outlier.alpha 
#> [1] 0

# Update outlier.alpha in copy
newvariable$layers[[1]]$geom_params$outlier.alpha <- 1

# Check updated outlier.alpha
newvariable$layers[[1]]$geom_params$outlier.alpha 
#> [1] 1
irisplot$layers[[1]]$geom_params$outlier.alpha 
#> [1] 0

reprex 包(v0.3.0)于 2020-12-14 创建


推荐阅读