How to arrange the position of this legend - r

As shown in the map below, how can I place the legend to the last cell in the grid?
The code I used is
psp1 <- tm_shape(province) +
tm_borders(col = 'black') +
tm_shape(county) +
tm_polygons(col = 'estimate', title = 'Changes in %', style = 'fixed', palette = brewer.pal(n = 6, name = 'Spectral'),
breaks = c(-15, -10, -5, 0, 5, 10, 15), legend.hist = F) +
tm_facets('warming', ncol = 2) +
tm_shape(province) +
tm_borders(col = 'black') +
tm_compass(north = 0, type = 'arrow', show.labels =0, position = c('right','top')) +
tm_layout(legend.format = list(fun = function(x) formatC(x, digits = 1, format = "f")),
fontface = 'bold',
legend.text.size = 1.3,
legend.width = 0.2,
legend.title.size = 1.5,
panel.label.size = 1.5,
panel.label.fontface = 'bold')
The data can be found from here. Thanks.

My answer comes perhaps a little late... (almost one year after your request!) Anyway, your question is very interesting and I hope this answer will be useful, either now or for future projects, for you or other SO users (by the way, thanks for having kept your input data accessible for a year ;-)).
As far as I know, it is not possible to solve your problem by using the tm_facets() function of the tmap library. So I suggest a slightly different "strategy" (still using the tmap library) to get what you are looking for.
It is articulated in two steps:
Build the maps and the legend manually... fortunately, not quite manually since the solution I suggest uses one custom function (i.e. make_graph()) that is run through a Map() function.
Edit the map mosaic with the legend using the R base grid library. Again, the implementation is made easier by the use of one custom function (i.e. Maps_setup()) run through a Map() function.
So, please find below a reprex that details the approach.
Reprex
STEP 1 - BUILDING THE MAPS AND THE LEGEND
library(sf)
library(tmap)
library(RColorBrewer)
# Import data
province <- st_read("province.shp")
county <- st_read("county.shp")
# Split the 'sf' object 'county' into a list of five 'sf' objects corresponding
# to the five warming scenarios (i.e. the first five facets of the final figure)
county_warm_list <- split(county , f = county$warming)
# Build the function 'make_graph' to generate the maps
make_graph <- function(x,y){
results <- tm_shape(x,
is.master = TRUE) +
tm_polygons(col = 'estimate',
title = 'Changes in %',
style = 'fixed',
palette = brewer.pal(n = 6, name = 'Spectral'),
breaks = c(-15, -10, -5, 0, 5, 10, 15),
legend.hist = FALSE,
midpoint = 0) +
tm_shape(province) +
tm_borders(col = 'black') +
tm_compass(north = 0,
type = 'arrow',
show.labels = 0,
position = c(0.93, 0.87),
size = 1.2) +
tm_layout(legend.show = FALSE,
# NB: the use of the 'get_asp_ratio()' function enables
# to optimize the size of each map inside its own facet:
asp = tmaptools::get_asp_ratio(x),
panel.labels = y,
panel.label.size = 0.8,
panel.label.fontface = 'bold',
inner.margins = c(0.02, 0.02, 0.02, 0.02))
return(results)
}
# Run the 'make_graph()' function through the list of the five 'sf' objects (i.e.
# 'county_warm_list') to generate the maps with their respective title using
# the 'Map()' function
map_titles <- names(county_warm_list)
Maps_list <- Map(make_graph, county_warm_list, map_titles)
# Build the legend using only the object "county"
Maps_legend <- tm_shape(county) +
tm_polygons(col = 'estimate',
title = 'Changes in %',
style = 'fixed',
palette = brewer.pal(n = 6, name = 'Spectral'),
breaks = c(-15, -10, -5, 0, 5, 10, 15),
legend.hist = FALSE,
midpoint = 0) +
tm_layout(legend.only = TRUE,
legend.position = c("center", "center"),
legend.format = list(fun = function(x) formatC(x, digits = 1, format = "f")),
fontface = 'bold',
legend.text.size = 1.3,
legend.width = 0.2,
legend.title.size = 1.5)
# Add the legend to 'Maps_list'
Maps_list$Legend <- Maps_legend
At the end of this first step, you get a list (i.e. Maps_list) containing 6 elements (i.e. five maps and one legend).
STEP 2 - LAYOUT OF THE MOSAIC OF MAPS WITH THE LEGEND
library(grid)
grid.newpage()
# Build the function 'Maps_setup' to set up the layout
Maps_setup <- function(x,y,z){
pushViewport(viewport(layout = grid.layout(nrow = 3, ncol = 2,
widths = unit(7.32, "cm"),
heights = unit(5, "cm"))))
setup <- print(x, vp = viewport(layout.pos.row = y, layout.pos.col = z))
return(setup)
}
# Run the 'Maps_setup()' function through the six objects of 'Maps_list' (i.e.
# 5 maps + 1 legend) to place the maps and the legend on the page using
# the 'Map()' function
# The 'pos_row' and 'pos_col' vectors are used to indicate where to place the
# maps as the 'Maps_setup()' function works through the list
pos_row <- rep(1:3, each = 2)
pos_col <- rep(1:2, times = 3)
Final_Results <- Map(Maps_setup, Maps_list, pos_row, pos_col)
Created on 2022-01-25 by the reprex package (v2.0.1)

Related

Create venn diagrams in R with circles one inside another

I want to create venn diagrams to emphasize that groups (circles) are completely located inside one another, i.e., there are no elements in the inner circles that are not simutanously in outer circles.
I've used ggvenn and arrived at these results:
colonias <- c("colônias")
possessoes <- c("possessões", colonias)
dominios <- c("domínios", possessoes, colonias)
ggvenn(tipologia_britanica,
show_elements = T,
label_sep = "\n",
fill_color = brewer.pal(name="Dark2", n=3),
fill_alpha = 0.6,
stroke_size = 0.2,
stroke_alpha = 0.2,
set_name_size = 5,
text_size = 5)
The result is tchnically correct because it show that "colonias" are common to all three groups and that "possessoes" are common to both "possessoes" and "dominios". But graphically I would like te groups to be completely inside one another to show that are no elements in "colonias" that are not common to all three, and in "possessoes" that are not common to "dominios". I'm not sure that ggvenn package is capable of plotting that.
One way may use the package eulerr.
However, your question isn't very clear so I let you play with the package
See the example below :
library(eulerr)
fit <- euler(c("A" = 10, "B" = 10, "A&B" = 8, "A&B&C"=3))
plot(fit,
fills = list(fill = c("red", "steelblue4","green"), alpha = 0.5),
labels = list(col = "black", font = 4),quantities = T)
I don't think ggvenn allows a plot with this kind of relationship. However, it's not terribly difficult to draw it yourself with ggplot and geom_circle from ggforce
ggplot(data.frame(group = c("domínios", "possessões", "colônias"),
r = c(3, 2, 1)),
aes(x0 = 3 - r, y0 = 0, fill = factor(group, group))) +
geom_circle(aes(r = r), alpha = 1) +
geom_text(aes(x = c(0, 1, 2), y = c(2.3, 1.3, 0), label = group),
size = 8) +
scale_fill_manual(values = c('#77bca2', '#e1926b', '#a09cc8'),
guide = 'none') +
coord_equal() +
theme_void()

Control Label of Contour Lines in `contour()`

I am using image() and contour() to create a "heatmap" of probabilities - for example:
I was asked to change the labels such that they "do not overlap the lines, and the lines are unbroken." After consulting ?contour(), I tried changing to method = "edge" and method = "simple", but both fail print the labels (although the lines are unbroken), and cant seem to find posts regarding similar issues elsewhere.
Any advice on how to manipulate the labels to appear adjacent to (not on top of) unbroken lines would be much appreciated. I would prefer base R but also would welcome options from more flexible packages or alternative base R functions.
Minimal code to recreate example figure is here:
# Generate Data
Rs <- seq(0.02, 1.0, 0.005)
ks <- 10 ^ seq(-2.3, 0.5, 0.005)
prob <- function(Y,R,k) {
exp(lgamma(k*Y+Y-1) - lgamma(k*Y) - lgamma(Y+1) + (Y-1) * log(R/k) - (k*Y+Y-1) * log(1+R/k))
}
P05 <- matrix(NA, ncol = length(ks), nrow = length(Rs))
for(i in 1:length(Rs)) {
for(j in 1:length(ks)) {
P05[i,j] <- 1 - sum(prob(1:(5 - 1), Rs[i], ks[j]))
}
}
colfunc <- colorRampPalette(c("grey25", "grey90"))
lbreaks <- c(-1e-10, 1e-5, 1e-3, 5e-3, 1e-2, 2e-2, 5e-2, 1e-1, 1.5e-1, 1)
## Create Figure
image(Rs, ks, P05,
log="y", col = rev(colfunc(length(lbreaks)-1)), breaks = lbreaks, zlim = lbreaks,
ylim = c(min(ks), 2), xlim = c(0,1))
contour(Rs, ks, P05, levels = lbreaks, labcex = 1, add = TRUE)
There is an easy(ish) way to do this in ggplot, using the geomtextpath package.
First, convert your matrix to an x, y, z data frame:
df <- expand.grid(Rs = Rs, ks = ks)
df$z <- c(P05)
Now plot a filled contour, and then geom_textcontour. By default the text will break the lines, as in contour, but if you set the vjust above one or below zero the lines will close up as they don't need to break for the text.
I've added a few theme and scale elements to match the aesthetic of the base graphics function. Note the text and line size, color etc remain independently adjustable.
library(geomtextpath)
ggplot(df, aes(Rs, ks, z = z)) +
geom_contour_filled(breaks = lbreaks) +
geom_textcontour(breaks = lbreaks, color = 'black', size = 5,
aes(label = stat(level)), vjust = 1.2) +
scale_y_log10(breaks = c(0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2),
expand = c(0, 0)) +
scale_fill_manual(values = rev(colfunc(9)), guide = 'none') +
scale_x_continuous(expand = c(0, 0)) +
theme_classic(base_size = 16) +
theme(axis.text.y = element_text(angle = 90, hjust = 0.5),
axis.ticks.length.y = unit(3, 'mm'),
plot.margin = margin(20, 20, 20, 20))
The contour function is mostly written in C, and as far as I can see, it doesn't support the kinds of labels you want.
So I think there are two ways to do this, neither of which is very appealing:
Modify the source to the function. You can see the start of the labelling code here. I think you would need to rebuild R to incorporate your changes; it's not easy to move a function from a base package to a contributed package.
Draw the plot with no labels, and add them manually after the fact. You could add them using text(), or produce an output file and use an external program to edit the output file.

Fix text position in rasterVis::vectorplot

I'm trying to fix the position of some text in a rasterVis::vectorplot so that it stays in the same position even if I change the width and height of the png file.
I tried using the margin parameters of par but with no luck.
This is an example of what I got so far:
#Some raster data
proj <- CRS('+proj=longlat +datum=WGS84')
df <- expand.grid(x = seq(-2, 2, .01), y = seq(-2, 2, .01))
df$z <- with(df, (3*x^2 + y)*exp(-x^2-y^2))
r <- rasterFromXYZ(df, crs=proj)
#[A]
png("test01.png",width = 918,height = 850,res=100)
vectorplot(r,par.settings=list(layout.widths = list(axis.key.padding = 3)),
narrows = 500,length=0.1,lwd.arrows=0.4)
grid.text(substr(R.version.string, 1, 15),rot=90, x=0.92,y=0.14,gp = gpar(fontsize = 12, fontface = "italic"))
dev.off()
This is the output of [A]. That's how I want it:
Now, changing the width and height:
##[B]
png("test02.png",width = 1718,height = 850,res=100)
vectorplot(r,
par.settings=list(layout.widths = list(axis.key.padding = 3)),
narrows = 500,length=0.1,lwd.arrows=0.4)
grid.text(substr(R.version.string, 1, 15),rot=90, x=0.92,y=0.14,gp = gpar(fontsize = 12, fontface = "italic"))
dev.off()
This is the output of [B]:
As you can see the text doesn't stay in the same place. (I'm new with the rasterVis library.)
The layer function of the latticeExtra package with panel.text will help you here. The panel.text functions prints inside the area of the panel, so you have to add the option clip = list(panel = FALSE) to the settings list in order to print outside this area:
library(grid)
library(rasterVis)
vectorplot(r,par.settings=list(clip = list(panel = FALSE),
layout.widths = list(axis.key.padding = 3)),
narrows = 500,length=0.1,lwd.arrows=0.4) +
layer(panel.text(2.05, -2,
substr(R.version.string, 1, 15),
adj = c(0, 1),
srt = 90))

Converting a ggplot/manipulate plot to a plotly or a js-scripted plot

I'm trying to convert my ggplot to a plotly plot using ggplotly(). However, it doesn't seem to work on this code, after manipulate is acted on the plot. Is there any other way to do it?
library(ggplot2)
library(manipulate)
grades <- data.frame(Final = 20 * runif(70))
myFinalsPlot <- function(sliderInput, initialIndex, finalIndex) {
ggplot(data.frame(grades$Final[initialIndex:finalIndex]),
aes(x = grades$Final[initialIndex:finalIndex])) +
geom_histogram(aes(y = ..density..),
binwidth = sliderInput, colour = "green", fill = "yellow") +
geom_density(alpha = 0.2, fill = "#FF6666") +
labs(x = "Marks", y = "Grades")
}
myFinalsPlot <- manipulate(myFinalsPlot(slidersInput, 1, 70),
slidersInput = slider(1, 12, step = 1, initial = 5))
First, to make your code work with the ggplot2 plot, there is an issue in your code that you need to fix. You shouldn't give the same name to your function and plot object. Replace this:
myFinalsPlot <- manipulate(myFinalsPlot(slidersInput, 1, 70),
slidersInput = slider(1, 12, step = 1, initial = 5))
By, e.g.:
myPlot <- manipulate(myFinalsPlot(slidersInput, 1, 70),
slidersInput = slider(1, 12, step = 1, initial = 5))
Now, regarding plotly plots, I don't think it is supposed to work with manipulate. I quote RStudio's website https://support.rstudio.com/hc/en-us/articles/200551906-Interactive-Plotting-with-Manipulate:
RStudio works with the manipulate package to add interactive capabilities to standard R plots.

Neat formatting for Venn diagram in R with unbalanced group sizes

I'm using the VennDiagram R package to try to generate a neatly formatted diagram comparing two groups. I have successfully used this package in the past to compare relatively similarly-sized groups. However, now I'm comparing groups that have significantly different sizes (# of unique elements in the first group is ~3,600, # of unique elements in the second group is ~60, and # of overlapping elements is ~80).
The appearance of my current Venn diagram is that the group with the larger # of elements has this value displayed within its circle, but the labels for the intersection of the two groups and the unique elements in the second group are too large to be included in those regions of the diagram, so instead, they are displayed outside of the diagram with a line connecting them to the associated region. I don't like the appearance of this, and would like to reduce the size of all 3 labels so that they can be displayed within their respective regions of the diagram. However, after having reviewed the associated documentation/examples and publication (Chen & Boutros 2011), I'm still not clear about how to do this. (For example, I see parameters that permit the specification of font size of the figure title and subtitle, but I don't see where the labels' font size can be specified...)
I have attempted workarounds such as trying to make the labels invisible so that I can manually add them in a separate application, but this doesn't seem to be an option...
Any suggestions for how I can reduce the font size of my labels and specify that these labels appear within the regions of the diagram rather than outside of the diagram, will be appreciated. Thanks!
Update: As requested below, I am providing my example code:
library(VennDiagram);
library(grid);
Data <- read.csv('ExampleDataset_VennDiagram.csv')
Dataset1 <- Data[,1]
Dataset2 <- Data[,2]
MyVennDiagram <- venn.diagram(
x = list(
A = Dataset1,
B = Dataset2
),
main = "",
main.cex = NULL,
filename = NULL,
lwd = 2,
fill = c("blue", "green"),
alpha = 0.75,
label.col = "black",
cex=c(2,2,2),
fontfamily = "sansserif",
fontface = "bold",
cat.col = c("blue", "green"),
cat.cex = 0,
cat.fontfamily = "serif",
cat.fontface = "bold",
cat.dist = c(0.05, 0.05),
cat.pos = c(-20, 14),
);
grid.newpage()
grid.draw(MyVennDiagram)
Update: Based on missuse's suggestion below, using ext.text = FALSE works perfectly!
Thanks to everyone who contributed to this thread.
The eulerr library appears to generate nice-looking diagrams, and will definitely be a resource I use in the future -- thanks for sharing.
A possible solution to this is to avoid using euler diagrams.
To illustrate your problem here is some data:
A = sample(1:1000, 500, replace = T)
B = sample(1:10000, 50)
Here is the diagram obtained by
library(VennDiagram);
library(grid)
MyVennDiagram = venn.diagram(
x = list(
A = A,
B = B
),
main = "",
main.cex = NULL,
filename = NULL,
lwd = 2,
fill = c("cornflowerblue", "pink"),
alpha = 0.75,
label.col = "black",
cex=c(2,2,2),
fontface = "plain",
cat.col = c("cornflowerblue", "pink"),
cat.cex = 0,
cat.fontfamily = "serif",
cat.fontface = "plain",
cat.dist = c(0.05, 0.05),
cat.pos = c(-20, 14),
cat.default.pos = "text",
)
grid.newpage()
grid.draw(MyVennDiagram)
by avoiding scaling of the circles with scaled = FALSE
MyVennDiagram = venn.diagram(
x = list(
A = A,
B = B
),
main = "",
main.cex = NULL,
filename = NULL,
lwd = 2,
fill = c("cornflowerblue", "pink"),
alpha = 0.75,
label.col = "black",
cex=c(2,2,2),
fontface = "plain",
cat.col = c("cornflowerblue", "pink"),
cat.cex = 0,
cat.fontfamily = "serif",
cat.fontface = "plain",
cat.dist = c(0.05, 0.05),
cat.pos = c(-20, 14),
cat.default.pos = "text",
scaled = FALSE
)
grid.newpage()
grid.draw(MyVennDiagram)
As per user20650 suggestion the best option is to use ext.text=FALSE in the original call:
Also check library(eulerr) it accepts a bit different input, here is an illustration:
library(eulerr)
library(tidyverse)
data.frame(dat = unique(c(A, B))) %>%
mutate(A = dat %in% A,
B = dat %in% B) %>%
select(A, B) %>%
euler() %>%
eulerr:::plot.euler(counts = T)
As per user20650 comment acceptable input is also:
plot(euler(setNames(list(unique(A),unique(B)), c("A", "B"))), counts=TRUE)

Resources