Plot of BinaryTree (ctree, party) ignores plot option of par() - r

I would like to plot the BinaryTree in the uppper part of the plot, and make a second one in the second part (bottom). Here is some example code to show, that the plot of the tree completely ignores the partitioning options set by par()
library("party")
### regression
airct <- ctree(Ozone ~ ., data = subset(airquality, !is.na(Ozone)))
### classification
irisct <- ctree(Species ~ .,data = iris)
par(mfrow = c(2, 1))
plot(airct)
plot(irisct)
This code does not plot the two trees in the same plot (page). How can I correct this?
Even when following the very detailed answer does not work in this case: plots generated by 'plot' and 'ggplot' side-by-side
the plotting of a ctree ignores all established options.

The plots in party and its successor package partykit are implemented in grid and hence the base graphics options from par() such as mfrow do not work. In addition to the remarks from the comments, you can use grid.layout() to achieve similar results.
Doing so in plain grid is a bit technical but the code should not be too hard to follow:
grid.newpage()
pushViewport(viewport(layout = grid.layout(2, 1)))
pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 1))
plot(airct, newpage = FALSE)
popViewport()
pushViewport(viewport(layout.pos.row = 2, layout.pos.col = 1))
plot(irisct, newpage = FALSE)
popViewport()
The reason for the newpage = FALSE argument is that by default the plot is drawn on a new page, rather than adding to a potentially existing plot.

Related

How to plot the restults of ctree in grid?

The results of the plot can be normally arranged in grids. I currently have an issue by plotting the results of the ctree function from the party package in a grid. This question is a duplicate of a question from 6 years and 8 months ago (Plot of BinaryTree (ctree, party) ignores plot option of par()). It was opted that gridExtra could provide a solution. However, till now no solution for this issue has been given. Consider the example below.
library(party)
library(gridExtra)
#Create random dataframe
dfA <- data.frame(x=c(rnorm(50, 5), rnorm(50, 2)),
y=c(rbinom(50, 1, .9), rbinom(50, 1, .1)))
#Duplicate dataframe
dfB <- dfA
#Plot in base R wit par (does not work)
par(mfrow = c(1, 2))
plot(party::ctree(y~x, data=dfA))
plot(party::ctree(y~x, data=dfB))
#Try to organize in a grid wit gridExtra (does not work)
treeA <- party::ctree(y~x, data=dfA)
treeB <- party::ctree(y~x, data=dfB)
grobA <- arrangeGrob(plot(treeA))
grobB <- arrangeGrob(plot(treeB))
grid.arrange(grobA, grobB, ncol=2)
Error in gList(list(wrapvp = list(x = 0.5, y = 0.5, width = 1, height = 1, :
only 'grobs' allowed in "gList"
The arrangeGrob(plot(treeA)) and arrangeGrob(plot(treeB)) also return an error stating Error in vapply(x$grobs, as.character, character(1)) : values must be length 1, but FUN(X[[1]]) result is length 0
Does someone known how plot the results of the ctree function in a grid?
Thank you in advance.
## grab the scene as a grid object
library(gridExtra)
library(gridGraphics)
library(grid)
list.to.pass <- list('plot(ctree(y~x, data=dfA))',
'plot(ctree(y~x, data=dfB))')
out<-list()
for (i in c(1,2)){
print(i)
formula(list.to.pass[[i]])
out[[i]] <- grid.grab()
print(out[[i]])
dev.off()
}
grid.arrange(out[[1]], out[[2]], nrow = 1,ncol=2)
You will get:
The plots in party and its successor package partykit are implemented in grid and hence the base graphics options from par() such as mfrow do not work. You can use grid.layout() to achieve similar results. Doing so in plain grid is a bit technical but the code should not be too hard to follow:
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 1))
plot(treeA, newpage = FALSE)
popViewport()
pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 2))
plot(treeB, newpage = FALSE)
popViewport()
The reason for the newpage = FALSE argument is that by default the plot is drawn on a new page, rather than adding to a potentially existing plot.

R: Survminer double graph

I am involved in a project where we are plotting survival curves for an event with a pretty low incidence, and the Kaplan-Meier curves (plotted using survminer) are pretty flat. I do not want to simply zoom in on the Y-axis as I think the incidence rates may then be misinterpreted by the reader. One way to show both the 'true' rate and zoom in on eventual small differences is to do it as NEJM does it:
https://www.nejm.org/na101/home/literatum/publisher/mms/journals/content/nejm/2011/nejm_2011.364.issue-9/nejmoa1007432/production/images/img_large/nejmoa1007432_f1.jpeg.
I have, however, not found a way to do this directly in survminer. For reproducibility's sake, I would like to avoid involving any Adobe software.
Does anyone know a way to get a small, zoomed in version included on top of the original graph? I would like to accomplish this with survminer but tips on any other good ggplot-based KM packages are appreciated.
Small example:
library(survival)
library(survminer)
df <- genfan
df$treat<-sample(c(0,1),nrow(df),replace=TRUE)
fit <- survfit(Surv(hours, status) ~ treat, data = df)
p <- ggsurvplot(fit, data = df, risk.table = TRUE, fun = 'event', ylim = c(0, 1))
p # Normal flat, singular graph
There are a few ways to do this but one suggestion is too make the two plots you have and arrange them with grid.arrange. First make the two plots. Then pull out the risk table and plot separately for the first plot (you cannot put a ggsurvplot object in a grid.arrange). Nest the second plot in plot one with a annotation_custom. Finally, use layout_matrix to specify the dimensions of your plot and put it back together with grid.arrange.
library(survival)
library(survminer)
library(grid)
library(gridExtra)
df <- genfan
df$treat<-sample(c(0,1),nrow(df),replace=TRUE)
fit <- survfit(Surv(hours, status) ~ treat, data = df)
p <- ggsurvplot(fit, data = df, risk.table = TRUE, fun = 'event', ylim = c(0, 1))
#zoomed plot and remove risk table
g <- ggsurvplot(fit, data = df, risk.table = FALSE, fun = 'event', ylim = c(0, .5))
risktab <- p$table
justplot <- p$plot
p2 <- justplot +
annotation_custom(grob = ggplotGrob(g$plot+
theme(legend.position = "none")),
xmin = 60,xmax=Inf,ymin = .5,ymax = Inf)
lay <- rbind(c(1,1),
c(1,1),
c(2,2))
gridExtra::grid.arrange(p2, risktab,
#use layout matrix to set sizes
layout_matrix=lay
)

Add barplots to circles in inner nodes in partykit plotted trees

The partykit package plots barplots at the terminal nodes of trees which gives a visual rendition of the posterior probabilities of the dependent variable classes.
I would like to add those barplots also in the inner nodes, below the standard circles/ellipses. This needs to use a function that is a mixture of node_inner() and node_barplot() to the inner_panel argument of the plot() method.
But those function have pretty complex internals and I'm not sure how to mix the two in order to have to inner plots stacked vertically.
Any ideas?
It's possible, it just doesn't look very appealing. If you want to show the name of the splitting variable and the p-value, it would be better to tweak the mainlab argument of node_barplot. In the answer to
Ctree classification with weights - results displayed there is in illustration how to include weights in the title - in a similar fashion you could display splitting variable and p-value.
If you are determined to set up a new panel function that has two subpanels, you need a little bit of grid programming (the graphics system that the plot() method is based on). You need to set up a grid.layout and then go through the resulting viewports.
make_inner_and_barplot <- function(object, ...) {
function(node) {
## layout
pushViewport(viewport(layout = grid.layout(nrow = 2, ncol = 1,
heights = unit(c(0.2, 0.8), "npc"))))
## background color
grid.rect(gp = gpar(fill = "white", col = 0))
## circle
pushViewport(viewport(layout.pos.col = 1, layout.pos.row = 1))
node_inner(object)(node)
popViewport()
## circle
pushViewport(viewport(layout.pos.col = 1, layout.pos.row = 2))
node_barplot(object, id = FALSE, ...)(node)
popViewport(2)
}
}
With the resulting panel function you can then do:
ct <- ctree(factor(cyl) ~ ., data = mtcars, minsplit = 2)
plot(ct, inner_panel = make_inner_and_barplot(ct), tnex = 0.8)

How to manually set the scale for multiple boxplots in lattice bwplot?

I've got the results of clustering and decided to make a boxplot for each cluster, using lattice.
Next, I was faced with the need to establish a scale, acceptable to all cluster boxplots.
Found a solution, which allows to exclude an outliers and set free relation.
library(lattice)
trellis.device(new=FALSE, col=FALSE)
bwplot(value ~ variable | Cluster, data = test,
layout = c(2,2),
prepanel = function(x, y) {
bp <- boxplot(split(y, x), plot = FALSE)
ylim <- range(bp$stats)
list(ylim = ylim) },
scales = list(y = list(relation = "free")),
do.out = F)
So, I've got pretty good plots, but it can be better, if I manually set the ylim for each plot. Eg there is only integer values in my data and the value 0.5 at upper left cluster graph is meaningless.
So, is there any way to set multiple ylims in bwplot parameters?
From the documentation under ?bwplot:
xlim could also be a list, with as many components as the number of panels (recycled if necessary), with each component as described above. This is meaningful only when scales$x$relation is "free", in which case these are treated as if they were the corresponding limit components returned by prepanel calculations.
The ylim argument has the corresponding functionality for the y-axis.
So, set relation = "free" in the scales argument as you did, and then pass a list to the ylim argument to individually set the y-axis limits for each panel:
bwplot(len ~ factor(dose) | supp, data = ToothGrowth,
scales = list(relation = "free"),
ylim = list(c(5, 31), c(0, 36)))

How to plot grid plots on a same page?

I am using a package (treemap) that uses grid package to produce a treemap. However, I would like to plot several of these treemaps together, to add different color schemes to these plots. tmPlot function uses grid.newpage function, which clears the graphics window. I have not found a way to save grid.newpage objects as you can do for ggplot2objects. Is there a way to plot several grid.newpage objects to a same window?
## Example
library(treemap)
# load Gross national income data
data(GNI2010)
size <- aggregate(GNI ~ continent, GNI2010, sum)
size <- size[with(size, order(GNI, decreasing = T)),]
cont <- size$continent
widths <- c(sum(size[c(1,3,5),]$GNI),
sum(size$GNI) - sum(size[c(1,3,5),]$GNI))
heights <- c(sum(size[c(1,2),]$GNI),
sum(size[c(3,4),]$GNI),
sum(size[c(5,6),]$GNI))
palettes <- c("Greens", "Blues", "Reds", "Oranges", "Purples", "Greys")
i <- 1 # This is to be replaced by for loop
x <- subset(GNI2010, continent == cont[i], cex = 5)
# create treemap
layout(matrix(1:6, 3, byrow = TRUE), widths = widths, heights = heights)
x1 <- tmPlot(x,
index=c("iso3"),
vSize="population",
vColor="GNI",
type="value", title = "",
position.legend = "none",
palette = palettes[i])
grid.text(cont[i], 0.5, 0.5, gp=gpar(fontsize=20, font = 2, col = "white"))
## x1 is does not make a plot as such and tmPlot overwrites layout
I understand that my solution to scale the plots based on GNI sum is not right. I might make another question about that later, once I figure out how to plot these treemaps in a same window.
EDIT: I think the answer to this question is "no". Currently you cannot save grid.newpage objects by name, neither can you save several of these on a page, because the function "erases the current device or moves to a new page" as said in the description. However, it is possible to find work arounds. tmPlot package does not currently (as of 23 March, 2013) support viewports, but the development version does.
Thanks for your question. The output of tmPlot is indeed not a saved plot.
In the next update I will add argument vp, by which a viewport can be specified to draw in. Only if it is not specified, grid.newpage is called.
UPDATE: You could check and test the development version at https://github.com/mtennekes/treemap
To apply the example of Bryan Hanson:
vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y)
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
tmPlot(GNI2010,
index="continent",
vSize="population",
vColor="GNI",
type="value",
vp = vplayout(1,1))
tmPlot(GNI2010,
index=c("continent", "iso3"),
vSize="population",
vColor="GNI",
type="value",
vp = vplayout(1,2))
Here's an approach that is very flexible for any grid graphics:
vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y)
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
print(a, vp = vplayout(1,1))
print(b, vp = vplayout(1,2))
Where a and b are your saved plot objects. So test each plot individually ahead of time, save them as a, b, ... then plot them as above.
Oh, and if tmPlot always does grid.newpage then check to see if it has a has new.page argument which you can set to FALSE, or make a copy of the function and comment out the newpage.

Resources