How to change the position of the zoomed area from facet_zoom()? - r

With facet_zoom() from the ggforce package one can create nice zooms to highlight certain regions of a plot. Unfortunately, when zooming in on the y axis the original plot is always on the right side.
Is there a way to place the original plot on the left?
This would feel more intuitive to first look at the main plot and then at the zoomed region. As an example I would like to swap the position of the two facets in this plot:
(No reproducible example added, since I believe this is a question about the existence of a certain functionality.)

I've tweaked the current code for FacetZoom on GitHub to swop the horizontal order from [zoom, original] to [original, zoom]. The changes aren't complicated, but they are scattered throughout draw_panels() function's code, so the full code is rather long.
Result:
# example 1, with split = FALSE, horizontal = TRUE (i.e. default settings)
p1 <- ggplot(mtcars, aes(x = mpg, y = disp, colour = factor(cyl))) +
geom_point() +
theme_bw()
p1 + ggtitle("Original") + facet_zoom(y = disp > 300)
p1 + ggtitle("Modified") + facet_zoom2(y = disp > 300)
# example 2, with split = TRUE
p2 <- ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
geom_point() +
theme_bw()
p2 + ggtitle("Original") +
facet_zoom(xy = Species == "versicolor", split = TRUE)
p2 + ggtitle("Modified") +
facet_zoom2(xy = Species == "versicolor", split = TRUE)
Code used (I've commented out the original code, where modified code is used, & indicated the packages for functions from other packages):
library(ggplot)
library(ggforce)
library(grid)
# define facet_zoom2 function to use FacetZoom2 instead of FacetZoom
# (everything else is the same as facet_zoom)
facet_zoom2 <- function(x, y, xy, zoom.data, xlim = NULL, ylim = NULL,
split = FALSE, horizontal = TRUE, zoom.size = 2,
show.area = TRUE, shrink = TRUE) {
x <- if (missing(x)) if (missing(xy)) NULL else lazyeval::lazy(xy) else lazyeval::lazy(x)
y <- if (missing(y)) if (missing(xy)) NULL else lazyeval::lazy(xy) else lazyeval::lazy(y)
zoom.data <- if (missing(zoom.data)) NULL else lazyeval::lazy(zoom.data)
if (is.null(x) && is.null(y) && is.null(xlim) && is.null(ylim)) {
stop("Either x- or y-zoom must be given", call. = FALSE)
}
if (!is.null(xlim)) x <- NULL
if (!is.null(ylim)) y <- NULL
ggproto(NULL, FacetZoom2,
shrink = shrink,
params = list(
x = x, y = y, xlim = xlim, ylim = ylim, split = split, zoom.data = zoom.data,
zoom.size = zoom.size, show.area = show.area,
horizontal = horizontal
)
)
}
# define FacetZoom as a ggproto object that inherits from FacetZoom,
# with a modified draw_panels function. the compute_layout function references
# the version currently on GH, which is slightly different from the CRAN
# package version.
FacetZoom2 <- ggproto(
"FacetZoom2",
ggforce::FacetZoom,
compute_layout = function(data, params) {
layout <- rbind( # has both x & y dimension
data.frame(name = 'orig', SCALE_X = 1L, SCALE_Y = 1L),
data.frame(name = 'x', SCALE_X = 2L, SCALE_Y = 1L),
data.frame(name = 'y', SCALE_X = 1L, SCALE_Y = 2L),
data.frame(name = 'full', SCALE_X = 2L, SCALE_Y = 2L),
data.frame(name = 'orig_true', SCALE_X = 1L, SCALE_Y = 1L),
data.frame(name = 'zoom_true', SCALE_X = 1L, SCALE_Y = 1L)
)
if (is.null(params$y) && is.null(params$ylim)) { # no y dimension
layout <- layout[c(1,2, 5:6),]
} else if (is.null(params$x) && is.null(params$xlim)) { # no x dimension
layout <- layout[c(1,3, 5:6),]
}
layout$PANEL <- seq_len(nrow(layout))
layout
},
draw_panels = function(panels, layout, x_scales, y_scales, ranges, coord,
data, theme, params) {
if (is.null(params$x) && is.null(params$xlim)) {
params$horizontal <- TRUE
} else if (is.null(params$y) && is.null(params$ylim)) {
params$horizontal <- FALSE
}
if (is.null(theme[['zoom']])) {
theme$zoom <- theme$strip.background
}
if (is.null(theme$zoom.x)) {
theme$zoom.x <- theme$zoom
}
if (is.null(theme$zoom.y)) {
theme$zoom.y <- theme$zoom
}
axes <- render_axes(ranges, ranges, coord, theme, FALSE)
panelGrobs <- ggforce:::create_panels(panels, axes$x, axes$y)
panelGrobs <- panelGrobs[seq_len(length(panelGrobs) - 2)]
if ('full' %in% layout$name && !params$split) {
panelGrobs <- panelGrobs[c(1, 4)]
}
# changed coordinates in indicator / lines to zoom from
# the opposite horizontal direction
if ('y' %in% layout$name) {
if (!inherits(theme$zoom.y, 'element_blank')) {
zoom_prop <- scales::rescale(
y_scales[[2]]$dimension(ggforce:::expansion(y_scales[[2]])),
from = y_scales[[1]]$dimension(ggforce:::expansion(y_scales[[1]])))
indicator <- polygonGrob(
x = c(0, 0, 1, 1), # was x = c(1, 1, 0, 0),
y = c(zoom_prop, 1, 0),
gp = gpar(col = NA, fill = alpha(theme$zoom.y$fill, 0.5)))
lines <- segmentsGrob(
x0 = c(1, 1), x1 = c(0, 0), # was x0 = c(0, 0), x1 = c(1, 1)
y0 = c(0, 1), y1 = zoom_prop,
gp = gpar(col = theme$zoom.y$colour,
lty = theme$zoom.y$linetype,
lwd = theme$zoom.y$size,
lineend = 'round'))
indicator_h <- grobTree(indicator, lines)
} else {
indicator_h <- zeroGrob()
}
}
if ('x' %in% layout$name) {
if (!inherits(theme$zoom.x, 'element_blank')) {
zoom_prop <- scales::rescale(x_scales[[2]]$dimension(ggforce:::expansion(x_scales[[2]])),
from = x_scales[[1]]$dimension(ggforce:::expansion(x_scales[[1]])))
indicator <- polygonGrob(c(zoom_prop, 1, 0), c(1, 1, 0, 0),
gp = gpar(col = NA, fill = alpha(theme$zoom.x$fill, 0.5)))
lines <- segmentsGrob(x0 = c(0, 1), y0 = c(0, 0), x1 = zoom_prop, y1 = c(1, 1),
gp = gpar(col = theme$zoom.x$colour,
lty = theme$zoom.x$linetype,
lwd = theme$zoom.x$size,
lineend = 'round'))
indicator_v <- grobTree(indicator, lines)
} else {
indicator_v <- zeroGrob()
}
}
if ('full' %in% layout$name && params$split) {
space.x <- theme$panel.spacing.x
if (is.null(space.x)) space.x <- theme$panel.spacing
space.x <- unit(5 * as.numeric(convertUnit(space.x, 'cm')), 'cm')
space.y <- theme$panel.spacing.y
if (is.null(space.y)) space.y <- theme$panel.spacing
space.y <- unit(5 * as.numeric(convertUnit(space.y, 'cm')), 'cm')
# change horizontal order of panels from [zoom, original] to [original, zoom]
# final <- gtable::gtable_add_cols(panelGrobs[[3]], space.x)
# final <- cbind(final, panelGrobs[[1]], size = 'first')
# final_tmp <- gtable::gtable_add_cols(panelGrobs[[4]], space.x)
# final_tmp <- cbind(final_tmp, panelGrobs[[2]], size = 'first')
final <- gtable::gtable_add_cols(panelGrobs[[1]], space.x)
final <- cbind(final, panelGrobs[[3]], size = 'first')
final_tmp <- gtable::gtable_add_cols(panelGrobs[[2]], space.x)
final_tmp <- cbind(final_tmp, panelGrobs[[4]], size = 'first')
final <- gtable::gtable_add_rows(final, space.y)
final <- rbind(final, final_tmp, size = 'first')
final <- gtable::gtable_add_grob(final, list(indicator_h, indicator_h),
c(2, 6), 3, c(2, 6), 5,
z = -Inf, name = "zoom-indicator")
final <- gtable::gtable_add_grob(final, list(indicator_v, indicator_v),
3, c(2, 6), 5,
z = -Inf, name = "zoom-indicator")
heights <- unit.c(
unit(max_height(list(axes$x[[1]]$top, axes$x[[3]]$top)), 'cm'),
unit(1, 'null'),
unit(max_height(list(axes$x[[1]]$bottom, axes$x[[3]]$bottom)), 'cm'),
space.y,
unit(max_height(list(axes$x[[2]]$top, axes$x[[4]]$top)), 'cm'),
unit(params$zoom.size, 'null'),
unit(max_height(list(axes$x[[2]]$bottom, axes$x[[4]]$bottom)), 'cm')
)
# swop panel width specifications according to the new horizontal order
widths <- unit.c(
# unit(max_width(list(axes$y[[3]]$left, axes$y[[4]]$left)), 'cm'),
# unit(params$zoom.size, 'null'),
# unit(max_height(list(axes$y[[3]]$right, axes$y[[4]]$right)), 'cm'),
# space.x,
# unit(max_width(list(axes$y[[1]]$left, axes$y[[2]]$left)), 'cm'),
# unit(1, 'null'),
# unit(max_height(list(axes$y[[1]]$right, axes$y[[2]]$right)), 'cm')
unit(max_width(list(axes$y[[1]]$left, axes$y[[2]]$left)), 'cm'),
unit(1, 'null'),
unit(max_height(list(axes$y[[1]]$right, axes$y[[2]]$right)), 'cm'),
space.x,
unit(max_width(list(axes$y[[3]]$left, axes$y[[4]]$left)), 'cm'),
unit(params$zoom.size, 'null'),
unit(max_height(list(axes$y[[3]]$right, axes$y[[4]]$right)), 'cm')
)
final$heights <- heights
final$widths <- widths
} else {
if (params$horizontal) {
space <- theme$panel.spacing.x
if (is.null(space)) space <- theme$panel.spacing
space <- unit(5 * as.numeric(convertUnit(space, 'cm')), 'cm')
heights <- unit.c(
unit(max_height(list(axes$x[[1]]$top, axes$x[[2]]$top)), 'cm'),
unit(1, 'null'),
unit(max_height(list(axes$x[[1]]$bottom, axes$x[[2]]$bottom)), 'cm')
)
# change horizontal order of panels from [zoom, original] to [original, zoom]
# first <- gtable::gtable_add_cols(panelGrobs[[2]], space)
# first <- cbind(final, panelGrobs[[1]], size = 'first')
final <- gtable::gtable_add_cols(panelGrobs[[1]], space)
final <- cbind(final, panelGrobs[[2]], size = "first")
final$heights <- heights
# swop panel width specifications according to the new horizontal order
# unit(c(params$zoom.size, 1), 'null')
final$widths[panel_cols(final)$l] <- unit(c(1, params$zoom.size), 'null')
final <- gtable::gtable_add_grob(final, indicator_h, 2, 3, 2, 5,
z = -Inf, name = "zoom-indicator")
} else {
space <- theme$panel.spacing.y
if (is.null(space)) space <- theme$panel.spacing
space <- unit(5 * as.numeric(convertUnit(space, 'cm')), 'cm')
widths <- unit.c(
unit(max_width(list(axes$y[[1]]$left, axes$y[[2]]$left)), 'cm'),
unit(1, 'null'),
unit(max_height(list(axes$y[[1]]$right, axes$y[[2]]$right)), 'cm')
)
final <- gtable::gtable_add_rows(panelGrobs[[1]], space)
final <- rbind(final, panelGrobs[[2]], size = 'first')
final$widths <- widths
final$heights[panel_rows(final)$t] <- unit(c(1, params$zoom.size), 'null')
final <- gtable::gtable_add_grob(final, indicator_v, 3, 2, 5,
z = -Inf, name = "zoom-indicator")
}
}
final
}
)
Note: create_panels and expansion are un-exported functions from the ggforce package, so I referenced them with triple colons. This isn't robust for writing packages, but should suffice as a temporary workaround.
Update 30 Oct 2019: A suggestion for those seeing errors like Invalid 'type' (list) of argument after trying to use this solution as-is. The issue is likely due to updates made to the ggforcepackage since this solution was developed. To get the code in this solution working again, install the version of ggforce that was available when the solution was developed. This can be done with the devtools package pointing to the 4008a2e commit:
devtools::install_github("thomasp85/ggforce", ref = '4008a2e')

Related

adding a logo to a grid.table PDF output in R

I have a table output in pdf format and I want to customise it to bring in line with a corporate theme. However, I'm new to this area in R and still finding it difficult to find my feet in adding logos.
My original dataset is composed of over 600 rows of data and is sensitive, so I've used a sample dataset to demonstrate. So far, I've got the following code using the grid and gridExtra packages:
library(grid)
library(gridExtra)
Data <- data.frame(Staff = c("Rod","Barry","Cheiny"),
M1 = c(50,40,55),
M2 = c(60,50,55),
M3 = c(55,50,45))
maxrow <- c(35);
npages <- ceiling(nrow(Data)/maxrow);
pdf("Data.pdf", height = 11, width = 10)
idx <- seq(1, maxrow)
grid.table(Data, rows = NULL, theme = ttheme_minimal())
grid.text("data",gp = gpar(fontsize = 12,fontface = "bold",alpha = 0.5),
vjust = -40,
hjust = -0.5)
for (i in 2:npages){
grid.newpage();
if(i*maxrow <= nrow(Data)) {
idx <- seq(1+((i-1)*maxrow), i*maxrow)
}else{
idx <- seq(1+((i-1)*maxrow), nrow(Data))
}
grid.table(Data, rows =NULL, theme = ttheme_minimal())
}
dev.off()
I'm getting a reasonable output at the moment, but I want to add a logo to each of the pages generated.
Anyone know how to add a logo that will repeat across all the pages?
It's easy to add elements with grid.draw(), but the design is up to you
library(grid)
library(gridExtra)
Data <- data.frame(Staff = c("Rod","Barry","Cheiny"),
M1 = c(50,40,55),
M2 = c(60,50,55),
M3 = c(55,50,45))
library(png)
img <- readPNG(system.file("img", "Rlogo.png", package="png"))
footer <- grobTree(rectGrob(y=0,vjust=0,gp=gpar(fill="grey97",col=NA), height=unit(1,"in")),
textGrob(y=unit(0.5,"in"), expression(Corporate^TM~line~(c))),
rasterGrob(img, x=1, hjust=1,y=unit(0.5,"in"),height=unit(1,"in")-unit(2,"mm")))
maxrow <- c(35);
npages <- ceiling(nrow(Data)/maxrow);
pdf("Data.pdf", height = 11, width = 10)
idx <- seq(1, maxrow)
grid.table(Data, rows = NULL, theme = ttheme_minimal())
grid.draw(footer)
grid.text("data",gp = gpar(fontsize = 12,fontface = "bold",alpha = 0.5),
vjust = -40,
hjust = -0.5)
for (i in 2:npages){
grid.newpage();
if(i*maxrow <= nrow(Data)) {
idx <- seq(1+((i-1)*maxrow), i*maxrow)
}else{
idx <- seq(1+((i-1)*maxrow), nrow(Data))
}
grid.table(Data, rows =NULL, theme = ttheme_minimal())
grid.draw(footer)
}
dev.off()

biwavelet package: "cex.axis" not working in plot.biwavelet(); A bug?

I am using biwavelet package to conduct wavelet analysis. However, when I want to adjust the label size for axis using cex.axis, the label size does not changed. On the other hand, cex.lab and cex.main are working well. Is this a bug? The following gives a reproducible example.
library(biwavelet)
t1 <- cbind(1:100, rnorm(100))
t2 <- cbind(1:100, rnorm(100))
# Continuous wavelet transform
wt.t1 <- wt(t1)
par(oma = c(0, 0.5, 0, 0), mar = c(4, 2, 2, 4))
plot(wt.t1,plot.cb = T,plot.phase = T,type = 'power.norm',
xlab = 'Time(year)',ylab = 'Period(year)',mgp=c(2,1,0),
main='Winter station 1',cex.main=0.8,cex.lab=0.8,cex.axis=0.8)
Edit
There was a previous question on this site a month ago: Wavelets plot: changing x-, y- axis, and color plot, but not solved. Any help this time? Thank you!
Yeah, it is a bug. Here is patched version: my.plot.biwavelet()
This version accepts argument cex.axis (defaults to 1), and you can change it when needed. I will briefly explain to you what the problem is, in the "explanation" section in the end.
my.plot.biwavelet <- function (x, ncol = 64, fill.cols = NULL, xlab = "Time", ylab = "Period",
tol = 1, plot.cb = FALSE, plot.phase = FALSE, type = "power.corr.norm",
plot.coi = TRUE, lwd.coi = 1, col.coi = "white", lty.coi = 1,
alpha.coi = 0.5, plot.sig = TRUE, lwd.sig = 4, col.sig = "black",
lty.sig = 1, bw = FALSE, legend.loc = NULL, legend.horiz = FALSE,
arrow.len = min(par()$pin[2]/30, par()$pin[1]/40), arrow.lwd = arrow.len *
0.3, arrow.cutoff = 0.9, arrow.col = "black", xlim = NULL,
ylim = NULL, zlim = NULL, xaxt = "s", yaxt = "s", form = "%Y", cex.axis = 1,
...) {
if (is.null(fill.cols)) {
if (bw) {
fill.cols <- c("black", "white")
}
else {
fill.cols <- c("#00007F", "blue", "#007FFF",
"cyan", "#7FFF7F", "yellow", "#FF7F00", "red",
"#7F0000")
}
}
col.pal <- colorRampPalette(fill.cols)
fill.colors <- col.pal(ncol)
types <- c("power.corr.norm", "power.corr", "power.norm",
"power", "wavelet", "phase")
type <- match.arg(tolower(type), types)
if (type == "power.corr" | type == "power.corr.norm") {
if (x$type == "wtc" | x$type == "xwt") {
x$power <- x$power.corr
x$wave <- x$wave.corr
}
else {
x$power <- x$power.corr
}
}
if (type == "power.norm" | type == "power.corr.norm") {
if (x$type == "xwt") {
zvals <- log2(x$power)/(x$d1.sigma * x$d2.sigma)
if (is.null(zlim)) {
zlim <- range(c(-1, 1) * max(zvals))
}
zvals[zvals < zlim[1]] <- zlim[1]
locs <- pretty(range(zlim), n = 5)
leg.lab <- 2^locs
}
else if (x$type == "wtc" | x$type == "pwtc") {
zvals <- x$rsq
zvals[!is.finite(zvals)] <- NA
if (is.null(zlim)) {
zlim <- range(zvals, na.rm = TRUE)
}
zvals[zvals < zlim[1]] <- zlim[1]
locs <- pretty(range(zlim), n = 5)
leg.lab <- locs
}
else {
zvals <- log2(abs(x$power/x$sigma2))
if (is.null(zlim)) {
zlim <- range(c(-1, 1) * max(zvals))
}
zvals[zvals < zlim[1]] <- zlim[1]
locs <- pretty(range(zlim), n = 5)
leg.lab <- 2^locs
}
}
else if (type == "power" | type == "power.corr") {
zvals <- log2(x$power)
if (is.null(zlim)) {
zlim <- range(c(-1, 1) * max(zvals))
}
zvals[zvals < zlim[1]] <- zlim[1]
locs <- pretty(range(zlim), n = 5)
leg.lab <- 2^locs
}
else if (type == "wavelet") {
zvals <- (Re(x$wave))
if (is.null(zlim)) {
zlim <- range(zvals)
}
locs <- pretty(range(zlim), n = 5)
leg.lab <- locs
}
else if (type == "phase") {
zvals <- x$phase
if (is.null(zlim)) {
zlim <- c(-pi, pi)
}
locs <- pretty(range(zlim), n = 5)
leg.lab <- locs
}
if (is.null(xlim)) {
xlim <- range(x$t)
}
yvals <- log2(x$period)
if (is.null(ylim)) {
ylim <- range(yvals)
}
else {
ylim <- log2(ylim)
}
image(x$t, yvals, t(zvals), zlim = zlim, xlim = xlim,
ylim = rev(ylim), xlab = xlab, ylab = ylab, yaxt = "n",
xaxt = "n", col = fill.colors, ...)
box()
if (class(x$xaxis)[1] == "Date" | class(x$xaxis)[1] ==
"POSIXct") {
if (xaxt != "n") {
xlocs <- pretty(x$t) + 1
axis(side = 1, at = xlocs, labels = format(x$xaxis[xlocs],
form))
}
}
else {
if (xaxt != "n") {
xlocs <- axTicks(1)
axis(side = 1, at = xlocs, cex.axis = cex.axis)
}
}
if (yaxt != "n") {
axis.locs <- axTicks(2)
yticklab <- format(2^axis.locs, dig = 1)
axis(2, at = axis.locs, labels = yticklab, cex.axis = cex.axis)
}
if (plot.coi) {
polygon(x = c(x$t, rev(x$t)), lty = lty.coi, lwd = lwd.coi,
y = c(log2(x$coi), rep(max(log2(x$coi), na.rm = TRUE),
length(x$coi))), col = adjustcolor(col.coi,
alpha.f = alpha.coi), border = col.coi)
}
if (plot.sig & length(x$signif) > 1) {
if (x$type %in% c("wt", "xwt")) {
contour(x$t, yvals, t(x$signif), level = tol,
col = col.sig, lwd = lwd.sig, add = TRUE, drawlabels = FALSE)
}
else {
tmp <- x$rsq/x$signif
contour(x$t, yvals, t(tmp), level = tol, col = col.sig,
lwd = lwd.sig, add = TRUE, drawlabels = FALSE)
}
}
if (plot.phase) {
a <- x$phase
locs.phases <- which(zvals < quantile(zvals, arrow.cutoff))
a[locs.phases] <- NA
phase.plot(x$t, log2(x$period), a, arrow.len = arrow.len,
arrow.lwd = arrow.lwd, arrow.col = arrow.col)
}
box()
if (plot.cb) {
fields::image.plot(x$t, yvals, t(zvals), zlim = zlim, ylim = rev(range(yvals)),
xlab = xlab, ylab = ylab, col = fill.colors,
smallplot = legend.loc, horizontal = legend.horiz,
legend.only = TRUE, axis.args = list(at = locs,
labels = format(leg.lab, dig = 2)), xpd = NA)
}
}
Test
library(biwavelet)
t1 <- cbind(1:100, rnorm(100))
t2 <- cbind(1:100, rnorm(100))
# Continuous wavelet transform
wt.t1 <- wt(t1)
par(oma = c(0, 0.5, 0, 0), mar = c(4, 2, 2, 4))
my.plot.biwavelet(wt.t1,plot.cb = T,plot.phase = T,type = 'power.norm',
xlab = 'Time(year)',ylab = 'Period(year)',mgp=c(2,1,0),
main='Winter station 1',cex.main=0.8,cex.lab=0.8,cex.axis=0.8)
As expected, it is working.
Explanation
In plot.biwavelet(), why passing cex.axis via ... does not work?
plot.biwavelet() generates the your final plot mainly in 3 stages:
image(..., xaxt = "n", yaxt = "n") for generating basic plot;
axis(1, at = atTicks(1)); axis(2, at = atTicks(2)) for adding axis;
fields::image.plot() for displaying colour legend strip.
Now, although this function takes ..., they are only fed to the first image() call, while the following axis(), (including polygon(), contour(), phase.plot()) and image.plot() take none from .... When later calling axis(), no flexible specification with respect to axis control are supported.
I guess during package development time, problem described as in: Giving arguments from “…” argument to right function in R had been encountered. Maybe the author did not realize this potential issue, leaving a bug here. My answer to that post, as well as Roland's comments, points toward a robust fix.
I am not the package author so can not decide how he will fix this. My fix is brutal, but works for you temporary need: just add the cex.axis argument to axis() call. I have reached Tarik (package author) with an email, and I believe he will give you a much better explanation and solution.
I fixed this issue by passing the ... argument to axis in plot.biwavelet. Your code should now work as desired. Note that changes to cex.axis and other axis arguments will affect all three axes (x, y, z).
You can download the new version (0.20.8) of biwavelet from GitHub by issuing the following command at the R console (this assumes that you have the package devtools already installed): devtools::install_github("tgouhier/biwavelet")
Thanks for pointing out the bug!

Changing axes labels for biplot() in R

I am trying to visualize the results of a PCoA{ape} by making a biplot in R.
The axes now get the default labels axis 1 and axis 2, but I want to edit this.
This is the code I have tried:
biplot(pcoa.ntK, Y=NULL, plot.axes=c(1,2), rn=ntnames,
xlabs="PC1 (%)", ylabs="PC2 (%)")
But the labels don't change.
Can someone tell me what I'm doing wrong here?
And I also would like to edit the title, anyone tips for this?
My data:
ntK <- matrix(
c(0.00000, 0.01500, 0.01832, 0.02061, 0.01902, 0.01270, 0.02111, 0.01655, 0.01520, 0.01691,
0.01667, 0.00000, 0.01175, 0.01911, 0.01759, 0.01127, 0.01854, 0.01041, 0.00741, 0.02007,
0.02432, 0.01404, 0.00000, 0.02551, 0.01972, 0.01838, 0.02505, 0.01484, 0.01391, 0.02687,
0.01501, 0.01252, 0.01399, 0.00000, 0.01442, 0.01294, 0.01402, 0.01132, 0.01239, 0.01455,
0.02343, 0.01951, 0.01830, 0.02440, 0.00000, 0.01727, 0.02470, 0.02021, 0.01699, 0.02482,
0.01320, 0.01054, 0.01439, 0.01847, 0.01457, 0.00000, 0.01818, 0.01366, 0.00977, 0.01394,
0.02468, 0.01950, 0.02206, 0.02251, 0.02343, 0.02040, 0.00000, 0.02028, 0.01875, 0.02558,
0.02254, 0.01276, 0.01522, 0.02117, 0.02234, 0.01790, 0.02363, 0.00000, 0.01152, 0.02557,
0.01804, 0.00792, 0.01244, 0.02019, 0.01637, 0.01116, 0.01904, 0.01004, 0.00000, 0.02099,
0.01862, 0.01988, 0.02227, 0.02200, 0.02218, 0.01476, 0.02408, 0.02066, 0.01947, 0.00000),
nrow=10,
ncol=10)
library(ape)
ntnames <- c("A","B","C","D","E","F","G","H","I","J")
pcoa.ntK <- pcoa(ntK)
biplot is a generic function. The default method and the method for use with objects that come from using the prcomp function in the stats package do allow you to specify axis labels and a title, but for some reason the person that wrote the method that is called with objects of class pcoa hasn't allowed you to specify them. I think your only option would be to write your own version of biplot.pcoa (or ask the package maintainer to add this option).
This is a very quick and dirty hack of the function in the ape package that might do what you want, but no promises that it won't have broken something else!
biplot.pcoa <- function (x, Y = NULL, plot.axes = c(1, 2), dir.axis1 = 1, dir.axis2 = 1,
rn = NULL, xlabs = NULL, ylabs = NULL, main = NULL, ...)
{
k <- ncol(x$vectors)
if (k < 2)
stop("There is a single eigenvalue. No plot can be produced.")
if (k < plot.axes[1])
stop("Axis", plot.axes[1], "does not exist.")
if (k < plot.axes[2])
stop("Axis", plot.axes[2], "does not exist.")
if (!is.null(rn))
rownames(x$vectors) <- rn
labels = colnames(x$vectors[, plot.axes])
if (!is.null(xlabs)) labels[1] <- xlabs
if (!is.null(ylabs)) labels[2] <- ylabs
diag.dir <- diag(c(dir.axis1, dir.axis2))
x$vectors[, plot.axes] <- x$vectors[, plot.axes] %*% diag.dir
if (is.null(Y)) {
limits <- apply(x$vectors[, plot.axes], 2, range)
ran.x <- limits[2, 1] - limits[1, 1]
ran.y <- limits[2, 2] - limits[1, 2]
xlim <- c((limits[1, 1] - ran.x/10), (limits[2, 1] +
ran.x/5))
ylim <- c((limits[1, 2] - ran.y/10), (limits[2, 2] +
ran.y/10))
par(mai = c(1, 1, 1, 0.5))
plot(x$vectors[, plot.axes], xlab = labels[1], ylab = labels[2],
xlim = xlim, ylim = ylim, asp = 1)
text(x$vectors[, plot.axes], labels = rownames(x$vectors),
pos = 4, cex = 1, offset = 0.5)
if (is.null(main)){
title(main = "PCoA ordination", line = 2.5)
} else title(main = main, line = 2.5)
}
else {
n <- nrow(Y)
points.stand <- scale(x$vectors[, plot.axes])
S <- cov(Y, points.stand)
U <- S %*% diag((x$values$Eigenvalues[plot.axes]/(n -
1))^(-0.5))
colnames(U) <- colnames(x$vectors[, plot.axes])
par(mai = c(1, 0.5, 1.4, 0))
biplot(x$vectors[, plot.axes], U, xlab = labels[1], ylab = labels[2])
if (is.null(main)) {
title(main = c("PCoA biplot", "Response variables projected",
"as in PCA with scaling 1"), line = 4)
} else title(main = main, line = 4)
}
invisible()
}
biplot(pcoa.ntK, xlabs = 'My x label', ylabs = 'My y label', main = 'My title')
You can check the source code of biplot.pcoa and you'll see it's not that hard to modify. The author of the package decided to hard-code the axis labels based on the input and also the main title of the plot. Here's a modified version that will first check if values for xlab, ylab and main were used before using the pre-defined ones:
biplot.pcoa <- function (x, Y = NULL, plot.axes = c(1, 2), dir.axis1 = 1, dir.axis2 = 1,
rn = NULL, ...)
{
k <- ncol(x$vectors)
if (k < 2)
stop("There is a single eigenvalue. No plot can be produced.")
if (k < plot.axes[1])
stop("Axis", plot.axes[1], "does not exist.")
if (k < plot.axes[2])
stop("Axis", plot.axes[2], "does not exist.")
if (!is.null(rn))
rownames(x$vectors) <- rn
args <- list(...)
labels = ifelse(c("xlab", "ylab") %in% names(args), c(args$xlab, args$ylab), colnames(x$vectors[, plot.axes]))
diag.dir <- diag(c(dir.axis1, dir.axis2))
x$vectors[, plot.axes] <- x$vectors[, plot.axes] %*% diag.dir
if (is.null(Y)) {
limits <- apply(x$vectors[, plot.axes], 2, range)
ran.x <- limits[2, 1] - limits[1, 1]
ran.y <- limits[2, 2] - limits[1, 2]
xlim <- c((limits[1, 1] - ran.x/10), (limits[2, 1] +
ran.x/5))
ylim <- c((limits[1, 2] - ran.y/10), (limits[2, 2] +
ran.y/10))
par(mai = c(1, 1, 1, 0.5))
title <- ifelse("main" %in% names(args), args$main, "PCoA ordination")
plot(x$vectors[, plot.axes], xlab = labels[1], ylab = labels[2],
xlim = xlim, ylim = ylim, asp = 1,
main = title)
text(x$vectors[, plot.axes], labels = rownames(x$vectors),
pos = 4, cex = 1, offset = 0.5)
#title(main = "PCoA ordination", line = 2.5)
}
else {
n <- nrow(Y)
points.stand <- scale(x$vectors[, plot.axes])
S <- cov(Y, points.stand)
U <- S %*% diag((x$values$Eigenvalues[plot.axes]/(n -
1))^(-0.5))
colnames(U) <- colnames(x$vectors[, plot.axes])
par(mai = c(1, 0.5, 1.4, 0))
title <- ifelse("main" %in% names(args), args$main, c("PCoA biplot", "Response variables projected",
"as in PCA with scaling 1"))
biplot(x$vectors[, plot.axes], U, xlab = labels[1], ylab = labels[2], main = title)
# title(main = c("PCoA biplot", "Response variables projected",
# "as in PCA with scaling 1"), line = 4)
}
invisible()
}
Then:
biplot(pcoa.ntK, Y=NULL, plot.axes=c(1,2), rn=ntnames,
xlab="PC1 (%)", main = "Main Title")
Keep in mind this won't change the original function, so you'll need to load this modified version every time you load the package and need wish to set the labels like this.

Major and minor axis ticks for dates in base R

I want to create major and minor ticks in my date-formatted x-axis, so that for every 3rd tick (representing every 3 months) I have a major tick and a label.
This is a reproducible example of what I have so far, which currently has uniform ticks.
month<-c("2010-08-01", "2010-09-01", "2010-10-01", "2010-12-01", "2011-01-01", "2011-02-01",
"2011-03-01", "2011-04-01", "2011-05-01", "2011-06-01", "2011-07-01", "2011-09-01",
"2011-11-01", "2012-01-01", "2012-02-01", "2012-03-01", "2012-05-01", "2012-07-01",
"2012-08-01")
prevalence<-c(10,7.5,5.2,3.5,6.4,2.7,5.8,13.2,4.3,4.7,6.4,4.4,5.2,3.3,1.0,3.1,9.9,33.3,1.0)
df<-data.frame(month, prevalence)
df$month<-as.Date(df$month)
plot(df$month, df$prevalence,lwd = 1.8, ylim=c(0,40),pch=16, bty='n', xaxt='n',
ylab="Prevalence (%)", xlab="Month",col='black',cex=1,cex.lab=1.0,cex.axis=1.0)
at <- seq(from = min(df$month), to = max(df$month), by = "month") # produces a regular sequence of dates
axis.Date(side = 1, at = at, labels = FALSE, tck=-0.04)
axis(side=2, at=c(0,10,20,30,40,50), labels=c("", "", "", "", "", ""), tck=-0.04)
lines(df$month, df$prevalence, col='black', lwd=1.8)
I have tried using the package magicaxis, but it does not seem to allow for date-formatted axes.
As a quick fix you could use repeat axis.Date calls.
at1 <- at[c(TRUE, TRUE, FALSE)]
axis.Date(side = 1, at = at1, labels = FALSE, tck=-0.02)
at2 <- at[c(FALSE, FALSE, TRUE)]
axis.Date(side = 1, at = at2, labels = TRUE, tck=-0.04)
The TRUE and FALSE are used to subset the vector at
I don't know if this is still a problem for someone, but I made a general purpose function for axes with minor ticks, based on the base axis() function, and with similar arguments. It's available in the StratigrapheR package under minorAxis()
minorAxis <- function(side, n = NULL, at.maj = NULL, at.min = NULL, range = NULL,
tick.ratio = 0.5, labels.maj = TRUE, line = NA, pos = NA,
outer = FALSE, font = NA, lty = "solid", lwd = 1,
lwd.ticks = lwd, col = NULL, col.ticks = NULL, hadj = NA,
padj = NA, extend = FALSE, tcl = NA, ...)
{
if(side == 1 | side == 3){
tick.pos <- par("xaxp")
} else if (side == 2 | side == 4) {
tick.pos <- par("yaxp")
}
# Define the positions of major ticks ----
if(is.null(at.maj)) {
# nat.int <- (tick.pos[2] - tick.pos[1])/tick.pos[3]
at.maj <- seq(tick.pos[1], tick.pos[2],
by = (tick.pos[2] - tick.pos[1])/tick.pos[3])
}
# Define range, exclude at.maj values if necessary ----
if(length(range) != 0){
eff.range <- range
r1 <- at.maj - min(range)
r2 <- at.maj - max(range)
p1 <- which.min(abs(r1))
p2 <- which.min(abs(r2))
if(!(abs(r1[p1]/min(range)) < 1.5e-8) & r1[p1] < 0) p1 <- p1 + 1
if(!(abs(r2[p2]/max(range)) < 1.5e-8) & r2[p2] > 0) p2 <- p2 - 1
at.maj <- at.maj[p1:p2]
} else {
if(side == 1 | side == 3){
eff.range <- par("usr")[1:2]
} else if (side == 2 | side == 4) {
eff.range <- par("usr")[3:4]
}
}
# Define limits ----
if(!extend) {
if(!is.null(at.min) & length(range) == 0){
limits <- c(min(c(at.min, at.maj)), max(c(at.min, at.maj)))
} else {
limits <- c(min(at.maj), max(at.maj))
}
} else {
limits <- eff.range
}
# Standard axis when n and at.min are not given ----
if(is.null(n) & is.null(at.min)){
axis(side, at = limits, labels = FALSE, tick = TRUE, line = line,
pos = pos, outer = outer, lty = lty, lwd = lwd, lwd.ticks = 0,
col = col,...)
axis(side, at = at.maj, labels = labels.maj, tick = TRUE, line = line,
pos = pos, outer = outer, font = font, lty = lty,
lwd = 0, lwd.ticks = lwd.ticks, col = col, col.ticks = col.ticks,
hadj = hadj, padj = padj, tcl = tcl,...)
} else {
# Work the minor ticks: check regularity ----
mina <- min(at.maj)
maxa <- max(at.maj)
difa <- maxa - mina
na <- difa / (length(at.maj) - 1)
if(is.null(at.min))
{
# n realm ----
# Checks----
sia <- seq(mina,maxa,by = na)
if(!isTRUE(all.equal(sort(sia),sort(at.maj)))) {
stop("at.maj is irregular, use at.min for minor ticks (not n)")
}
if(!(is.numeric(n) & length(n) == 1)){
stop("n should be a numeric of length one")
}
# Work it ----
tick.pos <- c(mina,maxa,difa/na)
nat.int <- (tick.pos[2] - tick.pos[1])/tick.pos[3]
# Define the position of minor ticks ----
distance.between.minor <- nat.int/n
p <- seq(min(at.maj), max(at.maj), by = distance.between.minor)
q <- sort(every_nth(p,n,empty=FALSE))
# Extend outside of major ticks range if necessary ----
if(!extend) {
tick.seq <- q
} else {
possible.low.minors <- min(at.maj) - (n:1) * distance.between.minor
possible.hi.minors <- max(at.maj) + (1:n) * distance.between.minor
r3 <- possible.low.minors - min(eff.range)
r4 <- possible.hi.minors - max(eff.range)
p3 <- which.min(abs(r3))
p4 <- which.min(abs(r4))
if(!(abs(r3[p3]/min(eff.range)) < 1.5e-8) & r3[p3] < 0) p3 <- p3 + 1
if(!(abs(r4[p4]/max(eff.range)) < 1.5e-8) & r4[p4] > 0) p4 <- p4 - 1
if(p3 < length(possible.low.minors + 1)){
low.candidates <- seq(p3, length(possible.low.minors), 1)
low.laureates <- possible.low.minors[low.candidates]
} else {
low.laureates <- NULL
}
if(p4 > 0){
hi.candidates <- seq(1, p4, 1)
hi.laureates <- possible.hi.minors[ hi.candidates]
} else {
hi.laureates <- NULL
}
tick.seq <- c(low.laureates,q,hi.laureates)
}
} else {
# at.min realm ----
tick.pos <- c(mina,maxa,na)
tick.seq <- sort(at.min)
if(length(range) != 0){
r3 <- tick.seq - min(eff.range)
r4 <- tick.seq - max(eff.range)
p3 <- which.min(abs(r3))
p4 <- which.min(abs(r4))
if(!(abs(r3[p3]/min(eff.range)) < 1.5e-8) & r3[p3] < 0) p3 <- p3 + 1
if(!(abs(r4[p4]/max(eff.range)) < 1.5e-8) & r4[p4] > 0) p4 <- p4 - 1
tick.seq <- tick.seq [p3:p4]
}
}
# Define the length of ticks ----
if(is.na(tcl)) maj.tcl <- par()$tcl else if (!is.na(tcl)) maj.tcl <- tcl
min.tcl <- maj.tcl*tick.ratio
# Plot the axes ----
axis(side, at = limits, labels = FALSE, tick = TRUE, line = line,
pos = pos, outer = outer, lty = lty, lwd = lwd, lwd.ticks = 0,
col = col,...)
axis(side, at = at.maj, labels = labels.maj, tick = TRUE, line = line,
pos = pos, outer = outer, font = font, lty = lty,
lwd = 0, lwd.ticks = lwd.ticks, col = col, col.ticks = col.ticks,
hadj = hadj, padj = padj, tcl = maj.tcl,...)
axis(side, at = tick.seq, labels = FALSE, tick = TRUE, line = line,
pos = pos, outer = outer, lwd = 0, lwd.ticks = lwd.ticks, col = col,
col.ticks = col.ticks, tcl = min.tcl,...)
}
}
# Run this as example:
plot(c(0,1), c(0,1), axes = FALSE, type = "n", xlab = "", ylab = "")
minorAxis(1, n = 10, range = c(0.12,0.61))
minorAxis(3, n = 10, extend=FALSE)

align axes in double plot ggplot R

How can I align the two ggplots in the following function so that the x-axis is corresponding between the table and the graph ? As it is now the interval between the "ticks" is less in the table. Any ideas?
//M
Edit: Forgot to give credit to the learnr website for the table plotting method....
function ggkm:
ggkm<-function(time,event,stratum="-1",tit="",xscale=round(seq(0,max(time),by=max(time)/10),0)) {
lev<-levels(factor(stratum))
w2<-lev[1]!="-1"
if (w2) {stratum<-as.factor(stratum)}
m2s<-Surv(time,as.numeric(event))
if (w2) {fit <- survfit(m2s~stratum)}
else fit<-survfit(m2s~-1)
w<-fit$time
k<-fit$surv
o<-length(levels(stratum))
strata<-c(rep(names(fit$strata[1:o]),fit$strata[1:o]))
lev2<-levels(as.factor(strata))
upper<-fit$upper
lower<-fit$lower
if (w2) {f<-data.frame(w,k,strata,upper,lower)}
else f<-data.frame(w,k,upper,lower)
if (w2) {r<-ggplot (f,aes(x=w,y=k,fill=strata,group=strata))+geom_line(aes(color=strata))+scale_fill_brewer(f$strata,palette="Set1")+scale_color_brewer(f$strata,palette="Set1")}
else r<-ggplot(f,aes(x=w,y=k))+geom_line()
r<-r+geom_ribbon(aes(ymin=lower,ymax=upper),alpha=0.3)+opts(title=tit)
r<-r+opts(panel.grid.minor=theme_blank(),panel.grid.major=theme_blank(),panel.background=theme_blank(),axis.line=theme_segment())
r<-r+opts(legend.position=c(0.8,0.8))
#r<-r+opts(legend.title="")
if (w2) {
r<-r+scale_fill_brewer("",palette="Set1",breaks=lev2,labels=lev)+scale_color_brewer("",palette="Set1",breaks=lev2,labels=lev)
}
r<-r+geom_hline(yintercept=0.5,linetype=2)
r+expand_limits(x = 0, y = 0)+scale_x_continuous("Observation time (months)",expand = c(0, 0),breaks=xscale,labels=xscale,limits=c(min(xscale),max(xscale)))+scale_y_continuous("Probability of overall survival (proportion)",expand = c(0,0))->r
##number at risk table
u<-llply(names(fit$strata),function(x) rep(x,fit$strata[x]))
p<-ldply(u,function(x) data.frame(x))
fit2<-data.frame(p,fit$n.risk,fit$surv,fit$time,fit$n.event)
q<-dlply(fit2,.(x),function(g) data.frame(g$fit.n.risk,g$fit.surv,g$fit.time,g$fit.n.event))
e<-ldply(q,function(y){
o<-ldply(xscale,function(x) y[min(which((x-y$g.fit.time<0))),1])
oo<-cbind(o,xscale)
})
melt(e,id=c("xscale","x"))->e2
e2$strata<-as.factor(e2$x)
cast(subset(e2,e2$variable!="x.time"),strata~xscale,identity)->e3
#e3[["strata"]]<-names(e3[["strata"]])
dg<-ggplot(e2,aes(x=xscale,y=strata,color=strata,label=format(factor(value),nsmall=1)))+geom_text(size=2.5)+theme_bw()+scale_color_brewer(e2$strata,palette="Set1")
#levels(e2$strata)<-lev
dg<-dg+scale_y_discrete(limits=e2$strata)+expand_limits(x=0,y=0)
dg<-dg+opts(panel.grid.minor=theme_blank(),panel.grid.major=theme_blank(),panel.background=theme_blank(),axis.line=theme_blank())
dg<-dg+opts(panel.border=theme_blank(),axis.text.x=theme_blank(),axis.text.y=theme_blank(),axis.ticks=theme_blank())
#dg<-dg+opts(panel.border=theme_blank(),axis.text.x=theme_blank(),axis.ticks=theme_blank())
dg<-dg+opts(plot.margin = unit(c(-0.5,1, 0, 0.5), "lines")) + xlab(NULL) + ylab(NULL)+labs(colour="")
dg<-dg+opts(legend.position="none")
##Same page
Layout <- grid.layout(nrow = 2, ncol = 1, heights = unit(c(2,0.25), c("null", "null")))
grid.show.layout(Layout)
vplayout <- function(...) {
grid.newpage()
pushViewport(viewport(layout = Layout))
}
subplot <- function(x, y) viewport(layout.pos.row = x,layout.pos.col = y)
mmplot <- function(a, b) {
vplayout()
print(a, vp = subplot(1, 1))
print(b, vp = subplot(2, 1))
}
t<-mmplot(r, dg)
return(t)
}
example using ggkm (albeit a poor dataset)
library(survival)
require(ggplot2)
data(leukemia)
with(leukemia,ggkm(time,status,x,tit="Leukemia"))

Resources