首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我可以从"setup_data“访问"map_data”的结果吗?(适用于"compute_layout“,但不适用于"map_data")在ggplot2 ggproto中

我可以从"setup_data“访问"map_data”的结果吗?(适用于"compute_layout“,但不适用于"map_data")在ggplot2 ggproto中
EN

Stack Overflow用户
提问于 2021-06-03 21:10:54
回答 1查看 70关注 0票数 1

我能从setup_data中的"map_data“访问”ggpplot2“的结果吗?

(适用于"compute_layout“,但不适用于"map_data")

大家好。我正在开发一个ggplot2扩展,它将实现一个新的faceting方法。

我不想进入算法的细节,但只需说,我需要首先为输入data的每一行计算一些新列,只有这样,我才能执行compute_layoutmap_data

当然,一种选择是计算我的新列两次,一次在compute_layout中,一次在map_data中,但这将是计算成本的两倍,只是不那么优雅。

setup_paramssetup_data似乎就是为这个精确的用例设计的。

什么不起作用,❌

我正在基于这个大精巧创建一个可复制的小例子。

我刚刚做了一个小小的修改,尝试使用hello函数向数据中添加一个setup_data列。

代码语言:javascript
复制
library(ggplot2)
facet_bootstrap <- function(n = 9, prop = 0.2, nrow = NULL, ncol = NULL, 
                            scales = "fixed", shrink = TRUE, strip.position = "top") {
  facet <- facet_wrap(~.bootstrap, nrow = nrow, ncol = ncol, scales = scales, 
                      shrink = shrink, strip.position = strip.position)
  facet$params$n <- n
  facet$params$prop <- prop
  ggproto(NULL, FacetBootstrap,
          shrink = shrink,
          params = facet$params
  )
}
FacetBootstrap <- ggproto("FacetBootstrap", FacetWrap,
                          setup_data = function(data, params){
                            data[[1]]$hello <- 'world'
                            print("In SETUP_DATA:")
                            print("   names(data):")
                            print(names(data[[1]]))
                            print("")
                            data
                          },
                          compute_layout = function(data, params) {
                            id <- seq_len(params$n)
                            print("In COMPUTE_LAYOUT:")
                            print("   names(data):")
                            print(names(data[[1]]))
                            print("")
                            dims <- wrap_dims(params$n, params$nrow, params$ncol)
                            layout <- data.frame(PANEL = factor(id))
                            if (params$as.table) {
                              layout$ROW <- 1+as.integer((id - 1L) %/% dims[2] + 1L)
                            } else {
                              layout$ROW <- 1+as.integer(dims[1] - (id - 1L) %/% dims[2])
                            }
                            layout$COL <- 2+as.integer((id - 1L) %% dims[2] + 1L)
                            layout <- layout[order(layout$PANEL), , drop = FALSE]
                            rownames(layout) <- NULL
                            # Add scale identification
                            layout$SCALE_X <- if (params$free$x) id else 1L
                            layout$SCALE_Y <- if (params$free$y) id else 1L
                            cbind(layout, .bootstrap = id)
                          },
                          map_data = function(data, layout, params) {
                            print("In MAP_DATA:")
                            print("   names(data):")
                            print(names(data))
                            print("")
                            if (is.null(data) || nrow(data) == 0) {
                              return(cbind(data, PANEL = integer(0)))
                            }
                            n_samples <- round(nrow(data) * params$prop)
                            new_data <- lapply(seq_len(params$n), function(i) {
                              cbind(data[sample(nrow(data), n_samples), , drop = FALSE], PANEL = i)
                            })
                            do.call(rbind, new_data)
                          }
)
ggplot(diamonds, aes(carat, price)) + 
  geom_point(alpha = 0.1) + 
  facet_bootstrap(n = 9, prop = 0.05)

有产出:

代码语言:javascript
复制
[1] "In SETUP_DATA:"
[1] "   names(data):"
 [1] "carat"   "cut"     "color"   "clarity" "depth"   "table"  
 [7] "price"   "x"       "y"       "z"       "hello"  
[1] ""
[1] "In COMPUTE_LAYOUT:"
[1] "   names(data):"
 [1] "carat"   "cut"     "color"   "clarity" "depth"   "table"  
 [7] "price"   "x"       "y"       "z"       "hello"  
[1] ""
[1] "In MAP_DATA:"
[1] "   names(data):"
 [1] "carat"   "cut"     "color"   "clarity" "depth"   "table"  
 [7] "price"   "x"       "y"       "z"      
[1] ""

请注意我的hello是如何在compute_layout中使用的,而不是map_data中的。

✅是什么工作?

作为一种解决方法,我可以创建一些列并使用parameters使用setup_params传递它们。这有点恶心,因为它们不是概念上的“参数”,它们是数据。但如果其他一切都失败了-我会采取这种方法

代码语言:javascript
复制
library(ggplot2)
facet_bootstrap <- function(n = 9, prop = 0.2, nrow = NULL, ncol = NULL, 
                            scales = "fixed", shrink = TRUE, strip.position = "top") {
  facet <- facet_wrap(~.bootstrap, nrow = nrow, ncol = ncol, scales = scales, 
                      shrink = shrink, strip.position = strip.position)
  facet$params$n <- n
  facet$params$prop <- prop
  ggproto(NULL, FacetBootstrap,
          shrink = shrink,
          params = facet$params
  )
}
FacetBootstrap <- ggproto("FacetBootstrap", FacetWrap,
                          setup_params = function(data, params){
                            params$hello <- 'world'
                            print("In SETUP_DATA:")
                            print("   params$hello:")
                            print(params$hello)
                            print("")
                            params
                          },
                          compute_layout = function(data, params) {
                            id <- seq_len(params$n)
                            print("In COMPUTE_LAYOUT:")
                            print("   params$hello:")
                            print(params$hello)
                            print("")
                            dims <- wrap_dims(params$n, params$nrow, params$ncol)
                            layout <- data.frame(PANEL = factor(id))
                            if (params$as.table) {
                              layout$ROW <- 1+as.integer((id - 1L) %/% dims[2] + 1L)
                            } else {
                              layout$ROW <- 1+as.integer(dims[1] - (id - 1L) %/% dims[2])
                            }
                            layout$COL <- 2+as.integer((id - 1L) %% dims[2] + 1L)
                            layout <- layout[order(layout$PANEL), , drop = FALSE]
                            rownames(layout) <- NULL
                            # Add scale identification
                            layout$SCALE_X <- if (params$free$x) id else 1L
                            layout$SCALE_Y <- if (params$free$y) id else 1L
                            cbind(layout, .bootstrap = id)
                          },
                          map_data = function(data, layout, params) {
                            print("In MAP_DATA:")
                            print("   params$hello:")
                            print(params$hello)
                            print("")
                            if (is.null(data) || nrow(data) == 0) {
                              return(cbind(data, PANEL = integer(0)))
                            }
                            n_samples <- round(nrow(data) * params$prop)
                            new_data <- lapply(seq_len(params$n), function(i) {
                              cbind(data[sample(nrow(data), n_samples), , drop = FALSE], PANEL = i)
                            })
                            do.call(rbind, new_data)
                          }
)
ggplot(diamonds, aes(carat, price)) + 
  geom_point(alpha = 0.1) + 
  facet_bootstrap(n = 9, prop = 0.05)

具有以下输出

代码语言:javascript
复制
[1] "In SETUP_DATA:"
[1] "   params$hello:"
[1] "world"
[1] ""
[1] "In COMPUTE_LAYOUT:"
[1] "   params$hello:"
[1] "world"
[1] ""
[1] "In MAP_DATA:"
[1] "   params$hello:"
[1] "world"
[1] ""

结果摘要

  • 当我从"setup_data“修改数据时,我可以在"compute_layout”✅中访问它。
  • 当我从"setup_data“修改数据时,我无法在"map_data”❌中访问它
  • 当我从"setup_params“修改数据时,我可以在"compute_layout”✅中访问它。
  • 当我从"setup_params“修改数据时,我可以在"map_data”✅中访问它。

最后问题

  • 上面的结果是预期的,还是我只是想错了?
  • 什么是为data计算新列一次的理想方法,然后使它们既可用于map_data,也可用于Facet ggproto

提前感谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-06-03 22:24:05

TL;DR:在datasetup_data函数中的每个列表元素中设置一个新列。

setup_params和setup_data似乎就是为这个精确的用例设计的。

没错,但我从您的问题中得到的印象是,对于数据摄入的操作顺序存在一些混淆。面和坐标是一个图的“布局”的一部分。在设置布局之前,层设置它们的数据(有时会复制全局数据)。然后,布局可以检查数据并进行调整(通常附加面板列)。如果我们检查/打印控制台ggplot2:::Layout$setup,我们会看到以下内容(我的评论):

代码语言:javascript
复制
<ggproto method>
  <Wrapper function>
    function (...) 
f(..., self = self)

  <Inner function (f)>
    function (self, data, plot_data = new_data_frame(), plot_env = emptyenv()) 
{
    data <- c(list(plot_data), data)

    # First `setup_params` is used
    self$facet_params <- self$facet$setup_params(data, self$facet$params)
    self$facet_params$plot_env <- plot_env

    # Second, `setup_data` is used
    data <- self$facet$setup_data(data, self$facet_params)
    self$coord_params <- self$coord$setup_params(data)
    data <- self$coord$setup_data(data, self$coord_params)
    
    # Third, `compute_layout` is used.
    self$layout <- self$facet$compute_layout(data, self$facet_params)
    self$layout <- self$coord$setup_layout(self$layout, self$coord_params)
    check_layout(self$layout)
    
    # Lastly, `map_data` is used for every data *except* the global data!
    lapply(data[-1], self$facet$map_data, layout = self$layout, 
        params = self$facet_params)
}

由此我们得知操作的顺序是setup_params -> setup_data -> compute_layout --> map_data。请注意,map_datalapply(data[-1], ...)开头,其中data是一个具有data.frames的列表,其中位置1为全局数据,之后为层数据。

setup_data方法只将data[[1]]$hello <- 'world'应用于全局数据,而不应用于层数据。将该行替换为data <- lapply(data, cbind, hello = "world")将其应用于全局数据和层数据。此时,每个层都已经拥有自己的(全局数据的副本)数据,因此从效率的角度来看,没有多少方面可以有效地将列附加到全局数据中,而层可以“继承”这些数据。

更确切地说,这是我的建议:

代码语言:javascript
复制
FacetBootstrap <- ggproto(
  "FacetBootstrap", FacetWrap,
  setup_data = function(data, params){
    data <- lapply(data, cbind, hello = "world")
    print("In SETUP_DATA:")
    print("   names(data):")
    print(names(data[[1]]))
    print("")
    data
  },
  ...other code...
)
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67828722

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档