add second axis label to facetted plot - r

How does one add a second axis label to facetted plots?
I realize in most cases they should be the same, but I have a top row with skill metrics for different models while the bottom row is the difference in skill so I'd like to include bquote(Delta*.(costlong)) below the existing axis label, and to the left of the 2nd row of facets.
I've tried
`labs(y=bquote(Delta*.(costlong)*" "*.(costlong)*" "))+`
but its impossible to center well and it moves when exporting.
I've also played with
+annotation_custom(textGrob('bquote(Delta*.(costlong))),xmin=-20,xmax=-10,ymin=0,ymax=.2)
but it doesn't show up. I am limiting my x axis from 0 to 160.
here is some data and plotting code:
costlong=bquote(r^2)
skill.m=data.frame(doy=seq(1,160),yr=2000:2001,variable=factor(c('skill_phvfull','skill_phvrcnfull','fulldiff'),levels=c('skill_phvfull','skill_phvrcnfull','fulldiff')),value=runif(2*3*160,0,.6))
skill.m$set=ifelse(grepl('skill',as.character(skill.m$variable)),'data','diff')
skill.m$set=as.factor(skill.m$set)
skill.m[grepl('diff',as.character(skill.m$variable)),'value']=skill.m[grepl('diff',as.character(skill.m$variable)),'value']/3
ggplot(skill.m)+
geom_point(aes(x=as.numeric(doy),y=value,colour=variable),alpha=.75,size=1)+
facet_grid(set~yr,scale='free',space='free')+
labs(y=costlong)+
scale_colour_brewer(name='',palette='Set1',labels=c('PHV (blended)','PHV+RCN (blended)','Blended Skill Difference'))+
scale_x_continuous('day of year',limits=c(1,160),labels=c(1,seq(30,160,30)),breaks=c(1,seq(30,160,30)))+
scale_y_continuous(breaks=seq(0,.7,.1),labels=seq(0,.7,.1))+
guides(colour=guide_legend(override.aes=list(alpha=1,size=2)))+
theme_bw()+
theme(axis.line=element_line(colour='grey10'),
strip.background=element_rect(fill='white',colour='white'),
strip.text=element_text(face='bold',size='12'),
axis.text.x=element_text(size=8,angle=90,hjust=1,vjust=.5),
axis.text.y=element_text(size=8,angle=0),
legend.key=element_rect(colour='white'),
legend.position = "bottom",
legend.box = "horizontal")
any suggestions?

One way is to use gtable functions. Draw the ggplot with a blank y label. Then convert the ggplot to a grob, construct two text grobs for the two labels (the upper component and the lower component), then insert the text grobs into the layout.
library(ggplot2)
library(gtable)
library(grid)
costlong=bquote(r^2)
skill.m=data.frame(doy=seq(1,160),yr=2000:2001,variable=factor(c('skill_phvfull','skill_phvrcnfull','fulldiff'),levels=c('skill_phvfull','skill_phvrcnfull','fulldiff')),value=runif(2*3*160,0,.6))
skill.m$set=ifelse(grepl('skill',as.character(skill.m$variable)),'data','diff')
skill.m$set=as.factor(skill.m$set)
skill.m[grepl('diff',as.character(skill.m$variable)),'value']=skill.m[grepl('diff',as.character(skill.m$variable)),'value']/3
p = ggplot(skill.m)+
geom_point(aes(x=as.numeric(doy),y=value,colour=variable),alpha=.75,size=1)+
facet_grid(set~yr,scale='free',space='free')+
labs(y="")+
scale_colour_brewer(name='',palette='Set1',labels=c('PHV (blended)','PHV+RCN (blended)','Blended Skill Difference'))+
scale_x_continuous('day of year',limits=c(1,160),labels=c(1,seq(30,160,30)),breaks=c(1,seq(30,160,30)))+
scale_y_continuous(breaks=seq(0,.7,.1),labels=seq(0,.7,.1))+
guides(colour=guide_legend(override.aes=list(alpha=1,size=2)))+
theme_bw()+
theme(axis.line=element_line(colour='grey10'),
strip.background=element_rect(fill=NA,colour=NA),
strip.text=element_text(face='bold',size='12'),
axis.text.x=element_text(size=8,angle=90,hjust=1,vjust=.5),
axis.text.y=element_text(size=8,angle=0),
legend.key=element_rect(colour='white'),
legend.position = "bottom",
legend.box = "horizontal")
# Convert the plot to a grob
gt = ggplotGrob(p)
# Check the layout
gtable_show_layout(gt) # To manually find the row and column for the labels
# Construct text grobs - one for each label
labL = textGrob(expression(bold(Delta * r^2)), rot = 90,
gp = gpar(fontsize = 12))
labU = textGrob(expression(bold(r^2)), rot = 90,
gp = gpar(fontsize = 12))
# Insert the text grobs into the layout
gt <- gtable_add_grob(gt, labL, t = 9, l = 2)
gt <- gtable_add_grob(gt, labU, t = 7, l = 2)
# Make the column a little wider
gt$widths[2] = unit(2, "lines")
# Draw it
grid.newpage()
grid.draw(gt)

Related

How to arrange `ggplot2` objects side-by-side and ensure equal plotting areas?

I am trying to arrange two ggplot2 plots side by side, i.e., in a two-column
layout using the package gridExtra. I am interested in ensuring that both
plots have equal plotting area (i.e., the gray plot panel is the same for both
plots) regardless of the height of the x-axis labels. As you can see in the
example below, when longer x-axis labels are used, gridExtra::grid.arrange()
seems to compensate this by adjusting the plotting area (i.e., the grayed out
part of the plot).
# Dummy data.
data <- data.frame(x = 1:10, y = rnorm(10))
# Dummy labels.
x_labels_short <- 1:10
x_labels_long <- 100001:100010
# Common settings for both `ggplot2` plots.
layers <- list(
labs(
x = "Plot title"
),
theme(
axis.text.x = element_text(
angle = 90,
vjust = 0.5,
hjust = 1
)
)
)
# `ggplot2 plot (a).
plot_a <- ggplot(data, aes(x, y)) +
scale_x_continuous(breaks = 1:10, labels = x_labels_short) +
layers
# `ggplo2` plot (b).
plot_b <- ggplot(data, aes(x, y)) +
scale_x_continuous(breaks = 1:10, labels = x_labels_long) +
layers
# Showing the plots side by side.
gridExtra::grid.arrange(
plot_a,
plot_b,
ncol = 2
)
Output:
What I want is for both plots to (1) have equal plotting area and (b) the x-axis
title of plot_a to be aligned with that of plot_b (i.e., the x-axis title of
plot_a to be offset based on the length of of the x-axis labels of plot_b).
If this is not clear, this is what I want to achieve would look like with base
R.
# Wrapper for convenience.
plot_gen <- function(data, labels) {
plot(
NULL,
xlim = c(1, 10),
ylim = c(min(data$y), max(data$y)),
xlab = "",
ylab = "y",
xaxt = "n"
)
axis(
side = 1,
at = 1:10,
labels = labels,
las = 2
)
title(
xlab = "Plot title",
line = 4.5
)
}
# Get `par`.
old_par = par(no.readonly = TRUE)
# Set the two-column layout.
layout(matrix(1:2, ncol = 2))
# Adjust margins.
par(mar = old_par$mar + c(1.5, 0, 0, 0))
# Plot base plot one.
plot_gen(data, x_labels_short)
# Plot base plot two.
plot_gen(data, x_labels_long)
# Restore `par`.
par(old_par)
# Restore layout.
layout(1:1)
Output:
Quick mention. I found a similar question on SO (i.e.,
How to specify the size of a graph in ggplot2 independent of axis labels), however I fail to see how the
answers address the problem. Also, the plots I am trying to arrange are based
on different data and I don't think I can use a facet_wrap approach.
One suggestion: the patchwork package.
library(patchwork)
plot_a + plot_b
It also works for more complex layouts, e.g.:
(plot_a | plot_b) / plot_a

Add axes to grid of ggplots

I have a grid composed of several ggplots and want to add an x axis, where axis ticks and annotations are added between the plots. I could not came up with a better solution than to create a custom plot for the axis and adding it below with arrangeGrob. But they do not align with the plots (I draw arrows where the numbers should be). Also there is a large white space below which I don't want.
I will also need an analogue for the y-axis.
library(ggplot2)
library(gridExtra)
library(ggpubr)
library(grid)
# Create a grid with several ggplots
p <-
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
theme_transparent() +
theme(plot.background = element_rect(color = "black"))
main.plot <- arrangeGrob(p, p, p, p, p, p, p, p, ncol = 4, nrow = 2)
# grid.draw(main.plot)
# Now add an x axis to the main plot
x.breaks <- c(0, 1, 2.5, 8, 10)
p.axis <- ggplot() +
ylim(-0.1, 0) +
xlim(1, length(x.breaks)) +
ggpubr::theme_transparent()
for (i in seq_along(x.breaks)) {
p.axis <- p.axis +
geom_text(aes_(x = i, y = -0.01, label = as.character(x.breaks[i])), color = "red")
}
# p.axis
final.plot <- arrangeGrob(main.plot, p.axis, nrow = 2)
grid.draw(final.plot)
Any help appreciated.
Note: In the code below, I assume each plot in your grid has equal width / height, & used equally spaced label positions. If that's not the case, you'll have to adjust the positions yourself.
Adding x-axis to main.plot:
library(gtable)
# create additional row below main plot
# height may vary, depending on your actual plot dimensions
main.plot.x <- gtable_add_rows(main.plot, heights = unit(20, "points"))
# optional: check results to verify position of the new row
dev.off(); gtable_show_layout(main.plot.x)
# create x-axis labels as a text grob
x.axis.grob <- textGrob(label = x.breaks,
x = unit(seq(0, 1, length.out = length(x.breaks)), "npc"),
y = unit(0.75, "npc"),
just = "top")
# insert text grob
main.plot.x <- gtable_add_grob(main.plot.x,
x.axis.grob,
t = nrow(main.plot.x),
l = 1,
r = ncol(main.plot.x),
clip = "off")
# check results
dev.off(); grid.draw(main.plot.x)
You can do the same for the y-axis:
# create additional col
main.plot.xy <- gtable_add_cols(main.plot.x, widths = unit(20, "points"), pos = 0)
# create y-axis labels as a text grob
y.breaks <- c("a", "b", "c") # placeholder, since this wasn't specified in the question
y.axis.grob <- textGrob(label = y.breaks,
x = unit(0.75, "npc"),
y = unit(seq(0, 1, length.out = length(y.breaks)), "npc"),
just = "right")
# add text grob into main plot's gtable
main.plot.xy <- gtable_add_grob(main.plot.xy,
y.axis.grob,
t = 1,
l = 1,
b = nrow(main.plot.xy) - 1,
clip = "off")
# check results
dev.off(); grid.draw(main.plot.xy)
(Note that the above order of x-axis followed by y-axis should not be switched blindly. If you are adding rows / columns, it's good habit to use gtable_show_layout() frequently to check the latest gtable object dimensions, & ensure that you are inserting new grobs into the right cells.)
Finally, let's add some buffer on all sides, so that the labels & plot borders don't get cut off:
final.plot <- gtable_add_padding(main.plot.xy,
padding = unit(20, "points"))
dev.off(); grid.draw(final.plot)

How to control vertical spaces in Facet_wrap in ggplot

I have created a facet chart across two dimensions Rating and Geography (Geo_class). how does one introduce spaces between the different Geography classes (panel.spacing.x), and yet avoid introducing the space between Rating classes. Sample data here https://www.dropbox.com/s/n3tbiexbvpuqm3t/Final_impact_melt_All5.csv?dl=0
in the image below, 1 to 3, 4,5,6,7 represent Ratings, Geo_Class is (Saudi Arabia, NOn GCC, Other GCC and All). Method is New or Old.
Im using the following code to generate the plot
p<-ggplot(Final_impact_melt_All5, aes(x=Method, y=Capital_Charge, fill= Capital_Charge_type))+ geom_bar(stat='Identity', width= 1)
p + facet_wrap (Geo_class ~ Ratings, nrow = 2) + scale_fill_brewer(palette ="Oranges") + theme(axis.text=element_text(size=6),panel.spacing.x=unit(0, "lines"),panel.spacing.y=unit(1, "lines"))
what id like is as separate the chart into 4 panels (one each for Geo_class ie. Saudi Arabia, Other GCC, Non GCC and All). Id like to keep spacing between the ratings to zero so that this takes on the look of a cluster stacked bar chart.
Another bonus would be if i can get rid of multiple times the Geography class is repeated and it just show up once atop each of the 4 new panels.
Is this what you want? As far as I know, what you ask for cannot be accomplished using ggplot only. The code below is not pretty. It depends of gtable and grid functions. It decomposes the strips in the ggplot plot, then constructs the new strips with appropriate spacing. The code works for the particular configuration of inner and outer strip labels in the example. If that changes, the code will break. And it might not survive the next version of ggplot.
library(ggplot2)
library(grid)
library(gtable)
# Set spacing between Geo_class and between Ratings
OuterSpacing = unit(5, "pt") # spacing between Geo_class
InnerSpacing = unit(0, "pt") # spacing between Ratings
# Your ggplot
p <- ggplot(Final_impact_melt_All5,
aes(x=Method, y=Capital_Charge, fill= Capital_Charge_type)) +
geom_bar(stat='Identity', width= 1)
plot = p +
facet_wrap (Geo_class ~ Ratings, nrow = 2) +
scale_fill_brewer(palette ="Oranges") +
theme(axis.text=element_text(size=6),
panel.spacing.x = OuterSpacing,
panel.spacing.y = unit(1, "lines"))
# Get the ggplot grob
g = ggplotGrob(plot)
# Set spacing between 'Ratings' to 'InnerSpacing'
g$widths[c(seq(6, by=4, length.out=4), seq(26, by=4, length.out=4)) ] = InnerSpacing
# Get a list of strips
strip = lapply(grep("strip-t", g$layout$name), function(x) {g$grobs[[x]]})
# Number of strips
URow = 4; LRow = 5 # Top row and Bottom row
# Construct gtable to contain the new strip
Inner = (rep(unit.c(unit(1, "null"), InnerSpacing), LRow))[-10]
newStrip = gtable(widths = (rep(unit.c(Inner, OuterSpacing), URow))[-40],
heights = strip[[1]]$heights)
## Populate the gtable
# Top Row
cols1 = seq(1, by = 5, length.out = 4)
cols2 = (seq(1, by = 10, length.out = 4))
newStrip = gtable_add_grob(newStrip, lapply(strip[cols1], `[`, 1), t = 1, l = cols2, r = cols2 + 8)
# Bottom row
cols = seq(1, by = 2, length.out = 20)
newStrip = gtable_add_grob(newStrip, lapply(strip, `[`, 2), t = 2, l = cols)
## Add the strips to the plot,
# making sure the second half go in the upper section (t=6)
# and the first half go in the lower section (t=11)
pgNew = gtable_add_grob(g, newStrip[1:2, 21:39], t = 6, l = 4, r = 40)
pgNew = gtable_add_grob(pgNew, newStrip[1:2, 1:19], t = 11, l = 4, r = 40)
# Remove the original strip
for(i in 102:121) pgNew$grobs[[i]] = nullGrob()
# Draw the plot
grid.newpage()
grid.draw(pgNew)

Manipulating axis titles in ggpairs (GGally)

I'm using the code below to generate the following chart.
# Setup
data(airquality)
# Device start
png(filename = "example.png", units = "cm", width = 20, height = 14, res = 300)
# Define chart
pairs.chrt <- ggpairs(airquality,
lower = list(continuous = "smooth"),
diag = list(continuous = "blank"),
upper = list(continuous = "blank")) +
theme(legend.position = "none",
panel.grid.major = element_blank(),
axis.ticks = element_blank(),
axis.title.x = element_text(angle = 180, vjust = 1, color = "black"),
panel.border = element_rect(fill = NA))
# Device off and print
print(pairs.chrt)
dev.off()
I'm currently trying to modify the display of the axis titles. In particular, I would like for the axis titles to be:
Placed at a further distance from axis labels
Placed at an angle
As an example, I would like to obtain axis titles similar to the ones pictured below (I'm interested in axis labels only, not in rest of the chart):
Taken from : Geovisualist
I' tried adjusting my syntax changing the axis.title.x to different values but it does not yield the desired results. For instance running the code with angle = 45.
axis.title.x = element_text(angle = 45, vjust = 1, color = "black"),
panel.border = element_rect(fill = NA))
returns the same chart. I was able to control the axis labels by changing the axis.text.x for instance but I can't find the answer how to control the axis titles in this plot. Any help will be much appreciated.
Short answer: There doesn't seem to be an elegant or easy way to do it, but here's a workaround.
I dug into the ggpairs source code (in the GGally package source available from CRAN) to see how the variable labels are actually drawn. The relevant function in ggpairs.R is print.ggpairs. It turns out the variable labels aren't part of the ggplot objects in each cell of the plot matrix -- i.e. they're not axis titles, which is why they aren't affected by using theme(axis.title.x = element_text(angle = 45) or similar.
Rather, they seem to be drawn as text annotations using grid.text (in package 'grid'). grid.text takes arguments including x, y, hjust, vjust, rot (where rot is angle of rotation), as well as font size, font family, etc. using gpar (see ?grid.text), but it looks like there is currently no way to pass in different values of those parameters to print.ggpairs -- they're fixed at default values.
You can work around it by leaving your variable labels blank to begin with, and then adding them on later with customized placement, rotation, and styling, using a modification of the relevant part of the print.ggpairs code. I came up with the following modification. (Incidentally, because the original GGally source code was released under a GPL-3 license, so is this modification.)
customize.labels <- function(
plotObj,
varLabels = NULL, #vector of variable labels
titleLabel = NULL, #string for title
leftWidthProportion = 0.2, #if you changed these from default...
bottomHeightProportion = 0.1, #when calling print(plotObj),...
spacingProportion = 0.03, #then change them the same way here so labels will line up with plot matrix.
left.opts = NULL, #see pattern in left.opts.default
bottom.opts = NULL, #see pattern in bottom.opts.default
title.opts = NULL) { #see pattern in title.opts.default
require('grid')
vplayout <- function(x, y) {
viewport(layout.pos.row = x, layout.pos.col = y)
}
numCol <- length(plotObj$columns)
if (is.null(varLabels)) {
varLabels <- colnames(plotObj$data)
#default to using the column names of the data
} else if (length(varLabels) != numCol){
stop('Length of varLabels must be equal to the number of columns')
}
#set defaults for left margin label style
left.opts.default <- list(x=0,
y=0.5,
rot=90,
just=c('centre', 'centre'), #first gives horizontal justification, second gives vertical
gp=list(fontsize=get.gpar('fontsize')))
#set defaults for bottom margin label style
bottom.opts.default <- list(x=0,
y=0.5,
rot=0,
just=c('centre', 'centre'),#first gives horizontal justification, second gives vertical
gp=list(fontsize=get.gpar('fontsize')))
#set defaults for title text style
title.opts.default <- list(x = 0.5,
y = 1,
just = c(.5,1),
gp=list(fontsize=15))
#if opts not provided, go with defaults
if (is.null(left.opts)) {
left.opts <- left.opts.default
} else{
not.given <- names(left.opts.default)[!names(left.opts.default) %in%
names(left.opts)]
if (length(not.given)>0){
left.opts[not.given] <- left.opts.default[not.given]
}
}
if (is.null(bottom.opts)) {
bottom.opts <- bottom.opts.default
} else{
not.given <- names(bottom.opts.default)[!names(bottom.opts.default) %in%
names(bottom.opts)]
if (length(not.given)>0){
bottom.opts[not.given] <- bottom.opts.default[not.given]
}
}
if (is.null(title.opts)) {
title.opts <- title.opts.default
} else{
not.given <- names(title.opts.default)[!names(title.opts.default) %in%
names(title.opts)]
if (length(not.given)>0){
title.opts[not.given] <- title.opts.default[not.given]
}
}
showLabels <- TRUE
viewPortWidths <- c(leftWidthProportion,
1,
rep(c(spacingProportion,1),
numCol - 1))
viewPortHeights <- c(rep(c(1,
spacingProportion),
numCol - 1),
1,
bottomHeightProportion)
viewPortCount <- length(viewPortWidths)
if(!is.null(titleLabel)){
pushViewport(viewport(height = unit(1,"npc") - unit(.4,"lines")))
do.call('grid.text', c(title.opts[names(title.opts)!='gp'],
list(label=titleLabel,
gp=do.call('gpar',
title.opts[['gp']]))))
popViewport()
}
# viewport for Left Names
pushViewport(viewport(width=unit(1, "npc") - unit(2,"lines"),
height=unit(1, "npc") - unit(3, "lines")))
## new for axis spacingProportion
pushViewport(viewport(layout = grid.layout(
viewPortCount, viewPortCount,
widths = viewPortWidths, heights = viewPortHeights
)))
# Left Side
for(i in 1:numCol){
do.call('grid.text',
c(left.opts[names(left.opts)!='gp'],
list(label=varLabels[i],
vp = vplayout(as.numeric(i) * 2 - 1 ,1),
gp=do.call('gpar',
left.opts[['gp']]))))
}
popViewport()# layout
popViewport()# spacing
# viewport for Bottom Names
pushViewport(viewport(width=unit(1, "npc") - unit(3,"lines"),
height=unit(1, "npc") - unit(2, "lines")))
## new for axis spacing
pushViewport(viewport(layout = grid.layout(
viewPortCount, viewPortCount,
widths = viewPortWidths, heights = viewPortHeights)))
# Bottom Side
for(i in 1:numCol){
do.call('grid.text',
c(bottom.opts[names(bottom.opts)!='gp'],
list(label=varLabels[i],
vp = vplayout(2*numCol, 2*i),
gp=do.call('gpar',
bottom.opts[['gp']]))))
}
popViewport() #layout
popViewport() #spacing
}
And here's an example of calling that function:
require('data.table')
require('GGally')
require('grid')
fake.data <- data.table(test.1=rnorm(50), #make some fake data for demonstration
test.2=rnorm(50),
test.3=rnorm(50),
test.4=rnorm(50))
g <- ggpairs(data=fake.data,
columnLabels=rep('', ncol(fake.data)))
#Set columnLabels to a vector of blank column labels
#so that original variable labels will be blank.
print(g)
customize.labels(plotObj=g,
titleLabel = 'Test plot', #string for title
left.opts = list(x=-0.5, #moves farther to the left, away from vertical axis
y=0.5, #centered with respect to vertical axis
just=c('center', 'center'),
rot=90,
gp=list(col='red',
fontface='italic',
fontsize=12)),
bottom.opts = list(x=0.5,
y=0,
rot=45, #angle the text at 45 degrees
just=c('center', 'top'),
gp=list(col='red',
fontface='bold',
fontsize=10)),
title.opts = list(gp=list(col='green',
fontface='bold.italic'))
)
(This makes some very ugly labels -- for the purposes of demonstration only!)
I didn't tinker with placing the labels somewhere other than the left and bottom -- as in your Geovisualist example -- but I think you'd do it by changing the arguments to vplayout in the "Left Side" and "Bottom Side" pieces of code in customize.labels. The x and y coordinates in grid.text are defined relative to a viewport, which divides the display area into a grid in
pushViewport(viewport(layout = grid.layout(
viewPortCount, viewPortCount,
widths = viewPortWidths, heights = viewPortHeights
)))
The call to vplayout specifies which cell of the grid is being used to position each label.
Caveat: not a complete answer but perhaps suggests a way to approach it. You can do this by editing the grid objects.
# Plot in current window
# use left to add space at y axis and bottom for below xaxis
# see ?print.ggpairs
print(pairs.chrt, left = 1, bottom = 1)
# Get list of grobs in current window and extract the axis labels
# note if you add a title this will add another text grob,
# so you will need to tweak this so not to extract it
g <- grid.ls(print=FALSE)
idx <- g$name[grep("text", g$name)]
# Rotate yaxis labels
# change the rot value to the angle you want
for(i in idx[1:6]) {
grid.edit(gPath(i), rot=0, hjust=0.25, gp = gpar(col="red"))
}
# Remove extra ones if you want
n <- ncol(airquality)
lapply(idx[c(1, 2*n)], grid.remove)
My answer won't fix the diagonal label issue but it will fix the overlay one.
I had this issue with the report I am currently writing, where the axis titles were always over the axes, especially in ggpairs. I used a combination of adjusting the out.height/out.width in conjunction with fig.height/fig.width. Separately the problem was not fixed, but together it was. fig.height/fig.width took the labels away from the axis but made them too small to read, and out.height/out.width just made the plot bigger with the problem unchanged. The below gave me the results shown:
out.height="400px", out.width="400px",fig.height=10,fig.width=10
before:plot with issues
after:

How to manipulate y-axis text labels in R varImpPlot?

The following sample resembles my dataset:
require(randomForest)
alpha = c(1,2,3,4,5,6)
bravo = c(2,3,4,5,6,7)
charlie = c(2,6,5,3,5,6)
mydata = data.frame(alpha,bravo,charlie)
myrf = randomForest(alpha~bravo+charlie, data = mydata, importance = TRUE)
varImpPlot(myrf, type = 2)
I cannot seem to control the placement of the y-axis labels in varImpPlot. I have tried altering the plot parameters (e.g. mar, oma), with no success. I need the y-axis labels shifted to the left in order to produce a PDF with proper label placement.
How can I shift the y-axis labels to the left?
I tried to use adj parameter but it produces a bug. As varImpPlot , use dotchart behind, Here a version using lattice dotplot. Then you can customize you axs using scales parameters.
imp <- importance(myref, class = NULL, scale = TRUE, type = 2)
dotplot(imp, scales=list(y =list(cex=2,
at = c(1,2),
col='red',
rot =20,
axs='i') ,
x =list(cex=2,col='blue')) )
You can extract the data needed to construct the plot out of myref and construct a plot with ggplot. By doing so you have more freedom in tweaking the plot. Here are some examples
library(ggplot2)
str(myrf)
str(myrf$importance)
data <- as.data.frame(cbind(rownames(myrf$importance),round(myrf$importance[,"IncNodePurity"],1)))
colnames(data) <- c("Parameters","IncNodePurity")
data$IncNodePurity <- as.numeric(as.character(data$IncNodePurity))
Standard plot:
(p <- ggplot(data) + geom_point(aes(IncNodePurity,Parameters)))
Rotate y-axis labels:
(p1 <- p+ theme(axis.text.y = element_text(angle = 90, hjust = 1)))
Some more tweaking (also first plot shown here):
(p2 <- p1 + scale_x_continuous(limits=c(3,7),breaks=3:7) + theme(axis.title.y = element_blank()))
Plot that looks like the varImpPlot (second plot shown here) :
(p3 <- p2+ theme(panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_line(colour = 'gray', linetype = 'dashed'),
panel.background = element_rect(fill='white', colour='black')))
Saving to pdf is easy with ggplot:
ggsave("randomforestplot.pdf",p2)
or
ggsave("randomforestplot.png",p2)
p2
p3
Did I understood correctly, that you want to get texts charlie and bravo more left of the boundary of the plot? If so, here's one hack to archive this, based on the modification of the rownames used in plotting:
myrf = randomForest(alpha~bravo+charlie, data = mydata, importance = TRUE)
#add white spaces at the end of the rownames
rownames(myrf$importance)<-paste(rownames(myrf$importance), " ")
varImpPlot(myrf, type = 2)
The adj parameter in dotchart is fixed as 0 (align to right), so that cannot be changed without modifying the code of dotchart:
mtext(labs, side = 2, line = loffset, at = y, **adj = 0**, col = color,
las = 2, cex = cex, ...)
(from dotchart)
EDIT:
You can make another type of hack also. Take the code of dotchart, change the above line to
mtext(labs, side = 2, line = loffset, at = y, adj = adjust_ylab, col = color,
las = 2, cex = cex, ...)
Then add argument adjust_ylab to the argument list, and rename the function as for example dotchartHack. Now copy the code of varImpPlot, find the line which calls dotchart, change the function name to dotchartHack and add the argument adjust_ylab=adjust_ylab to function call, rename the function to varImpPlotHack and add adjust_ylab to this functions argument list. Now you can change the alignment of the charlie and bravo by changing the parameter adjust_ylab:
myrf = randomForest(alpha~bravo+charlie, data = mydata, importance = TRUE)
varImpPlotHack(myrf, type = 2,adjust_ylab=0.5)
From ?par:
The value of adj determines the way in which text strings are
justified in text, mtext and title. A value of 0 produces
left-justified text, 0.5 (the default) centered text and
right-justified text. (Any value in [0, 1] is allowed, and on most
devices values outside that interval will also work.)

Resources