ggplot2 fill gaps by joining a line and no symbol - r

I would like to have a chart that connects missing points by a line, but not show a symbol at the missing point. Here is some code that creates some test data and produces a chart, but I would like a (id=B) green line joining points 4 and 6 with a straight line, but no green triangle at time 5. I did try geom_path instead of geom_line, but nothing much changed. Thanks
library(ggplot2)
set.seed(1)
x <- rep(seq(1:10), 3)
y <- c(runif(10), runif(10) + 1, runif(10) + 2)
group <- c(rep("A", 10), rep("B", 10), rep("C", 10))
df <- data.frame(cbind(group, x, y))
df$x <- as.numeric(df$x)
df$y <- as.numeric(df$y)
df[15,]$y <- NA
ggplot(data=df, aes(x=x, y=y, group=group)) +
geom_line(aes(colour=group))+
geom_point(aes(colour=group, shape=group))+
scale_shape_manual(values = c(1:3)) +
theme(legend.position="bottom")

You can do this by removing rows where y is NA:
df2 <- df[!is.na(df$y), ]
ggplot(data=df2, aes(x=x, y=y, group=group)) +
geom_line(aes(colour=group))+
geom_point(aes(colour=group, shape=group))+
scale_shape_manual(values = c(1:3)) +
theme(legend.position="bottom")

library(tidyverse)
# form some data
set.seed(1)
x <- rep(seq(1:10), 3)
y <- c(runif(10), runif(10) + 1, runif(10) + 2)
group <- c(rep("A", 10), rep("B", 10), rep("C", 10))
df <- tibble(group=group, x = x, y = y)
# add an NA
df[15,]$y <- NA
# plot with a gap where the NA is
p1 <- # p1 fails because of NA
ggplot(data=df, aes(x=x, y=y, group=group)) +
geom_line(aes(colour=group))+
geom_point(aes(colour=group, shape=group))+
scale_shape_manual(values = c(1:3)) +
theme(legend.position="bottom")
# plot with no gap
p2 <- p1 %+% {df %>% na.omit()} # p2 does what you want
p1
p2

Related

Using cowplot in R to make a ggplot chart occupy two consecutive rows

This is my code:
library(ggplot2)
library(cowplot)
df <- data.frame(
x = 1:10, y1 = 1:10, y2 = (1:10)^2, y3 = (1:10)^3, y4 = (1:10)^4
)
p1 <- ggplot(df, aes(x, y1)) + geom_point()
p2 <- ggplot(df, aes(x, y2)) + geom_point()
p3 <- ggplot(df, aes(x, y3)) + geom_point()
p4 <- ggplot(df, aes(x, y4)) + geom_point()
p5 <- ggplot(df, aes(x, y3)) + geom_point()
# simple grid
plot_grid(p1, p2,
p3, p4,
p5, p4)
But I don't want to repeat p4 I want to "stretch" p4 to occupy col2 and rows 2 and 3.
Any help?
You may find this easier using gridExtra::grid.arrange().
library(gridExtra)
grid.arrange(p1, p2, p3, p4, p5,
ncol = 2,
layout_matrix = cbind(c(1,3,5), c(2,4,4)))
Result:
This is fairly straight forward with the patchwork package.
Also, never forget about the facet option - for this you'll need the ggh4x package.
Last, also the desired cowplot solution, which requires a convoluted nesting of several plot_grid objects. Not my favourite.
## Option 1 - patchwork
library(ggplot2)
library(patchwork)
df <- data.frame(
x = 1:10, y1 = 1:10, y2 = (1:10)^2, y3 = (1:10)^3, y4 = (1:10)^4
)
## patchwork allows working with lists, which I find neat.
make_p <- function(y){
ggplot(df, aes(x, !!sym(y))) + geom_point()
}
## custom layout grid
layout <- "
AB
CD
ED
"
ls_p <- lapply(paste0("y", c(1:4,3)), make_p)
wrap_plots(ls_p) + plot_layout(design = layout)
Another option, in your particular example, is to make use of ggh4x::facet_manual.
## Option 2 - faceting with ggh4x
library(tidyverse)
library(ggh4x)
df <- data.frame(
x = 1:10, y1 = 1:10, y2 = (1:10)^2, y3 = (1:10)^3, y4 = (1:10)^4,
## adding y5 for simplicity
y5 = (1:10)^3
)
design <- "
AB
CD
ED
"
## or you can pass a matrix as design argument
# design <- matrix(c(1,2,3,4,5,4), 3, 2, byrow = TRUE)
df %>%
pivot_longer(matches("^y")) %>%
ggplot(aes(x, value)) +
geom_point() +
facet_manual(~ name, design)
Last, the cowplot option.
## option 3 nesting plot_grids with cowplot
library(cowplot)
p1 <- ggplot(df, aes(x, y1)) + geom_point()
p2 <- ggplot(df, aes(x, y2)) + geom_point()
p3 <- ggplot(df, aes(x, y3)) + geom_point()
p4 <- ggplot(df, aes(x, y4)) + geom_point()
p5 <- ggplot(df, aes(x, y3)) + geom_point()
top_row <- plot_grid(p1, p2)
left_col <- plot_grid(p3, p5, ncol = 1)
bottom_panel <- plot_grid(left_col, p4, ncol = 2)
plot_grid(top_row, bottom_panel, ncol = 1)

In R, how do you stack figures made using ggplot2 on top of each other?

I have made two figures in ggplot that I now want to stack on top of each other. I can get them to stack using grid.arrange(p1, p2, ncol =1) function in the gridExtra package however, both figures get horizontally stretched out becoming the shape of rectangles. Any idea on how to keep both figures square (x and y-axes the same overall length).
library(ggplot2)
library(gridExtra)
x1 <- rnorm(20)
y1 <- rnorm(20)
x2 <- rnorm(20)
y2 <- rnorm(20)
dat1 <- data.frame(x1, y1)
dat2 <- data.frame(x2, y2)
p1 <- ggplot(data = dat1, aes(x=x1, y=y1)) + geom_point()
p2 <- ggplot(data = dat2, aes(x=x2, y=y2)) + geom_point()
grid.arrange(p1,p2, ncol=1)
I have tried adjusting the width by using the widths argument but I keep getting the error message Error in arrangeGrob(...) : length(widths) == ncol is not TRUE.
grid.arrange(p1,p2, ncol=1, widths = c(1,1))
The patchwork package is built for this.
Plot1 + Plot2 # side by side
Plot1/Plot2 # top over bottom
I'm a big fan of patchwork for arranging plots. You get square plots by setting the dimensions as you export the figure.
library(patchwork)
p3 <- p1/p2 + plot_layout(ncol = 1, heights = c(1,1))
ggsave("test.png", p3, width = 10, height = 20, units = c("cm"))
Thanks for the responses but I actually figured it out using the packages I mentioned. What I had to do was add theme(aspect.ratio = 1) to both figures.
p1 <- ggplot(data = dat1, aes(x=x1, y=y1)) + geom_point() + theme(aspect.ratio = 1)
p2 <- ggplot(data = dat2, aes(x=x2, y=y2)) + geom_point() + theme(aspect.ratio = 1)
grid.arrange(p1,p2, ncol=1)
How about this:
library(ggplot2)
library(gridExtra)
x1 <- rnorm(20)
y1 <- rnorm(20)
x2 <- rnorm(20)
y2 <- rnorm(20)
dat1 <- data.frame(x1, y1)
dat2 <- data.frame(x2, y2)
p1 <- ggplot(data = dat1, aes(x=x1, y=y1)) + geom_point()
p2 <- ggplot(data = dat2, aes(x=x2, y=y2)) + geom_point()
grid.arrange(p1,p2, ncol=1, widths = unit(10, c("cm")), heights = c(unit(10, c("cm")), unit(10, c("cm"))))

Vertically align faceted ggplots of different heights when using coord_equal()

I am trying to combine two FACETED ggplot objects with coord_equal() using cowplot::plot_grid() or egg::ggarrange() and vertically align them.
The egg::ggarrange() approach works fine for UNFACETED plots, with the solution posted here.
However, the egg::ggarrange() solution breaks down when faceting is included. The plots are correctly aligned, but the units of the y-axes are twice as large as those of the x-axes. Any suggestions for how to generalize this for faceting?
dat1 <- data.frame(x = rep(1:10, 2), y = 1:20, z = rep(c("A", "B"), 10))
dat2 <- data.frame(x = 1:10, y = 1:10, z = rep(c("A", "B"), 5))
plot1 <- ggplot(dat1, aes(x=x, y=y)) +
geom_point() + coord_equal() + facet_wrap(~z)
plot2 <- ggplot(dat2, aes(x=x, y=y)) +
geom_point() + coord_equal() + facet_wrap(~z)
egg::ggarrange(plot1, plot2, ncol = 1)
it seems to be a simple fix,
library(egg)
b <- body(gtable_frame)
b[6] <- parse(text="if (fixed_ar) {
ar <- as.numeric(g$heights[tt[1]]) / as.numeric(g$widths[ll[1]])
height <- width * (ar / length(ll))
g$respect <- FALSE
}")
body(gtable_frame) <- b
assignInNamespace("gtable_frame", gtable_frame, ns = 'egg')
The main problem is that plot1 and plot2 have different aspect ratios.
This is plot1:
And this plot2:
You can try to keep the aspect ratio using, i.e. theme(aspect.ratio=1) instead of coord_equal():
require(ggplot2)
dat1 <- data.frame(x = rep(1:10, 2), y = 1:20, z = rep(c("A", "B"), 10))
dat2 <- data.frame(x = 1:10, y = 1:10, z = rep(c("A", "B"), 5))
plot1 <- ggplot(dat1, aes(x=x, y=y)) + geom_point() + theme(aspect.ratio=1)+
facet_wrap(~z)
plot2 <- ggplot(dat2, aes(x=x, y=y)) + geom_point() + theme(aspect.ratio=1)+
facet_wrap(~z)
egg::ggarrange(plot1, plot2, ncol = 1,heights = c(1,10))
Hope it serves.

Plot geom_scatterpie on a geom_tile plot

I want to plot pie charts using geom_scatterpie on top of a geom_tile plot. However, I am getting an error:
Error: Discrete value supplied to continuous scale
Here's the simple code that I cannot get to work:
library(ggplot2)
library(scatterpie)
nasafile <- "http://eosweb.larc.nasa.gov/sse/global/text/global_radiation"
nasa <- read.table(file=nasafile, skip=13, header=TRUE)
p <- ggplot(aes(y = Lat , x = Lon), data = nasa )+
geom_tile(aes(fill=Ann)) +
scale_fill_gradientn(colours=brewer.pal('YlOrRd', n=9)) +
theme_bw() +
coord_equal()
plot(p)
This works, but if I add the geom_scatterpie on top of that:
First the data for the pie charts to plot:
d <- data.frame(x=rnorm(5), y=rnorm(5))
d$A <- abs(rnorm(5, sd=1))
d$B <- abs(rnorm(5, sd=2))
d$C <- abs(rnorm(5, sd=3))
But I get the error when I do this:
p + geom_scatterpie(aes(x=x, y=y), data=d, cols=c("A", "B", "C")) + coord_fixed()
The problem is that your geom_tile uses a continuous fill scale while geom_scatterpie uses a discrete fill scale. It works if you change Ann to a factor. Not ideal, but this works:
nasa$Ann <- as.factor(as.integer(nasa$Ann))
mypalette <- brewer.pal(9, "YlOrRd") # 6 for geom_tile, 3 for geom_scatterpie
p <- ggplot(aes(y = Lat , x = Lon), data = nasa )+
geom_tile(aes(fill=Ann)) +
scale_fill_manual(values = mypalette) +
theme_bw() +
coord_equal()
p
d <- data.frame(x=rnorm(5, 0, 50), y=rnorm(5, 0, 30)) # larger sd
d$A <- abs(rnorm(5, sd=1))
d$B <- abs(rnorm(5, sd=2))
d$C <- abs(rnorm(5, sd=3))
p + geom_scatterpie(aes(x=x, y=y, r = 20), data=d, cols=c("A", "B", "C")) #larger radius
Or, using, size= instead of fill= (and no geom_scatterpie):
p <- ggplot(aes(y = Lat , x = Lon), data = nasa )+
geom_tile(aes(fill=Ann)) +
scale_fill_gradientn(colours=brewer.pal('YlOrRd', n=9)) +
theme_bw() +
coord_equal()
p
d <- data.frame(Lon = c(-100, 0, 100),
Lat = c(-50, 0, 50),
genvar = c(.1, .3, .5))
p + geom_point(data = d, aes(x = Lon, y = Lat, size = genvar),
color = "white")

Is it possible to force a scale on geom_raster()?

I am trying to compare three plots using geom_raster(). The problem is that I would like to maintain the same scale in all three plots that was set in the first plot [-3,3].
Here is my code:
#raster plots
box <- .05
df <- expand.grid(x1 = seq(-1, 1, box), x2 = seq(-1, 1, box))
df$risk <- df$x1 + 2*df$x2
p1 <- ggplot(df, aes(x1, x2, fill = risk)) + geom_raster() +
scale_fill_gradientn(colours=c("#FFFFFF","#046380","#000000")) +
theme_minimal() +
ggtitle("True Risk")
df <- expand.grid(x1 = seq(-1, 1, box), x2 = seq(-1, 1, box))
df$risk <- .99*df$x1 + 1.98*df$x2
p2 <- ggplot(df, aes(x1, x2, fill = risk)) + geom_raster() +
scale_fill_gradientn(colours=c("#FFFFFF","#046380","#000000")) +
theme_minimal() +
ggtitle("Estimated Risk")
df <- expand.grid(x1 = seq(-1, 1, box), x2 = seq(-1, 1, box))
df$risk <- .01*df$x1 + .02*df$x2
p3 <- ggplot(df, aes(x1, x2, fill = risk)) + geom_raster() +
scale_fill_gradientn(colours=c("#FFFFFF","#046380","#000000")) +
theme_minimal() +
ggtitle("Difference")
library(gridExtra)
grid.arrange(p1, p2, p3, ncol=1)
This is my output
It is hard to see that the difference is minimal because the scale changes to [0.03, -0.03]. How can I show the correct surface, but on the original scale?
You can keep all the three variables in one data.frame and use facet_grid or facet_wrap to maintain the scale.
library(ggplot2)
library(reshape2)
box <- .05
df <- expand.grid(x1 = seq(-1, 1, box), x2 = seq(-1, 1, box))
# Calculate each field
df$TrueRisk <- df$x1 + 2*df$x2
df$EstimatedRisk <- .99*df$x1 + 1.98*df$x2
df$Difference <- .01*df$x1 + .02*df$x2
# Transform the data into long format for ggplot2
df <- melt(df, c("x1", "x2"))
# Use facet_grid/facet_wrap to create the plot
ggplot(df, aes(x1, x2, fill = value)) + geom_raster() +
facet_grid(variable ~ .) +
scale_fill_gradientn(colours=c("#FFFFFF","#046380","#000000")) +
theme_minimal() +
ggtitle("Risk")

Resources