在没有创建它的代码的情况下从 ggplot object 更改功能
Change features from ggplot object without the code that created it
我正在尝试了解有关 ggplot object 的工作原理的更多信息。我试图在没有初始代码的情况下实现更改 ggplot object 的目标。所以在这个例子中我使用 sjPlot
输出模型 object model_obj
。通过不依赖用于制作情节的初始代码,我希望能够改变情节的 3 件事。我希望能够
- 将图例标题从“因变量”更改为“结果”(我真的不在乎它叫什么它只是一个测试)
- Re-order y 轴上的系数
- 更改 x 轴边界
我知道 plot_models
函数已经允许其中的一些功能,但我正在尝试了解更多有关 ggplot 工作原理的信息。
我在网上找到的一个想法是,人们使用此 ggplot_build
函数从绘图 object 中提取一些特征,然后对其进行更改,然后将其还原为具有 ggplot_gtable
的绘图。但是,我不明白输出的哪些部分对应于上面的 3 个东西。
我也对使用 +
符号向 ggplot object 添加新功能的选项感兴趣
model_obj + ggtitle("New title")
但我不确定这在模型 object 的初始代码未知的情况下是否总是有用。
这是我的代码
require(sjPlot)
data(mtcars)
# fit models
fit1 <- glm(vs ~ mpg + disp + drat,family="binomial", data=mtcars)
fit2 <- glm(am ~ mpg + disp + drat,family="binomial", data=mtcars)
# plot multiple models
model_obj<-plot_models(fit1, fit2)
model_obj
#extract features of ggplot object (One possible solution)
q <- ggplot_build(model_obj)
q
###do something to the new q object that changes it
#get the ggplot back
q <- ggplot_gtable(q)
plot(q)
更新:
针对上述示例提出了一个有用的解决方案。
model_obj + scale_x_discrete(limits = c("disp", "drat", "mpg") ) + scale_y_continuous(trans = "log10", limits = c(.001, 10000) ) + labs(color = "Outcomes")
但是,我发现了一种情况,它也不起作用。举个例子,
library(sjPlot)
library(titanic)
titanic_train$Sex_num<-ifelse(titanic_train$Sex=="male", 0, 1)
fit1 <- glm(Survived ~ Embarked + Fare,family="binomial", data=titanic_train)
fit2 <- glm(Sex_num ~ Embarked + Fare,family="binomial", data=titanic_train)
model_obj<-plot_models(fit1, fit2)
model_obj + scale_x_discrete(limits = c("Embarked", "Fare") ) + scale_y_continuous(trans = "log10", limits = c(.001, 10000) ) + labs(color = "Outcomes")
在这种情况下,似乎存在一个小故障,即不再绘制估计值。我猜这与重新排序部分有关,用于估计具有参考类别的单个变量。
从概念上讲,逆向工作可能最容易。
当在绘图 window 上绘制 ggplot 时,实际绘制的是图形对象或“grobs”的集合,它们是简单的几何形状(点、线、多边形和文本)作为由 grid
包定义和渲染。从某种意义上说,ggplot 完全 sub-contracts 出其绘图的实际渲染 grid
。相反,ggplot 的工作是准确计算出哪里需要哪些 grob。因此,您可以将 ggplot 的最终产品视为 grob 的集合。此集合是您在 "ggplot_built"
对象上调用 ggplot_gtable
时获得的 gtable。
一旦实际构建了 grobs 集合,就可以对其进行适当的更改,但这样做相对困难,因为您正在处理相互依赖的几何对象的深度嵌套列表。这样做总是感觉有点“hacky”。如果可以的话,还是尽量在这个阶段之前把剧情搞好吧。
为了创建 gtable,ggplot 需要一个关于如何构建这个 grob 集合的完整蓝图。这个最终蓝图就是 "ggplot_built"
对象。然而,在这个阶段,又要考虑许多相互依赖的结构:它是一个复杂的 ggproto 对象,具有嵌套的数据、属性和函数,并且很容易被破坏。因此,更改 ggplot_built
对象也很困难。
在大多数情况下,我们希望在构建蓝图之前更改地块的规格。规范是实际的“ggplot”对象。如果我们制作一个非常简单的 ggplot 对象并将其存储为变量,则不会绘制任何内容 window,也不会构建 ggplot_built
或 gtable
对象。 ggplot 仍处于规范阶段,可以轻松更改
df <- data.frame(x = 1:10, y = 1:10)
p <- ggplot(df, aes(x = x, y = y)) + geom_point()
只有当我们在对象上隐式或显式调用 plot 时,最终的 specification -> blueprint -> grobs -> drawing
才会发生。
在你的例子中,model_obj
实际上是一个 ggplot 对象,所以你可以很容易地改变它的参数。例如,如果我想将其更改为极坐标 co-ordinates,我可以直接执行 model_obj + coord_polar()
(并收到警告,提示我尝试应用两个 coords
),或者直接覆盖坐标。我会坚持只在此处添加它们。
model_obj$coordinates + coord_polar()
model_obj
同样,如果我想更改色标的图例,我可以添加或替换色标对象。从现在开始,我将覆盖以保持更改。
model_obj$scales$scales[[3]] <- scale_color_manual(values = c("blue", "red"), name = "Outcome")
model_obj
现在,要重新排序 y 轴(实际上是 x 轴,因为 sjplot 对象已翻转 co-ordinates,我可以这样做:
model_obj$scales$scales[[1]] <- scale_x_discrete(limits = c("mpg", "disp", "drat"))
model_obj
最后,我们像这样更改 x 轴范围:
model_obj$scales$scales[[2]] <- scale_y_log10(limits = c(0.0001, 100))
model_obj
我找到了第二种情况的解决方案。我猜这不是 ggplot
故障,实际上是 sjplot
功能。
之所以没有绘制图表,是因为您需要为出现在图表上的变量使用准确的标签。所以你做不到
model_obj + scale_x_discrete(limits = c("Embarked", "Fare") )
因为只有 Fare
符合其中一个标签。见下文
library(sjPlot)
library(titanic)
titanic_train$Sex_num<-ifelse(titanic_train$Sex=="male", 0, 1)
titanic_train$Embarked_test<-ifelse(titanic_train$Embarked=="C", "C", ifelse(titanic_train$Embarked=="Q", "Q", ifelse(titanic_train$Embarked=="S", "S", NA)))
fit1 <- glm(Survived ~ Embarked_test + Fare,family="binomial", data=titanic_train)
fit2 <- glm(Sex_num ~ Embarked_test + Fare,family="binomial", data=titanic_train)
model_obj<-plot_models(fit1, fit2)
model_obj + scale_x_discrete(limits = c("Fare", "Embarked_testS", "Embarked_testQ") ) + scale_y_continuous(trans = "log10", limits = c(.001, 10000) ) + labs(color = "Outcomes")
我正在尝试了解有关 ggplot object 的工作原理的更多信息。我试图在没有初始代码的情况下实现更改 ggplot object 的目标。所以在这个例子中我使用 sjPlot
输出模型 object model_obj
。通过不依赖用于制作情节的初始代码,我希望能够改变情节的 3 件事。我希望能够
- 将图例标题从“因变量”更改为“结果”(我真的不在乎它叫什么它只是一个测试)
- Re-order y 轴上的系数
- 更改 x 轴边界
我知道 plot_models
函数已经允许其中的一些功能,但我正在尝试了解更多有关 ggplot 工作原理的信息。
我在网上找到的一个想法是,人们使用此 ggplot_build
函数从绘图 object 中提取一些特征,然后对其进行更改,然后将其还原为具有 ggplot_gtable
的绘图。但是,我不明白输出的哪些部分对应于上面的 3 个东西。
我也对使用 +
符号向 ggplot object 添加新功能的选项感兴趣
model_obj + ggtitle("New title")
但我不确定这在模型 object 的初始代码未知的情况下是否总是有用。
这是我的代码
require(sjPlot)
data(mtcars)
# fit models
fit1 <- glm(vs ~ mpg + disp + drat,family="binomial", data=mtcars)
fit2 <- glm(am ~ mpg + disp + drat,family="binomial", data=mtcars)
# plot multiple models
model_obj<-plot_models(fit1, fit2)
model_obj
#extract features of ggplot object (One possible solution)
q <- ggplot_build(model_obj)
q
###do something to the new q object that changes it
#get the ggplot back
q <- ggplot_gtable(q)
plot(q)
更新:
针对上述示例提出了一个有用的解决方案。
model_obj + scale_x_discrete(limits = c("disp", "drat", "mpg") ) + scale_y_continuous(trans = "log10", limits = c(.001, 10000) ) + labs(color = "Outcomes")
但是,我发现了一种情况,它也不起作用。举个例子,
library(sjPlot)
library(titanic)
titanic_train$Sex_num<-ifelse(titanic_train$Sex=="male", 0, 1)
fit1 <- glm(Survived ~ Embarked + Fare,family="binomial", data=titanic_train)
fit2 <- glm(Sex_num ~ Embarked + Fare,family="binomial", data=titanic_train)
model_obj<-plot_models(fit1, fit2)
model_obj + scale_x_discrete(limits = c("Embarked", "Fare") ) + scale_y_continuous(trans = "log10", limits = c(.001, 10000) ) + labs(color = "Outcomes")
在这种情况下,似乎存在一个小故障,即不再绘制估计值。我猜这与重新排序部分有关,用于估计具有参考类别的单个变量。
从概念上讲,逆向工作可能最容易。
当在绘图 window 上绘制 ggplot 时,实际绘制的是图形对象或“grobs”的集合,它们是简单的几何形状(点、线、多边形和文本)作为由 grid
包定义和渲染。从某种意义上说,ggplot 完全 sub-contracts 出其绘图的实际渲染 grid
。相反,ggplot 的工作是准确计算出哪里需要哪些 grob。因此,您可以将 ggplot 的最终产品视为 grob 的集合。此集合是您在 "ggplot_built"
对象上调用 ggplot_gtable
时获得的 gtable。
一旦实际构建了 grobs 集合,就可以对其进行适当的更改,但这样做相对困难,因为您正在处理相互依赖的几何对象的深度嵌套列表。这样做总是感觉有点“hacky”。如果可以的话,还是尽量在这个阶段之前把剧情搞好吧。
为了创建 gtable,ggplot 需要一个关于如何构建这个 grob 集合的完整蓝图。这个最终蓝图就是 "ggplot_built"
对象。然而,在这个阶段,又要考虑许多相互依赖的结构:它是一个复杂的 ggproto 对象,具有嵌套的数据、属性和函数,并且很容易被破坏。因此,更改 ggplot_built
对象也很困难。
在大多数情况下,我们希望在构建蓝图之前更改地块的规格。规范是实际的“ggplot”对象。如果我们制作一个非常简单的 ggplot 对象并将其存储为变量,则不会绘制任何内容 window,也不会构建 ggplot_built
或 gtable
对象。 ggplot 仍处于规范阶段,可以轻松更改
df <- data.frame(x = 1:10, y = 1:10)
p <- ggplot(df, aes(x = x, y = y)) + geom_point()
只有当我们在对象上隐式或显式调用 plot 时,最终的 specification -> blueprint -> grobs -> drawing
才会发生。
在你的例子中,model_obj
实际上是一个 ggplot 对象,所以你可以很容易地改变它的参数。例如,如果我想将其更改为极坐标 co-ordinates,我可以直接执行 model_obj + coord_polar()
(并收到警告,提示我尝试应用两个 coords
),或者直接覆盖坐标。我会坚持只在此处添加它们。
model_obj$coordinates + coord_polar()
model_obj
同样,如果我想更改色标的图例,我可以添加或替换色标对象。从现在开始,我将覆盖以保持更改。
model_obj$scales$scales[[3]] <- scale_color_manual(values = c("blue", "red"), name = "Outcome")
model_obj
现在,要重新排序 y 轴(实际上是 x 轴,因为 sjplot 对象已翻转 co-ordinates,我可以这样做:
model_obj$scales$scales[[1]] <- scale_x_discrete(limits = c("mpg", "disp", "drat"))
model_obj
最后,我们像这样更改 x 轴范围:
model_obj$scales$scales[[2]] <- scale_y_log10(limits = c(0.0001, 100))
model_obj
我找到了第二种情况的解决方案。我猜这不是 ggplot
故障,实际上是 sjplot
功能。
之所以没有绘制图表,是因为您需要为出现在图表上的变量使用准确的标签。所以你做不到
model_obj + scale_x_discrete(limits = c("Embarked", "Fare") )
因为只有 Fare
符合其中一个标签。见下文
library(sjPlot)
library(titanic)
titanic_train$Sex_num<-ifelse(titanic_train$Sex=="male", 0, 1)
titanic_train$Embarked_test<-ifelse(titanic_train$Embarked=="C", "C", ifelse(titanic_train$Embarked=="Q", "Q", ifelse(titanic_train$Embarked=="S", "S", NA)))
fit1 <- glm(Survived ~ Embarked_test + Fare,family="binomial", data=titanic_train)
fit2 <- glm(Sex_num ~ Embarked_test + Fare,family="binomial", data=titanic_train)
model_obj<-plot_models(fit1, fit2)
model_obj + scale_x_discrete(limits = c("Fare", "Embarked_testS", "Embarked_testQ") ) + scale_y_continuous(trans = "log10", limits = c(.001, 10000) ) + labs(color = "Outcomes")