The grid-package has a special tool for generating axis. It writes the axis outside the current viewport and the height using grobHeight is therefore considered to be 0mm. Unfortunately I need to make space for the axis and I want to know the exact height of the object. Below is an example that illustrates the problem:
library(grid)
plotColorBar <- function () {
grid.newpage()
xg <- xaxisGrob(at=c(0,.25,.5,.75, 1),
label= sprintf("%d %%", c(0,.25,.5,.75, 1)*100),
main=FALSE)
bar_layout <- grid.layout(nrow=3, ncol=3,
heights = unit.c(unit(.80, "npc"),
grobHeight(xg),
unit(.2, "npc") - grobHeight(xg)),
widths = unit.c(unit(.25, "npc"),
unit(1, "npc") -
unit(.5, "npc"),
unit(.25, "npc")))
pushViewport(viewport(layout=bar_layout, name="Bar_layout"))
pushViewport(viewport(layout.pos.row=3,
layout.pos.col=2,
name="Color_bar"))
grid.draw(xg)
bar_clrs <- colorRampPalette(c("red", "blue"), space="Lab")(101)
grid.raster(t(as.raster(bar_clrs)), width=1, height=1, interpolate=FALSE)
popViewport()
pushViewport(viewport(layout.pos.row=1,
layout.pos.col=1:3,
name="Main_exc_bar"))
grid.rect(gp=gpar(col="black", fill="#00000022"))
grid.text("Coool")
popViewport()
}
png(filename="axisWihtoutHeight.png", width=250, height=250, res=96)
plotColorBar()
dev.off()
Gives the following image:
Notice that the grey area covers the axis text. When I try convertY(grobHeight(xg), "mm") it returns 0mm.
Now applying the #baptiste recommended fix improves the image slightly:
heightDetails.xaxis = function(x) do.call(sum, lapply(x$children, grobHeight))
png(filename="axisWihtHeight.png", width=250, height=250, res=96)
plotColorBar()
dev.off()
As you can see the text for some reason is twice the height. Adjusting this manually through the following option although this does feel a little clumsy:
heightDetails.xaxis = function(x) {
grobHeight(x$children$ticks) +
grobHeight(x$children$labels) +
grobHeight(x$children$labels)
}
png(filename="axisWihtDoubleHeight.png", width=250, height=250, res=96)
plotColorBar()
dev.off()
Final solution
As suggested it seems that unit(1.5*cex, "line") does the job nicely:
heightDetails.xaxis = function(x) {
cex <- 1
if (!is.null(x$children$labels$gp$cex))
cex <- x$children$labels$gp$cex
grobHeight(x$children$ticks) +
unit(1.5*cex, "line")
}
you'll want to define a heightDetails method for the gTree, something along those lines
heightDetails.xaxis = function(x) do.call(sum, lapply(x$children, grobHeight))
Related
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:
I'm creating ggplots in a loop and then using grid.arrange to plot each of my figures on one page in a lattice-type graph. The problem I have is that I have a border around each figure and they merge together when I plot them. Does anyone know how to add white space between the figures. I've looked for information about figure padding and also toyed around with trying to add blank geom_rect between my plots, but so far no luck. Some simplified code is provided below. Thanks for any help you can offer.
p = vector("list", 3) #List for arranging grid
for(ii in 1:3){
p[[ii]] = ggplot(mtcars, aes(x = wt, y = mpg))+
geom_point()+
theme(plot.background = element_rect(colour = 'black', size = 2))
}
do.call("grid.arrange", c(p, ncol=1))
I tried quite a few different efforts to get the viewports to be smaller within a 3 x 1 layout and finally realized that just adding some blank space with narrow heights in the 5 x 1 layout was pretty easy:
Layout <- grid.layout(nrow = 5, ncol = 1,
heights=c(1, .1, 1, .1, 1) )
# could have written code to alternate heights or widths with gaps
grid.show.layout(Layout)
vplayout <- function(...) { # sets up new page with Layout
grid.newpage()
pushViewport(viewport(layout = Layout))
}
subplot <- function(x, y) viewport(layout.pos.row = x,
layout.pos.col = y)
mmplot <- function(p=p) { # could make more general
vplayout()
print(p[[1]], vp = subplot(1, 1 ))
print(p[[2]], vp = subplot(3, 1))
print(p[[3]], vp = subplot(5, 1 ))
}
mmplot(a, z)
alternatively, this experimental version of gtable offers a similar interface to grid.arrange,
library(ggplot2)
library(gtable)
lp <- replicate(3, qplot(rnorm(10), rnorm(10)) +
theme(plot.background=element_rect(size = 3, colour="black")),
simplify = FALSE)
lg <- lapply(lp, ggplotGrob)
g <- do.call(gtable_arrange, c(lg, ncol=1, draw=FALSE))
g <- gtable_add_rows(g, heights = unit(1, "line"), pos = 1)
g <- gtable_add_rows(g, heights = unit(1, "line"), pos = 3)
grid.newpage()
grid.draw(g)
I'm trying to use the function grid.gradientFill from the gridSVG package, but unfortunately I'm not able to see a gradient in my SVG output.
I'm not sure if my code is correct or my Browser does not work (Chrome: 35.0.1916.153 m), can you please give some advise?
Here is my R code:
library(grid)
library(gridSVG)
lg <- linearGradient(col = c("black", "white", "black"))
x <- c(0.2,0.2,0.35,0.5,0.65,0.8,0.8,0.65,0.5,0.35)
y <- c(0.5,0.6,0.61,0.7,0.81,0.8,0.7,0.71,0.6,0.51)
s <- c(0,0,-1,0,-1,0,0,-1,0,-1)
grid.newpage()
vp <- viewport(width=0.75, height=0.75)
pushViewport(vp)
grid.rect(gp=gpar(col="blue"))
pushViewport(viewport(layout.pos.col=1, layout.pos.row=1))
grid.rect(x = unit(0.5, "npc"), y = unit(0.5, "npc"),
width = unit(1, "npc"), height = unit(1, "npc"),
just = "centre",
default.units = "npc",
gp=gpar(col="green", fill = "blue"), draw = TRUE, name = "tom")
grid.xspline(x = x, y = y,shape=s, open=FALSE, gp=gpar(col=NA, fill="darkred"), name="spline")
grid.gradientFill("spline", lg)
grid.gradientFill("tom", lg)
grid.export("c:/#temp/somekindofgradient.SVG")
I'm very interested in giving the spline a gradient ...
Any hint is appreciated :-)
So, finally I found the solution :-)
If you want to use
grid.gradientFill(object, ...)
The object, in my question the grid.xspline(...) object called "spline" does not have to have a fill parameter, meaning ...
Replacing
grid.xspline(x = x, y = y,shape=s, open=FALSE, gp=gpar(col=NA, fill="darkred"), name="spline")
with
grid.xspline(x = x, y = y,shape=s, open=FALSE, gp=gpar(col=NA), name="spline")
And there is some beautiful gradient :-)
Good Day All,
I want to add text floating in my wireframe plot and I am rather confused. I can certainly add the text as a title (e.g. main="Hello World") but I would rather not have my particular text in the title
Here is a sample wireframe:
library(lattice)
#set up some simplified data
x <- seq(-.8, .8, .1)
y <- seq(-.8, .8, .1)
myGrid <- data.frame(expand.grid(x,y))
colnames(myGrid) <- c("x","y")
myGrid$z <- myGrid$x + myGrid$y
wireframe(
myGrid$z ~ myGrid$x * myGrid$y,
xlab="X", ylab="Y", zlab="Z",
scales = list(z.ticks=5, arrows=FALSE, col="black", font=3, tck=1)
)
If I wanted to add "Hello World" in this plot floating somewhere how would I do that ?
Override the panel function and add text with grid.text.
wireframe(
myGrid$z ~ myGrid$x * myGrid$y,
xlab="X", ylab="Y", zlab="Z",
scales = list(z.ticks=5, arrows=FALSE, col="black", font=3, tck=1),
panel = function(...)
{
panel.wireframe(...)
grid.text("some text", 0, 0, default.units = "native")
}
)
Alternatively you could add the text after you plotted your wireframe with
grid::grid.text("some text", x=unit(0.7, "npc"), y=unit(0.8, "npc"))
The unit function allows you to specify the location of the text. If you use "npc" as your unit, the total width and height of your graph is 1. So the example above would display your text in the top right corner while x=y=unit(0.5, "npc") would plot it in the center.
I need to remove the ticks and major line from a generated axis using grid.yaxis.
According to ?grid.yaxis there is an "edits" parameter that will let me configure the "major", "ticks" and "labels".
However, I cant find how to use the edits parameter.
Here's some example code that will draw an axis in the center.
grid_test_axis <- function ()
{
grid.newpage()
vplayout <- function(x,y)
viewport(layout.pos.row=x,layout.pos.col=y)
pushViewport( viewport( layout=grid.layout( nrow=3, ncol=3, widths=c(1,1,1), heights=c(1,1,1)) ) )
pushViewport(viewport(layout.pos.col=1, clip="on"))
grid.rect(gp=gpar(fill="red"))
popViewport()
pushViewport(viewport(layout.pos.col=3, clip="on"))
grid.rect(gp=gpar(fill="brown"))
popViewport()
pushViewport(viewport(layout.pos.row=1,layout.pos.col=2))
grid.rect(gp=gpar(fill="yellow"))
popViewport()
pushViewport(viewport(layout.pos.row=3,layout.pos.col=2))
grid.rect(gp=gpar(fill="blue"))
popViewport()
pushViewport(viewport(layout.pos.row=2,layout.pos.col=2))
pushViewport(viewport(layout=grid.layout(nrow=1, ncol=2)))
pushViewport(viewport(layout.pos.row=1,layout.pos.col=2))
grid.yaxis(main=TRUE, at=seq(.1, .9, length=5))
popViewport(3)
}
I would like to just stay with the numbers, not the ticks or major line.
Thanks.
Try this:
grid.yaxis(name="ya", main=TRUE, at=seq(0.1, 0.9, length=5))
grid.remove(gPath("ya", "ticks"))
grid.remove(gPath("ya", "major"))