Combining a map and a XY ggplot chart in R - r

I need to combine a map, which represents an archaeological site, with an XY ggplot chart of different archaeological objects. The map is in a tiff file and must respect its proportion.
First, this is the map, highlighting the reference scales in red (in X axis, from -6000 to -4000 there are 20 meters of distance, for instance; in Y axis, from 900 to 2100 there are 12 meters).
My ggplot chart is obtained by running this code:
archaeo <- ggplot() +
geom_ellipsis(data=Unit_H,
aes(x0 = X, y0 = Y, a = Diameter_E.W/2+250, b = Diameter_N.S/2+250, angle = 0),
lwd=0, col="darkgray", fill="gray", alpha=0.15) +
geom_ellipsis(data=Unit_H,
aes(x0 = X, y0 = Y, a = Diameter_E.W/2+120, b = Diameter_N.S/2+120, angle = 0),
lwd=0, col="darkgray", fill="gray", alpha=0.25) +
geom_ellipsis(data=Unit_H,
aes(x0 = X, y0 = Y, a = Diameter_E.W/2, b = Diameter_N.S/2, angle = 0),
lwd=0.5, col="darkgray", fill="gray", alpha=0.75) +
geom_point(data=Unit_H, aes(X, Y), size = 0.5) +
geom_point(data=Refits_H_trans, aes(x,y,group=sample, colour=factor(sample))) +
geom_line(data=Refits_H_trans, lwd=0.2, lty=1, aes(x,y, group=sample, colour=factor(sample))) +
coord_fixed() +
theme_bw() +
theme(legend.position="none") +
ggtitle("Unit H") +
xlim(-6600,-3800) +
ylim(400,2400)
The resulting chart is:
Now, my problem, which deals with the inclusion of the map as background of the ggplot. I used background_image() from ggpubr, with this result:
map_levelH <- readPNG("Planta H-I.png")
Map.archaeo <- ggplot() +
background_image(map_levelH) +
geom_ellipsis(data=Unit_H,
aes(x0 = X, y0 = Y, a = Diameter_E.W/2+250, b = Diameter_N.S/2+250, angle = 0),
lwd=0, col="darkgray", fill="gray", alpha=0.15) +
geom_ellipsis(data=Unit_H,
aes(x0 = X, y0 = Y, a = Diameter_E.W/2+120, b = Diameter_N.S/2+120, angle = 0),
lwd=0, col="darkgray", fill="gray", alpha=0.25) +
geom_ellipsis(data=Unit_H,
aes(x0 = X, y0 = Y, a = Diameter_E.W/2, b = Diameter_N.S/2, angle = 0),
lwd=0.5, col="darkgray", fill="gray", alpha=0.75) +
geom_point(data=Unit_H, aes(X, Y), size = 0.5) +
geom_point(data=Refits_H_trans, aes(x,y,group=sample, colour=factor(sample))) +
geom_line(data=Refits_H_trans, lwd=0.2, lty=1, aes(x,y, group=sample, colour=factor(sample))) +
coord_fixed() +
theme_bw() +
theme(legend.position="none") +
ggtitle("Unit H") +
xlim(-6600,-3800) +
ylim(400,2400)
As you can see, scales of the ggplot and the map don't match. So, my questions are:
How can I georeference the map with the values of the ggplot X and Y axes?
I need to keep the proportion of the image, in order not to distort it. How can I do it? I am asking this because if I change the xlim values, the image also change and its proportion changes.

I've recreated a simple example where this problem exists:
library(tidyverse)
# create the background
bck_square <- data.frame(x=c(1,1,0,0),y=c(0,1,1,0))
p <- ggplot(bck_square, aes(x=x, y=y)) +
geom_point(size=10, color="red") +
theme(panel.border=element_blank(),
panel.grid.major=element_blank(),
panel.grid.minor=element_blank(),
#panel.background=element_blank(), keep the background to see where image ends
axis.text=element_blank(),
axis.ticks=element_blank(),
axis.title=element_blank())
p
I will save the image to be used as a background for my figures:
ggsave("temp.png",p)
img <- readPNG("temp.png")
Setting the background using background_image from the ggpubr package, the old square does not line up with the new square, even though the data is the same. This is expected, as ggsave adds a thin border around the image
library(ggpubr)
ggplot(bck_square, aes(x, y)) +
background_image(img) +
geom_point()
However, by using annotation_custom instead (see this guide), you can adjust where the minimum and maximum for the image should be. Playing around with the border parameters I was able to get the image background and figure to line up.
library(png)
library(grid)
min_border <- .064
max_border <- .061
ggplot(bck_square, aes(x, y)) +
annotation_custom(g,xmin=-min_border, xmax=1+max_border, ymin=-min_border, ymax=1+max_border) +
geom_point()
This method should work with a tiff file. Another potential solution could be spatial data transformations using rspatial (see the documentation here) but this may over-complicate the issue.

Related

How to colour background on a scatterplot using ggplot but still show data points in R?

This is my first question here so hope this makes sense and thank you for your time in advance!
I am trying to generate a scatterplot with the data points being the log2 expression values of genes from 2 treatments from an RNA-Seq data set. With this code I have generated the plot below:
ggplot(control, aes(x=log2_iFGFR1_uninduced, y=log2_iFGFR4_uninduced)) +
geom_point(shape = 21, color = "black", fill = "gray70") +
ggtitle("Uninduced iFGFR1 vs Uninduced iFGFR4 ") +
xlab("Uninduced iFGFR1") +
ylab("Uninduced iFGFR4") +
scale_y_continuous(breaks = seq(-15,15,by = 1)) +
scale_x_continuous(breaks = seq(-15,15,by = 1)) +
geom_abline(intercept = 1, slope = 1, color="blue", size = 1) +
geom_abline(intercept = 0, slope = 1, colour = "black", size = 1) +
geom_abline(intercept = -1, slope = 1, colour = "red", size = 1) +
theme_classic() +
theme(plot.title = element_text(hjust=0.5))
Current scatterplot:
However, I would like to change the background of the plot below the red line to a lighter red and above the blue line to a lighter blue, but still being able to see the data points in these regions. I have tried so far by using polygons in the code below.
pol1 <- data.frame(x = c(-14, 15, 15), y = c(-15, -15, 14))
pol2 <- data.frame(x = c(-15, -15, 14), y = c(-14, 15, 15))
ggplot(control, aes(x=log2_iFGFR1_uninduced, y=log2_iFGFR4_uninduced)) +
geom_point(shape = 21, color = "black", fill = "gray70") +
ggtitle("Uninduced iFGFR1 vs Uninduced iFGFR4 ") +
xlab("Uninduced iFGFR1") +
ylab("Uninduced iFGFR4") +
scale_y_continuous(breaks = seq(-15,15,by = 1)) +
scale_x_continuous(breaks = seq(-15,15,by = 1)) +
geom_polygon(data = pol1, aes(x = x, y = y), color ="pink1") +
geom_polygon(data = pol2, aes(x = x, y = y), color ="powderblue") +
geom_abline(intercept = 1, slope = 1, color="blue", size = 1) +
geom_abline(intercept = 0, slope = 1, colour = "black", size = 1) +
geom_abline(intercept = -1, slope = 1, colour = "red", size = 1) +
theme_classic() +
theme(plot.title = element_text(hjust=0.5))
New scatterplot:
However, these polygons hide my data points in this area and I don't know how to keep the polygon color but see the data points as well. I have also tried adding "fill = NA" to the geom_polygon code but this makes the area white and only keeps a colored border. Also, these polygons shift my axis limits so how do I change the axes to begin at -15 and end at 15 rather than having that extra unwanted length?
Any help would be massively appreciated as I have struggled with this for a while now and asked friends and colleagues who were unable to help.
Thanks,
Liv
Your question has two parts, so I'll answer each in turn using a dummy dataset:
df <- data.frame(x=rnorm(20,5,1), y=rnorm(20,5,1))
Stop geom_polygon from hiding geom_point
Stefan had commented with the answer to this one. Here's an illustration. Order of operations matters in ggplot. The plot you create is a result of each geom (drawing operation) performed in sequence. In your case, you have geom_polygon after geom_point, so it means that it will plot on top of geom_point. To have the points plotted on top of the polygons, just have geom_point happen after geom_polygon. Here's an illustrative example:
p <- ggplot(df, aes(x,y)) + theme_bw()
p + geom_point() + xlim(0,10) + ylim(0,10)
Now if we add a geom_rect after, it hides the points:
p + geom_point() +
geom_rect(ymin=0, ymax=5, xmin=0, xmax=5, fill='lightblue') +
xlim(0,10) + ylim(0,10)
The way to prevent that is to just reverse the order of geom_point and geom_rect. It works this way for all geoms.
p + geom_rect(ymin=0, ymax=5, xmin=0, xmax=5, fill='lightblue') +
geom_point() +
xlim(0,10) + ylim(0,10)
Removing whitespace between the axis and limits of the axis
The second part of your question asks about how to remove the white space between the edges of your geom_polygon and the axes. Notice how I have been using xlim and ylim to set limits? It is a shortcut for scale_x_continuous(limits=...) and scale_y_continuous(limits=...); however, we can use the argument expand= within scale_... functions to set how far to "expand" the plot before reaching the axis. You can set the expand setting for upper and lower axis limits independently, which is why this argument expects a two-component number vector, similar to the limits= argument.
Here's how to remove that whitespace:
p + geom_rect(ymin=0, ymax=5, xmin=0, xmax=5, fill='lightblue') +
geom_point() +
scale_x_continuous(limits=c(0,10), expand=c(0,0)) +
scale_y_continuous(limits=c(0,10), expand=c(0,0))

Marginal plots using axis_canvas in cowplot: How to insert gap between main panel and marginal plots

The following came up in a comment to this post: When making marginal plots with the axis_canvas() function in cowplot, how can we create a gap between the main plot and the marginal plot?
Example code:
require(cowplot)
pmain <- ggplot(data = mpg, aes(x = cty, y = hwy, color = factor(cyl))) +
geom_point() +
xlab("City driving (miles/gallon)") +
ylab("Highway driving (miles/gallon)") +
theme_minimal()
xbox <- axis_canvas(pmain, axis = "x", coord_flip = TRUE) +
geom_boxplot(data = mpg, aes(y = cty, x = factor(cyl), color = factor(cyl))) +
scale_x_discrete() + coord_flip()
ybox <- axis_canvas(pmain, axis = "y") +
geom_boxplot(data = mpg, aes(y = hwy, x = factor(cyl), color = factor(cyl))) +
scale_x_discrete()
p1 <- insert_xaxis_grob(pmain, xbox, grid::unit(0.6, "in"), position = "top")
p2 <- insert_yaxis_grob(p1, ybox, grid::unit(0.6, "in"), position = "right")
ggdraw(p2)
As we can see in this example, the marginal boxplots directly touch the main plot panel. The goal is to generate some gap. How can this be done?
I see two options:
Insert empty plot
We can apply the insert_xaxis_grob() / insert_yaxis_grob() functions iteratively to insert multiple grobs, one of which can be empty. In this way, we can insert a specified amount of space on either side of the marginal plots. Here I'm showing how to do this on the inside, to generate a gap between the main panel and the marginal plots:
# pmain, xbox, ybox are defined as in the question
pnull <- ggdraw() # generate empty plot
p1 <- insert_xaxis_grob(
insert_xaxis_grob(pmain, xbox, grid::unit(0.6, "in"), position = "top"),
pnull, grid::unit(0.2, "in"), position = "top")
p2 <- insert_yaxis_grob(
insert_yaxis_grob(p1, ybox, grid::unit(0.6, "in"), position = "right"),
pnull, grid::unit(0.2, "in"), position = "right")
ggdraw(p2)
Create gap in the marginal plots
Alternatively, since the marginal plots are drawn with ggplot2, we can just specify axis limits that generate space in the appropriate location. I.e., instead of xbox and ybox in the original code, we define xbox2 and ybox2 via:
xbox2 <- axis_canvas(pmain, axis = "x", coord_flip = TRUE) +
geom_boxplot(data = mpg, aes(y = cty, x = as.numeric(factor(cyl)), color = factor(cyl))) +
scale_x_continuous(limits = c(-2, 4.5)) + coord_flip()
ybox2 <- axis_canvas(pmain, axis = "y") +
geom_boxplot(data = mpg, aes(y = hwy, x = as.numeric(factor(cyl)), color = factor(cyl))) +
scale_x_continuous(limits = c(-2, 4.5))
p1 <- insert_xaxis_grob(pmain, xbox2, grid::unit(0.8, "in"), position = "top")
p2 <- insert_yaxis_grob(p1, ybox2, grid::unit(0.8, "in"), position = "right")
ggdraw(p2)
To understand what is happening here, let's compare xbox and xbox2 side by side:
plot_grid(xbox + panel_border("black"),
xbox2 + panel_border("black"), nrow = 1, scale = 0.9)
We see how xbox2 (on the right) has extra space at the bottom, which was created by starting the axis at -2, even though the first box plot is located at position 1. More information on how to choose the axis ranges for these marginal plots can be found here.

Broken polygons in levelplot using ggplot stat_density_2d

Creating a levelplot using ggplot's stat_density_2d I get "broken" polygons. For example, the outer one in the example below.
How can I fix this, to get a smooth form?
set.seed(0)
n <- 50
d <- data.frame(x = rnorm(n, -.7, .5),
y = rnorm(n, 0, .8))
ggplot(d, aes(x, y)) +
geom_point() +
stat_density_2d(aes(fill = ..level..), alpha=.1, geom = "polygon")
To build on #hrbrmstr's answer (which, at least on my machine, lops off one data point because the x scale isn't sufficiently wide), a slightly more involved approach would be to get the limits of the data, set the scale limits, then reset the plot limits back to the original range:
g <- ggplot(d, aes(x, y)) +
geom_point() +
stat_density_2d(aes(fill = ..level..), alpha=.1, geom = "polygon")
dat_lims <- lapply(d, function(v) c(min(v), max(v)))
plot_lims <- ggplot_build(g)$panel$ranges[[1]][c("x.range", "y.range")]
g +
scale_x_continuous(limits = dat_lims$x * 1.1) +
scale_y_continuous(limits = dat_lims$y * 1.1) +
coord_cartesian(xlim = plot_lims$x.range, ylim = plot_lims$y.range)
Output:

box plot in R with additional point

I have a dataframe of multiple columns (let's say n) with different range and a vector of length n. I want different x-axis for each variable to be shown below each box plot. I tried facet_grid and facet_wrap but it gives common x axis.
This is what I have tried:
d <- data.frame(matrix(rnorm(10000), ncol = 20))
point_var <- rnorm(20)
plot.data <- gather(d, variable, value)
plot.data$test_data <- rep(point_var, each = nrow(d))
ggplot(plot.data, aes(x=variable, y=value)) +
geom_boxplot() +
geom_point(aes(x=factor(variable), y = test_data), color = "red") +
coord_flip() +
xlab("Variables") +
theme(legend.position="none")
If you can live with having the text of the x axis above the plot, and having the order of the graphs a bit messed-up this could work:
library(grid)
p = ggplot(plot.data, aes(x = 0, y=value)) +
geom_boxplot() +
geom_point(aes(x = 0, y = test_data), color = "red") +
facet_wrap(~variable, scales = "free_y", switch = "y") +
xlab("Variables") +
theme(legend.position="none") + theme_bw() + theme(axis.text.x=element_blank())
print(p, vp=viewport(angle=270, width = unit(.75, "npc"), height = unit(.75, "npc")))
I'm actually just creating the graph without flipping coords, so that scales = 'free_y' works, swithcing the position of the strip labels, and then rotating the graph.
If you don't like the text above graph (which is understandable), I would consider creating a list of single plots and then putting them together with grid.arrange.
HTH,
Lorenzo

ggplot ribbon cut off at y limits

I want to use geom_ribbon in ggplot2 to draw shaded confidence ranges. But if one of the lines goes outside the set y limits, the ribbon is cut off without extending to the edge of the plot.
Minimal example
x <- 0:100
y1 <- 10+x
y2 <- 50-x
ggplot() + theme_bw() +
scale_x_continuous(name = "x", limits = c(0,100)) +
scale_y_continuous(name = "y", limits = c(-20,100)) +
geom_ribbon(aes(x=x, ymin=y2-20, ymax=y2+20), alpha=0.2, fill="#009292") +
geom_line(aes(x=x , y=y1)) +
geom_line(aes(x=x , y=y2))
What I want is to reproduce the same behaviour as I get with plotting in base R, where the shading extends to the edge
plot(x, y1, type="l", xlim=c(0,100),ylim=c(-20,100))
lines(x,y2)
polygon(c(x,rev(x)), c(y2-20,rev(y2+20)), col="#00929233", border=NA)
The problem is that limits is removing all data which are not within its range.
What you want is to first plot and then zoom in. This can be done by using coord_cartesian.
ggplot() + theme_bw() +
geom_ribbon(aes(x = x, ymin = y2 - 20, ymax = y2 + 20), alpha = 0.2, fill = "#009292") +
geom_line(aes(x = x, y = y1)) +
geom_line(aes(x = x, y = y2)) +
coord_cartesian(ylim = c(-25, 100), xlim = c(0,100))

Resources