我能从setup_data中的"map_data“访问”ggpplot2“的结果吗?
(适用于"compute_layout“,但不适用于"map_data")
大家好。我正在开发一个ggplot2扩展,它将实现一个新的faceting方法。
我不想进入算法的细节,但只需说,我需要首先为输入data的每一行计算一些新列,只有这样,我才能执行compute_layout和map_data。
当然,一种选择是计算我的新列两次,一次在compute_layout中,一次在map_data中,但这将是计算成本的两倍,只是不那么优雅。
setup_params和setup_data似乎就是为这个精确的用例设计的。
什么不起作用,❌
我正在基于这个大精巧创建一个可复制的小例子。
我刚刚做了一个小小的修改,尝试使用hello函数向数据中添加一个setup_data列。
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)有产出:
[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传递它们。这有点恶心,因为它们不是概念上的“参数”,它们是数据。但如果其他一切都失败了-我会采取这种方法
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)具有以下输出
[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] ""结果摘要
最后问题
data计算新列一次的理想方法,然后使它们既可用于map_data,也可用于Facet ggproto提前感谢!
发布于 2021-06-03 22:24:05
TL;DR:在data的setup_data函数中的每个列表元素中设置一个新列。
setup_params和setup_data似乎就是为这个精确的用例设计的。
没错,但我从您的问题中得到的印象是,对于数据摄入的操作顺序存在一些混淆。面和坐标是一个图的“布局”的一部分。在设置布局之前,层设置它们的数据(有时会复制全局数据)。然后,布局可以检查数据并进行调整(通常附加面板列)。如果我们检查/打印控制台ggplot2:::Layout$setup,我们会看到以下内容(我的评论):
<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_data以lapply(data[-1], ...)开头,其中data是一个具有data.frames的列表,其中位置1为全局数据,之后为层数据。
setup_data方法只将data[[1]]$hello <- 'world'应用于全局数据,而不应用于层数据。将该行替换为data <- lapply(data, cbind, hello = "world")将其应用于全局数据和层数据。此时,每个层都已经拥有自己的(全局数据的副本)数据,因此从效率的角度来看,没有多少方面可以有效地将列附加到全局数据中,而层可以“继承”这些数据。
更确切地说,这是我的建议:
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...
)https://stackoverflow.com/questions/67828722
复制相似问题