How can I display two plots in one row with R function ggarrange() so that they have the same dimensions, in particular the same height?
In this example, the second plot is a bit higher than the first plot. I would like to increase the size of a1_plot, so that it matches the size of a2_plot.
# required packages
library(ggplot2)
library(ggbreak)
library(directlabels)
library(ggpubr)
# make dataframe
df1 <- data.frame(first_column=c("value_1","value_2","value_3","value_4","value_4","value_5"),
second_column=c("123","123","325","325","656","656"),
third_column=c(12,13,1,19,200,360),
fourth_column=c(1,124,155,3533,5533,6666))
# plot 1
a1_plot <-
ggplot(df1, aes(x=third_column, y=fourth_column, colour=second_column)) +
scale_x_continuous(breaks = c(0,50,100,150,200,250,300)) +
ylab("Fourth column")+ xlab("Third column") +
scale_x_break(breaks = c(210,400)) +
geom_dl(mapping=aes(x=third_column, y=fourth_column, label=second_column),
method = list(dl.trans(x = x + 0.1), dl.combine("last.points"))) +
theme(legend.position = "none")
# plot 2
a2_plot <-
ggplot(data=df1)+
geom_point(aes(x=second_column, y=fourth_column) +
xlab("X axis")+ ylab("Y axis") +
theme(legend.position = "none")
# merge plot1 and plot2
ggarrange(print(a1_plot), print(a2_plot), labels = c('a1', 'a2'))
I was unable to change the height of plot 1. By adjusting the margins of plot 2, the problem has been solved.
theme(legend.position = "none", plot.margin = unit(x=c(3.6,5,3.9,0), units = "mm"))
Its looks like that ggtern has not been synchronised with new version of ggplot2.
Therefore we can not use ggtern.
library(ggtern)
set.seed(1)
plot <- ggtern(data = data.frame(x = runif(100),
y = runif(100),
z = runif(100)),
aes(x, y, z))
plot + stat_density_tern(geom = 'polygon',
n = 200,
aes(fill = ..level..,
alpha = ..level..)) +
geom_point() +
theme_rgbw() +
labs(title = "Example Density/Contour Plot") +
scale_fill_gradient(low = "blue",high = "red") +
guides(color = "none", fill = "none", alpha = "none")
Error: geom_point requires the following missing aesthetics: x and y
Does anyone have in find other options for ternary diagrams apart from ggtern in R?
Manually, you could plot the points with a function: (I used the formulas at https://en.wikipedia.org/wiki/Ternary_plot)
I'm not familiar with the output of stat_density_tern so I'm not sure what is expected from that part.
library(tidyverse)
tern <- function(df) {
df %>% mutate(x_pos = 0.5 * (2*y + z) / (x+y+z),
y_pos = sqrt(3) / 2 * z / (x+y+z))
}
tern(plot) %>%
ggplot(aes(x_pos, y_pos)) +
geom_point() +
annotate("path", x = c(0, 0.5, 1, 0), y = c(0,sqrt(3)/2,0,0)) +
coord_equal()
this worked for me! Uninstall ggtern and ggplot2 then
install_version("ggplot2", version = "3.3.0", repos = "http://cran.us.r-project.org")
install.packages("ggtern")
library(ggtern)
I use the following script which also supports making diagrams with 4 or more corners. It also divides the points into colored clusters by cutting a hierarchical clustering at the height where it has 32 subtrees, and it draws a line from each point to its two nearest neighbors.
library(tidyverse)
library(ggforce)
library(colorspace)
t=as.matrix(read.csv("https://pastebin.com/raw/1EDJJtHU",row.names=1,check.names=F))/100
fst=as.matrix(read.csv("https://pastebin.com/raw/6JmN2hRY",row.names=1))
mult=t%*%cmdscale(fst,ncol(fst)-1)
# t=cbind(t[,2]+t[,1],t[,8]+t[,9],rowSums(t[,-c(1,2,8,9)]))
# colnames(t)=c("Baltic + North_Atlantic","Siberian + East_Asian","Other")
# t=cbind(t[,2],t[,1],t[,8]+t[,9],rowSums(t[,-c(1,2,8,9)]))
# colnames(t)=c("Baltic","North_Atlantic","Siberian + East_Asian","Other")
ncorn=ncol(t)
start=ifelse(ncorn==4,.25,0)
corners=sapply(c(sin,cos),\(x)x((start+seq(0,2,,ncorn+1)[-(ncorn+1)])*pi))
corners=corners*min(2/diff(apply(corners,2,range)))
corners[,2]=corners[,2]-mean(range(corners[,2]))
xy=t%*%corners
grid=if(ncorn==3)do.call(rbind.data.frame,apply(simplify=F,rbind(c(1,2,3,2),c(1,3,2,3),c(2,1,3,1)),1,\(x)cbind(
seq(corners[x[1],1],corners[x[2],1],,11),
seq(corners[x[1],2],corners[x[2],2],,11),
seq(corners[x[3],1],corners[x[4],1],,11),
seq(corners[x[3],2],corners[x[4],2],,11)
)))else if(ncorn==4)do.call(rbind.data.frame,apply(simplify=F,rbind(c(1,2,4,3),c(1,4,2,3)),1,\(x)cbind(
seq(corners[x[1],1],corners[x[2],1],,11),
seq(corners[x[1],2],corners[x[2],2],,11),
seq(corners[x[3],1],corners[x[4],1],,11),
seq(corners[x[3],2],corners[x[4],2],,11)
)))else rbind.data.frame(cbind(corners,rbind(corners[-1,],corners[1,])),cbind(corners,matrix(colMeans(corners),ncorn,2,T)))
seg=as.data.frame(cbind(xy[rep(1:nrow(xy),each=2),],xy[apply(as.matrix(dist(mult)),1,\(x)order(x)[2:3]),]))
k=as.factor(cutree(hclust(dist(mult)),32))
set.seed(0)
hue=seq(0,360,,nlevels(k)+1)%>%head(-1)%>%sample()
pal1=hex(colorspace::HSV(hue,.6,1))
pal2=hex(colorspace::HSV(hue,.3,1))
angle=head(seq(360,0,length.out=ncorn+1),-1)
angle=ifelse(angle>90&angle<=270,angle+180,angle)
ggplot(as.data.frame(xy),aes(x=V1,y=V2))+
geom_polygon(data=as.data.frame(corners),fill="gray25")+
(if(ncorn>=5)geom_text(data=as.data.frame(corners),aes(x=1.04*V1,y=1.04*V2),label=colnames(t),size=3.2,angle=angle,color="gray85") # use rotated labels
else geom_text(data=as.data.frame(corners),aes(x=V1,y=1.03*V2),vjust=(1-corners[,2])/2,hjust=(1+corners[,1])/2,label=colnames(t),size=3.2,color="gray85"))+ # don't rotate labels
geom_segment(data=grid,aes(x=V1,y=V2,xend=V3,yend=V4),color="gray30",size=.4)+
ggforce::geom_mark_hull(aes(group=!!k,color=!!k,fill=!!k),concavity=1000,radius=unit(.15,"cm"),expand=unit(.15,"cm"),alpha=.15,size=.1)+
geom_segment(data=seg,aes(x=V1,y=V2,xend=V3,yend=V4),color="gray10",size=.25)+
geom_point(aes(color=k),size=.5)+
geom_text(aes(label=rownames(xy),color=!!k),size=2.2,vjust=-.6)+
coord_fixed(xlim=c(-1,1),ylim=c(-1,1))+
scale_fill_manual(values=pal1)+
scale_color_manual(values=pal2)+
theme(
axis.text=element_blank(),
axis.ticks=element_blank(),
axis.title=element_blank(),
legend.position="none",
panel.background=element_rect(fill="gray20"),
panel.grid=element_blank(),
plot.background=element_rect(fill="gray20",color=NA,size=0),
plot.margin=margin(0,0,0,0)
)
ggsave("1.png",width=7,height=7)
Or here's another version that uses Voronoi tesselation to plot the points (https://ggforce.data-imaginist.com/reference/geom_delvor.html):
t=read.table("https://pastebin.com/raw/CeLAEiAq")
t=distinct(t[,-1]) # geom_voronoi_tile doesn't handle a large number of overlapping points
pop=t[,1]
t=as.matrix(t[,-1])
ncorn=ncol(t)
start=ifelse(ncorn==4,.25,0)
corners=sapply(c(sin,cos),\(x)x((start+seq(0,2,,ncorn+1)[-(ncorn+1)])*pi))
corners=corners*min(2/diff(apply(corners,2,range))) # resize so bigger one of width and height is 2
corners[,2]=corners[,2]-mean(range(corners[,2])) # center vertically
xy=as.data.frame(t%*%corners)
# # use a simple grid with line from each corner to center for a plot with more than 3 corners
# grid=rbind.data.frame(cbind(corners,rbind(corners[-1,],corners[1,])),cbind(corners,matrix(colMeans(corners),ncorn,2,T)))
# use a grid with 10 subdivisions per side for a triangle plot
grid=do.call(rbind.data.frame,apply(simplify=F,rbind(c(1,2,3,2),c(1,3,2,3),c(2,1,3,1)),1,\(x)cbind(
seq(corners[x[1],1],corners[x[2],1],,11),
seq(corners[x[1],2],corners[x[2],2],,11),
seq(corners[x[3],1],corners[x[4],1],,11),
seq(corners[x[3],2],corners[x[4],2],,11)
)))
centers=data.frame(aggregate(xy,list(pop),mean),row.names=1)
set.seed(0)
color=as.factor(sample(length(unique(pop))))
cl=rbind(c(60,80),c(25,95),c(30,70),c(70,50),c(60,100),c(20,50),c(15,40))
hues=max(ceiling(length(color)/nrow(cl)),8)
pal1=as.vector(apply(cl,1,\(x)hcl(seq(15,375,,hues+1)[-(hues+1)],x[1],x[2])))
pal2=as.vector(apply(cl,1,\(x)hcl(seq(15,375,,hues+1)[-(hues+1)],if(x[2]>=60).5*x[1]else .1*x[1],if(x[2]>=60).2*x[2]else 95)))
xy=xy+runif(nrow(xy)*2)/1e3 # add a small random factor to prevent errors because of overlapping points
ggplot(xy,aes(V1,V2))+
geom_segment(data=grid,aes(V1,V2,xend=V3,yend=V4),color="gray85",size=.3)+
ggforce::geom_voronoi_tile(aes(group=0,fill=color[as.factor(pop)],color=color[as.factor(pop)]),size=.07,max.radius=.055)+ # `group=0` is just an arbitrary constant
# ggrepel::geom_label_repel(data=centers,aes(V1,V2,color=color,fill=color),label=rownames(centers),max.overlaps=Inf,point.size=0,size=2.3,alpha=.8,label.r=unit(.1,"lines"),label.padding=unit(.1,"lines"),label.size=.1,box.padding=0,segment.size=.3)+
geom_label(data=centers,aes(V1,V2,color=color,fill=color),label=rownames(centers),size=2.3,alpha=.8,label.r=unit(.1,"lines"),label.padding=unit(.1,"lines"),label.size=.1)+
coord_fixed(xlim=c(-1.08,1.08),ylim=c(-1.08,1.08),expand=F)+
scale_fill_manual(values=pal1)+
scale_color_manual(values=pal2)+
theme(
axis.text=element_blank(),
axis.ticks=element_blank(),
axis.title=element_blank(),
legend.position="none",
panel.background=element_rect(fill="white")
)
ggsave("1.png",width=7,height=7)
Is it possible to get these two maps side by side, with the same height, in one png image? The two images should be separated by minimal but appropriate space, as shown below. I am receptive to other solutions, especially ggmap.
I tried par(mar=... (and also mai) to reduce margin size but that did not seem to affect size or space between the two maps. I also used cex = 1.8 in the second par() function (for the state) which makes the height of the two maps similar but spaces them even farther apart.
When I save the file as PDF, I get each map on a separate page. When I try png, I get only the Missouri map.
MWE:
library(maps)
op <- par(mfrow=c(1,2))
png(file = "maps.png", width = 1000, height = 400)
par(mar=c(0,0,0,0))
map('state')
map('state', 'missouri', add = TRUE, fill = TRUE)
map('state', c('mississippi', 'alabama', 'north carolina', 'florida'), add = TRUE, fill = TRUE, col = "gray")
par(mar=c(0,0,0,0))
map('county', 'missouri')
map('county', 'missouri,scott', add=TRUE, fill=TRUE)
dev.off()
par(op)
Desired result:
The par options are specific to the active "graphic device" at that moment. To demonstrate:
Try this, starting with "normal" (non-file) graphics.
par(mfrow=1:2)
par('mfrow')
# [1] 1 2
png("maps.png")
par('mfrow')
# [1] 1 1
dev.off()
# windows
# 2
par('mfrow')
# [1] 1 2
I didn't close the previous plain-graphics-window, so once I closed the png device, the previously-active window became active again. And it was still thinking mfrow=1:2.
So I think your answer is this, where the only change is the order of png, par(mfrow=1:2).
png(file = "maps.png", width = 1000, height = 400)
op <- par(mfrow=c(1,2))
par(mar=c(0,0,0,0))
map('state')
map('state', 'missouri', add = TRUE, fill = TRUE)
map('state', c('mississippi', 'alabama', 'north carolina', 'florida'), add = TRUE, fill = TRUE, col = "gray")
par(mar=c(0,0,0,0))
map('county', 'missouri')
map('county', 'missouri,scott', add=TRUE, fill=TRUE)
dev.off()
par(op)
Inspired by #Andres comment, I developed a ggplot2 version that places the two maps side by side using patchwork. I found it easier for me to use geom_polygon instead of converting the maps to sf objects.
library(maptools)
library(ggplot2)
library(ggthemes) # for Tufte theme
library(patchwork)
# remove the unneeded ink not removed by
# theme_tufte()
theme_tufte_empty <- function(){
theme(axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks.length = unit(0, "cm"))
}
usa <- map_data("state")
us_missouri <- map_data('state','missouri') #do similar for other states
us_map <- ggplot() +
geom_polygon(data = usa, aes(x=long, y = lat, group = group), fill = NA, color = "black") +
geom_polygon(data = us_missouri, aes(x = long, y = lat, group = group), fill = "black") +
theme_tufte() + # quickly remove most ink
theme_tufte_empty() +
coord_fixed(1.3)
missouri <- map_data("county", "missouri")
mo_scott <- map_data("county", "missouri,scott")
mo_map <- ggplot() +
geom_polygon(data = missouri, aes(x=long, y = lat, group = group), fill = NA, color = "black") +
geom_polygon(data = mo_scott, aes(x = long, y = lat, group = group), fill = "black") +
theme_tufte() +
theme_tufte_empty() +
coord_fixed(1.3)
us_map + mo_map + plot_layout(ncol = 2, widths = c(1.5,1))
How to add image logo outside the plotting areas for ggplot2. Tried rasterGrob function from 'grid' package, but that keep's the image inside plot area.
Here is the starter script:
library(ggplot2)
library(png)
library(gridExtra)
library(grid)
gg <- ggplot(df1, aes(x = mpg, y = wt)) +
theme_minimal() +
geom_count() +
labs(title = "Title Goes Here", x = "", y = "")
img <- readPNG("fig/logo.png")
Here is the outcome I am looking for.
I can add the annotation on the right side, but the logo on the left is where I am getting challenged.
You can add the elements with annotation_custom but you need to turn off clipping for the images to show up when they're outside the plot area. I've changed your example slightly in order to make it reproducible.
library(ggplot2)
library(png)
library(gridExtra)
library(grid)
gg <- ggplot(mtcars, aes(x = mpg, y = wt)) +
theme_minimal() +
geom_count() +
labs(title = "Title Goes Here", x = "", y = "")
img = readPNG(system.file("img", "Rlogo.png", package="png"))
gg = gg +
annotation_custom(rasterGrob(img),
xmin=0.95*min(mtcars$mpg)-1, xmax=0.95*min(mtcars$mpg)+1,
ymin=0.62*min(mtcars$wt)-0.5, ymax=0.62*min(mtcars$wt)+0.5) +
annotation_custom(textGrob("Footer goes here", gp=gpar(col="blue")),
xmin=max(mtcars$mpg), xmax=max(mtcars$mpg),
ymin=0.6*min(mtcars$wt), ymax=0.6*min(mtcars$wt)) +
theme(plot.margin=margin(5,5,30,5))
# Turn off clipping
gt <- ggplot_gtable(ggplot_build(gg))
gt$layout$clip[gt$layout$name=="panel"] <- "off"
grid.draw(gt)
Another option is to use ggplot's caption feature to add the text footer, which saves some code:
gg = gg +
annotation_custom(rasterGrob(img),
xmin=0.95*min(mtcars$mpg)-1, xmax=0.95*min(mtcars$mpg)+1,
ymin=0.62*min(mtcars$wt)-0.5, ymax=0.62*min(mtcars$wt)+0.5) +
labs(caption="Footer goes here") +
theme(plot.margin=margin(5,5,15,5),
plot.caption=element_text(colour="blue", hjust=1.05, size=15))
# Turn off clipping
gt <- ggplot_gtable(ggplot_build(gg))
gt$layout$clip[gt$layout$name=="panel"] <- "off"
grid.draw(gt)
Jut adding an updated method from the terrific package Magick:
library(ggplot2)
library(magick)
library(here) # For making the script run without a wd
library(magrittr) # For piping the logo
# Make a simple plot and save it
ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point() +
ggtitle("Cars") +
ggsave(filename = paste0(here("/"), last_plot()$labels$title, ".png"),
width = 5, height = 4, dpi = 300)
# Call back the plot
plot <- image_read(paste0(here("/"), "Cars.png"))
# And bring in a logo
logo_raw <- image_read("http://hexb.in/hexagons/ggplot2.png")
# Scale down the logo and give it a border and annotation
# This is the cool part because you can do a lot to the image/logo before adding it
logo <- logo_raw %>%
image_scale("100") %>%
image_background("grey", flatten = TRUE) %>%
image_border("grey", "600x10") %>%
image_annotate("Powered By R", color = "white", size = 30,
location = "+10+50", gravity = "northeast")
# Stack them on top of each other
final_plot <- image_append(image_scale(c(plot, logo), "500"), stack = TRUE)
# And overwrite the plot without a logo
image_write(final_plot, paste0(here("/"), last_plot()$labels$title, ".png"))
I'm trying to figure out how to display my complete map in gglot2 including the island Both r_base and tmap were able to display the islands but ggplot2 couldn't differentiate the island from the rest of the waterbody...
.
My question is how to make the Islands appear in ggplot2?
See the code i used below.
library(ggplot2)
library (rgdal)
library (rgeos)
library(maptools)
library(tmap)
Loading the Persian Gulf shape fill referred to as iho
PG <- readShapePoly("iho.shp")
the shape file is available here
http://geo.vliz.be:80/geoserver/wfs?request=getfeature&service=wfs&version=1.0.0&typename=MarineRegions:iho&outputformat=SHAPE-ZIP&filter=%3CPropertyIsEqualTo%3E%3CPropertyName%3Eid%3C%2FPropertyName%3E%3CLiteral%3E41%3C%2FLiteral%3E%3C%2FPropertyIsEqualTo%3E
plot with r_base
Q<-plot(PG)
Corresponds to figure A
Ploting with tmap
qtm(PG)
Corresponds to figure B
convert to dataframe
AG <- fortify(PG)
Plot with ggplot2
ggplot()+ geom_polygon(data=AG, aes(long, lat, group = group),
colour = alpha("darkred", 1/2), size = 0.7, fill = 'skyblue', alpha = .3)
Corresponds to figure C
You need to tell ggplot you want the holes filled in with a different color..for example:
ggplot()+ geom_polygon(data=AG, aes(long, lat, group = group, fill = hole), colour = alpha("darkred", 1/2), size = 0.7) + scale_fill_manual(values = c("skyblue", "white")) + theme(legend.position="none")
Also try readOGR() function from the rgdal package instead of readShapePoly() it keeps all the projection and datum information when you read the shape file.
Further to #AdamMccurdy's answer:, there are some possibilities to get the same colour for islands and adjacent background.
The first sets the fill colour of the islands and the colour of the background to be the same. But the grid lines are under the polygon, and thus disappear.
The second is an attempt to get the grid lines back. It plots the background (which includes the grid lines) on top of the panel (using panel.ontop = TRUE). But it's a bit of a fiddle adjusting alpha values to get the same background and island colour.
The third sets the background and island colours to be the same (as in the first), then plots the grid lines on top of the panel. There's a couple of ways to do this; here, I grab the grid lines grob from the original plot, then draw them on top of the panel. Thus the colours remain the same, and no need for alpha transparencies.
library(ggplot2)
library (rgdal)
library (rgeos)
library(maptools)
PG <- readOGR("iho.shp", layer = "iho")
AG <- fortify(PG)
Method 1
bg = "grey92"
ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", bg)) +
theme(panel.background = element_rect(fill = bg),
legend.position = "none")
Method 2
ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", "grey97")) +
theme(panel.background = element_rect(fill = alpha("grey85", .5)),
panel.ontop = TRUE,
legend.position = "none")
Method 3
Minor edit updating to ggplot version 3.0.0
library(grid)
bg <- "grey92"
p <- ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", bg)) +
theme(panel.background = element_rect(fill = bg),
legend.position = "none")
# Get the ggplot grob
g <- ggplotGrob(p)
# Get the Grid lines
grill <- g[7,5]$grobs[[1]]$children[[1]]
# grill includes the grey background. Remove it.
grill$children[[1]] <- nullGrob()
# Draw the plot, and move to the panel viewport
p
downViewport("panel.7-5-7-5")
# Draw the edited grill on top of the panel
grid.draw(grill)
upViewport(0)
But this version might be a little more robust to changes to ggplot
library(grid)
bg <- "grey92"
p <- ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", bg)) +
theme(panel.background = element_rect(fill = bg),
legend.position = "none")
# Get the ggplot grob
g <- ggplotGrob(p)
# Get the Grid lines
grill <- getGrob(grid.force(g), gPath("grill"), grep = TRUE)
# grill includes the grey background. Remove it.
grill = removeGrob(grill, gPath("background"), grep = TRUE)
# Get the name of the viewport containing the panel grob.
# The names of the viewports are the same as the names of the grobs.
# It is easier to select panel's name from the grobs' names
names = grid.ls(grid.force(g))$name
match = grep("panel.\\d", names, value = TRUE)
# Draw the plot, and move to the panel viewport
grid.newpage(); grid.draw(g)
downViewport(match)
# Draw the edited grill on top of the panel
grid.draw(grill)
upViewport(0)