I am doing the map of the world showing profit and loss from different countries.
I already plotted the map, I prepared and joined data, Im at the final step.
My dataset is 'data_profit', 'Total_profit' are values Im using (negative and positive ones)- and it's used to fill the map with colors, depending on the value.
Rest of the code is map plotting.
ditch_the_axes <- theme(
axis.text = element_blank(),
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.border = element_blank(),
panel.grid = element_blank(),
axis.title = element_blank()
)
terra<-ggplot(data_profit, aes( x = long, y = lat, group = group )) +
geom_polygon(aes(x = long, y = lat,fill = Total_profit),color = "black")+
coord_fixed(1.3) +
theme_bw() +
ditch_the_axes+
scale_fill_gradient2( low="black", high="red", space ="Lab" )
data_profit <-
structure(list(long = c(-69.8991241455078, -69.8957061767578,
-69.9421920776367, -70.004150390625, -70.0661163330078, -70.0508804321289
), lat = c(12.4520015716553, 12.4229984283447, 12.4385251998901,
12.50048828125, 12.5469722747803, 12.5970697402954), group = c(1,
1, 1, 1, 1, 1), order = 1:6, region = c("Aruba", "Aruba", "Aruba",
"Aruba", "Aruba", "Aruba"), Total_profit = c(0, 0, 0, 0, 0, 0
)), row.names = c(NA, 6L), class = "data.frame")
and this is the output map:
So, the thing is the final map doesn't show the negative values (which should be in shade of black and grey). I checked whether 'Total_profit' values are numeric (with is.finite and is.numeric).
Do you have any idea what to change in the code?
Your question is unfortunately not reproducible, so I created some different sample data. I also suggest using "world map" and geom_map instead of geom_polygon (see below).
I assume the problem in your plot is a combination of missing values and the lack of correct limits in scale_..._gradient. Using scale_...gradientn comes with another problem - the asymmetric midpoint. (see here)
library(tidyverse)
library(scales) #load this for the mid-point problem and using scales::rescale (see below)
WorldData <- map_data('world') #easier way to draw countries than using geom_polygon
# In the next steps I am merging your profit data with the world map. I am creating a different sample data frame
data_profit <- data.frame(region = sample(WorldData$region,6), Total_profit = c(-5, -3, 0, 3, 5, 10))
map_profit <- dplyr::left_join(WorldData, data_profit, by ='region')
#> Warning: Column `region` joining character vector and factor, coercing into
#> character vector
# This plot is the easier one - using scale_fill_gradient(...). No mid-point problem. Note I am specifying the NA color using na.value.
# I am also specifying the limits using limits = ... I am not hard coding this, giving more flexibility for future graphs.
ggplot() +
geom_map(data = map_profit, map = WorldData,
aes(x = long, y = lat, map_id = region, fill = Total_profit, na.rm = FALSE)) +
scale_fill_gradient(low="black", high="red", na.value = 'blue',
limits = c(min(map_profit$Total_profit), max(map_profit$Total_profit)))
#> Warning: Ignoring unknown aesthetics: x, y, na.rm
# The next plot comes with the problem of an asymmetric mid-point
# I therefore rescale the values using rescale
ggplot() +
geom_map(data = map_profit, map = WorldData,
aes(x = long, y = lat, map_id = region, fill = Total_profit, na.rm = FALSE)) +
scale_fill_gradientn(colors = c("black", 'white', "red"), na.value = 'blue',
values = rescale(c(min(map_profit$Total_profit, na.rm = TRUE),0, max(map_profit$Total_profit,na.rm = TRUE))),
limits = c(min(map_profit$Total_profit), max(map_profit$Total_profit)))
#> Warning: Ignoring unknown aesthetics: x, y, na.rm
Created on 2019-07-11 by the reprex package (v0.3.0)
Related
Here is my raw data, first column is length and second is label:
length label
6.2 sc1__1__62000
0.5 sc1__63001__68000
2.6 sc1__75001__101000
0.7 sc1__103001__110000
....
There are 200 entries as such in the file.
I want to make an image as follow, width of each rectangle is same as corresponding length in the table:
How should I do this in R?
You need to use dplyr to create extra columns for plotting
library(dplyr)
library(ggplot2)
library(readr)
set.seed(20191234)
testdata <- tibble(
length = sample(1:10,10,replace = TRUE),
label = replicate(10,paste0(sample(letters,sample(5:15,10,replace = TRUE)),collapse = ""))
) %>%
# plot data
mutate(
xmax = cumsum(length),
xmin = dplyr::lag(xmax,default = 0),
ymin = 0,
ymax = 2,
text_x = (xmin+xmax)/2,
text_y = nchar(label)
)
Create plot using geom_rect() and geom_text()
text_y_adjust <- -0.032
testdata %>%
ggplot() +
geom_rect(aes(xmin = xmin,xmax = xmax,ymin =ymin,ymax = ymax),
alpha = 0,color = "black",size = 1) +
geom_text(aes(x = text_x,y = text_y_adjust * text_y, label = label),angle = 90) +
ylim(c(-2,2)) +
theme_void()
Note: If you modify any of height of rectangles, text_y_adjust ratio, or ylim, you need to also change other values correspondingly.
I suppose it can be done more elegant, but here is a suggestion:
#create sample dataframe
length<-c(6.2,0.5,2.6,0.7)
label<-letters[1:4]
d<-data.frame(length,label)
#calculate distance for labels
labelLength<-length[1]/2
for (i in 2:length(length)) {
labelLength[i]<-sum(length[1:(i-1)])+length[i]/2
}
#create plot
library(ggplot2)
p<-ggplot()+geom_bar(mapping=aes(x=1,y=length),
stat="identity",fill="white",color="red")+coord_flip()+
scale_y_continuous(breaks=labelLength,labels=label)+
theme(axis.text.x=element_text(angle=90,vjust=0.5,size=20),
axis.text.y=element_blank(),
axis.title.y = element_blank(),
panel.background = element_rect(fill="white"))
plot(p)
I am plotting a map of my study area and I am having problems to edit the legend title.
I need it to be "Projected fruit productivity in fallows in 40 yrs (fruits ha^-1) written in four lines. I could use bquote() to plot the -1 as a superscript. But it created an extra space that I cannot figure it out how to take it off. The extra space only appears when the title is divided into multiple lines.
Also, expression(atop()) creates the superscript but once I tried to divide it into more than two lines it does not show lines three and four.
This is the Map with the extra space using bquote()
This is the Map with the four line title using expression(atop())
I did try different solutions found on the internet, including this post. But they all plot the fourth line with the extra space or only plot the first or second line.
Bellow is the code I am using. Any help is welcomed.
The comments are different tries.
Data = spatial_dist_fallows.csv
library(sf) #sf = simple feature
library(ggplot2)
library(dplyr)
PAECM_fallows <-read.csv("spatial_dist_fallows.csv")
PAECM_fallows_sp <- st_as_sf(PAECM_fallows,coords = c("X", "Y"),crs = "+proj=longlat +datum=WGS84 +no_defs")
custom_bins_fruit = c(0,60,120,180,240,1400)
PAECM_fallows_fruit <- PAECM_fallows_sp %>%
mutate(prod_cat_fallow = cut(prod_40, breaks= custom_bins_fruit),
age_cat_fallow = cut(age, breaks = c(11,17,22,29,60)))
prod_map_PAECM_fruit<-ggplot()+
geom_sf(data = PAECM_fallows_fruit,aes(size = prod_cat_fallow), shape = 18, show.legend = "point")+
scale_size_manual(values= c(2,3,4,5,6),
# name = "Projected fruit\nproductivity in\nfallows in 40 yrs \n(fruits ha^-1)",
name = bquote("Projected fruit\nproductivity in\nfallows in 40 yrs \n( fruits"*ha^-1*")"),
# name = expression(paste("Projected fruit productivity\nin fallows in 40 yrs\n"),bquote(paste("("*fruits~ha^-1*")"))),#(Fruits/ha)
name = expression(atop("Projected fruit",
"productivity in",
"fallows in 40 yrs",
"( fruits ha"^-1,")")),
breaks= c(NA,"(0,60]","(60,120]","(120,180]","(180,240]","(240,1.4e+03]"),
labels= c("NA","\u2264 60","60 - 120","120 - 180","180 - 240","> 240"),
guide = guide_legend(override.aes = list(linetype = "blank", shape = 18, fill = NA)))+
# labs(size = expression(atop("Projected fruit\nproductivity in\nfallows in 40 yrs\n(fruits"*ha^-1*")", sep="")))+ #comment name line at the scale_size_manual
# labs(size = bquote("Projected fruit productivity \nin fallows in 40 yrs \n( fruits"*ha^-1*")"))+ #comment name line at the scale_size_manual
ggplot2::theme_minimal()+
ggplot2::theme(legend.text.align=0.5,
legend.title.align = 0.5,
plot.background = element_blank(),
panel.grid = element_line(colour = "white"),
panel.background = element_rect(fill = "grey87", color = "white"))+#,
coord_sf(xlim = c(-68.45,-68.2), ylim = c(-11.05,-10.8))
prod_map_PAECM_fruit
Extra question. Once I started to use the bquote I could not align the title text using theme(legend.title.align = 0.5), any other ideas?
After some other tries, I did come up with the following solution for the legend title.
name = expression(atop("",
atop(textstyle("Projected fruit"),
atop(textstyle("productivity in"),
atop(textstyle("fallows in 40 yrs"),
atop(textstyle("(fruits ha"^-1*")"))))))),
I used textstyle() to plot all text with the same size, otherwise it would be plotted smaller every time atop() was called. Atop() creates a space between the first and second line, that is why the first line of the code is atop("", so the first line will be a blank.
This is the final code with the map below.
library(sf) #sf = simple feature
library(ggplot2)
library(dplyr)
PAECM_fallows <-read.csv("spatial_dist_fallows.csv")
PAECM_fallows_sp <- st_as_sf(PAECM_fallows,coords = c("X", "Y"),crs = "+proj=longlat +datum=WGS84 +no_defs")
custom_bins_fruit = c(0,60,120,180,240,1400)
PAECM_fallows_fruit <- PAECM_fallows_sp %>%
mutate(prod_cat_fallow = cut(prod_40, breaks= custom_bins_fruit),
age_cat_fallow = cut(age, breaks = c(11,17,22,29,60)))
prod_map_PAECM_fruit_legend_test<-ggplot()+
geom_sf(data = PAECM_fallows_fruit,aes(size = prod_cat_fallow), shape = 18, show.legend = "point")+
scale_size_manual(values= c(2,3,4,5,6),
name = expression(atop("",
atop(textstyle("Projected fruit"),
atop(textstyle("productivity in"),
atop(textstyle("fallows in 40 yrs"),
atop(textstyle("(fruits ha"^-1*")"))))))),
breaks= c(NA,"(0,60]","(60,120]","(120,180]","(180,240]","(240,1.4e+03]"),
labels= c("NA","\u2264 60","60 - 120","120 - 180","180 - 240","> 240"),
guide = guide_legend(override.aes = list(linetype = "blank", shape = 18, fill = NA)))+
ggplot2::theme_minimal()+
ggplot2::theme(legend.text.align=0.5,
legend.title.align = 0.5,
plot.background = element_blank(),
panel.grid = element_line(colour = "white"),
panel.background = element_rect(fill = "grey87", color = "white"))+#,
coord_sf(xlim = c(-68.45,-68.2), ylim = c(-11.05,-10.8))
prod_map_PAECM_fruit_legend_test
Alternatively, you could use the annotation functions cowplot::draw_label() or ggplot2::annotation_custom(). I think that the explanations about these approaches given in ggplot2 two-line label with expression are helpful here as well.
1) Solution with cowplot::draw_label()
library(ggplot2)
library(cowplot)
#> Warning: package 'cowplot' was built under R version 3.5.2
#>
#> Attaching package: 'cowplot'
#> The following object is masked from 'package:ggplot2':
#>
#> ggsave
# If needed, revert to default theme (cowplot modifies the theme);
theme_set(theme_grey())
# Build a simple plot as example
p <- ggplot(mtcars, aes(x = wt, y = mpg, size = factor(gear))) +
geom_point() +
labs(size = element_blank()) + # remove default legend title
# Make enough space for the custom legend title by tweaking the right margin
theme(legend.margin = margin(t = 0, r = 26, b = 0, l = 0, unit = "mm"))
# Adjust further theme elements if needed, like text size, font, etc
# The lines of text and expression that constitute your custom legend title
lines <- list(
"Projected fruit",
"productivity in",
"fallows in 40 yrs",
expression("(fruits ha" ^-1 ~ ")")
)
# Using relative coordinates ranging from 0 to 1 (relative to the entire canvas).
# There is some guesswork with the coordinates until we get them right.
min_y <- 0.6
step <- 0.04 # dictates the line spacing; need to play with it until you get it right
ys <- seq(from = min_y + step * 4, to = min_y, by = -step)
x <- 0.87
# Add the annotations that will actually constitute the legend title.
gg <- ggdraw(p)
#> Warning: Using size for a discrete variable is not advised.
# Neglect the warning in this example.
for (i in 1:4){
gg <- gg + draw_label(lines[[i]], x = x, y = ys[i])
}
gg
Note that, cowplot::draw_label() can also be used in combination with setting the clipping off, coord_cartesian(clip = "off"), which allows plotting anywhere on the canvas (see next example with ggplot2::annotation_custom()). In such a case, we do not use the relative coordinates anymore, but the ones from the plot/data (the absolute coordinates).
2) Solution with ggplot2::annotation_custom()
Note that, cowplot::draw_label() uses ggplot2::annotation_custom() under the hood, so it is more or less the same annotation technique, but bit more verbose. We need to set clipping off. This time we do not use the relative coordinates anymore, but the ones from the plot/data (the absolute coordinates).
Building upon the p plot example from above:
min_y <- 24
step <- 1 # dictates the line spacing; need to play with it until you get it right
ys <- seq(from = min_y + step * 4, to = min_y, by = -step)
x <- 6.2
# set clipping off - allows plotting anywhere on the canvas
pp <- p + coord_cartesian(clip = "off")
for (i in 1:4){
pp <- pp + annotation_custom(grid::textGrob(lines[[i]]),
xmin = x, xmax = x, ymin = ys[i], ymax = ys[i])
}
pp
#> Warning: Using size for a discrete variable is not advised.
Created on 2019-01-15 by the reprex package (v0.2.1)
I have a dataframe like so:
set.seed(453)
year= as.factor(c(rep("1998", 20), rep("1999", 16)))
lepsp= c(letters[seq(from = 1, to = 20 )], c('a','b','c'),letters[seq(from =8, to = 20 )])
freq= c(sample(1:15, 20, replace=T), sample(1:18, 16,replace=T))
df<-data.frame(year, lepsp, freq)
df<-
df %>%
group_by(year) %>%
mutate(rank = dense_rank(-freq))
Frequencies freq of each lepsp within each year are ranked in the rank column. Larger freq values correspond to the smallest rank value and smaller freq values have the largest rank values. Some rankings are repeated if levels of lepsp have the same abundance.
I would like to split the df into multiple subsets by year. Then I would like to plot each subsetted dataframe in a multipanel figure. Essentially this is to create species abundance curves. The x-axis would be rank and the yaxis needs to be freq.
In my real dataframe I have 22 years of data. I would prefer the graphs to be displayed as 2 columns of 4 rows for a total of 8 graphs per page. Essentially I would have to repeat the solution offered here 3 times.
I also need to demarcate the 25%, 50% and 75% quartiles with vertical lines to look like this (desired result):
It would be great if each graph specified the year to which it belonged, but since all axis are the same name, I do not want x and y labels to be repeated for each graph.
I have tried to plot multiple lines on the same graph but it gets messy.
year.vec<-unique(df$year)
plot(sort(df$freq[df$year==year.vec[1]],
decreasing=TRUE),bg=1,type="b", ylab="Abundance", xlab="Rank",
pch=21, ylim=c(0, max(df$freq)))
for (i in 2:22){
points(sort(df$freq[df$year==year.vec[i]], decreasing=TRUE), bg=i,
type="b", pch=21)
}
legend("topright", legend=year.vec, pt.bg=1:22, pch=21)
I have also tried a loop, however it does not produce an output and is missing some of the arguments I would like to include:
jpeg('pract.jpg')
par(mfrow = c(6, 4)) # 4 rows and 2 columns
for (i in unique(levels(year))) {
plot(df$rank,df$freq, type="p", main = i)
}
dev.off()
Update
(Attempted result)
I found the following code after my post which gets me a little closer, but is still missing all the features I would like:
library(reshape2)
library(ggplot2)
library (ggthemes)
x <- ggplot(data = df2, aes(x = rank, y = rabun)) +
geom_point(aes(fill = "dodgerblue4")) +
theme_few() +
ylab("Abundance") + xlab("Rank") +
theme(axis.title.x = element_text(size = 15),
axis.title.y = element_text(size = 15),
axis.text.x = element_text(size = 15),
axis.text.y = element_text(size = 15),
plot.title = element_blank(), # we don't want individual plot titles as the facet "strip" will give us this
legend.position = "none", # we don't want a legend either
panel.border = element_rect(fill = NA, color = "darkgrey", size = 1.25, linetype = "solid"),
axis.ticks = element_line(colour = 'darkgrey', size = 1.25, linetype = 'solid')) # here, I just alter to colour and thickness of the plot outline and tick marks. You generally have to do this when faceting, as well as alter the text sizes (= element_text() in theme also)
x
x <- x + facet_wrap( ~ year, ncol = 4)
x
I prefer base R to modify graph features, and have not been able to find a method using base R that meets all my criteria above. Any help is appreciated.
Here's a ggplot approach. First off, I made some more data to get the 3x2 layout:
df = rbind(df, mutate(df, year = year + 4), mutate(df, year = year + 8))
Then We do a little manipulation to generate the quantiles and labels by group:
df_summ =
df %>% group_by(year) %>%
do(as.data.frame(t(quantile(.$rank, probs = c(0, 0.25, 0.5, 0.75)))))
names(df_summ)[2:5] = paste0("q", 0:3)
df_summ_long = gather(df_summ, key = "q", value = "value", -year) %>%
inner_join(data.frame(q = paste0("q", 0:3), lab = c("Common", "Rare-75% -->", "Rare-50% -->", "Rare-25% -->"), stringsAsFactors = FALSE))
With the data in good shape, plotting is fairly simple:
library(ggthemes)
library(ggplot2)
ggplot(df, aes(x = rank, y = freq)) +
geom_point() +
theme_few() +
labs(y = "Abundance (% of total)", x = "Rank") +
geom_vline(data = df_summ_long[df_summ_long$q != "q0", ], aes(xintercept = value), linetype = 4, size = 0.2) +
geom_text(data = df_summ_long, aes(x = value, y = Inf, label = lab), size = 3, vjust = 1.2, hjust = 0) +
facet_wrap(~ year, ncol = 2)
There's some work left to do - mostly in the rarity text overlapping. It might not be such an issue with your actual data, but if it is you could pull the max y values into df_summ_long and stagger them a little bit, actually using y coordinates instead of just Inf to get it at the top like I did.
I would like to annotate a heat-map by putting symbols next to the axis text.
For instance, lets say I am plotting out a distance matrix. Here's an example of such a matrix for environmental community similarity:
library(vegan)
library(tidyverse)
data(varespec)
data(varechem)
library(reshape2)
library(viridis)
vare.dist <- vegdist(varespec)
vare.hc <- hclust(as.dist(vare.dist))
vare.dist.long <- vare.dist %>% as.matrix %>% melt %>%
mutate(Var1 = factor(Var1, levels = unique(vare.hc$labels)[vare.hc$order]))%>%
mutate(Var2 = factor(Var2, levels = unique(vare.hc$labels)[vare.hc$order]))
vare.dist.long %>% #as.matrix %>% .[vare.hc$order, vare.hc$order] %>% melt %>%
ggplot(aes(x = Var1, y = Var2, fill = value)) + geom_tile() + scale_fill_viridis(direction = 1) +
theme(axis.text.x = element_text(angle = 270, hjust = 0, vjust = 0.5
))
Figure 1. A heatmap of distances between different sites in the varespec data set.
Let's say I want to indicate which sites have higher than average nitrogen, phosphorous and potassium. I want to indicate this data along the axis, reserving the y-axis for some other purpose. One ugly way of doing this would be to modify the text strings, and text font, for axis one.
## Define a few helper functions
transmit_factor_order <- function(ordered, unordered){
# allows us to put a character vector or unordered factor in the same order as another factor
# ordered is an ordered factor
# unordered is an unordered factor or character vector
if(class(ordered) != 'factor'){
stop("'ordered' must be of class factor")
}
nrow = length(ordered)
df = data.frame(ordered, unordered, oldorder = 1:nrow)
df = df[order(df[,"ordered"]),]
df[,"unordered"] = factor(df[,"unordered"], levels = unique(df[,"unordered"]))
df = df[order(df[,"oldorder"]),]
df$unordered
}
medcode_chr <- function(vec, low = "", high = "o"){
# convert a vector of numbers into one of symbols (or numbers) with one value
# for lower than median values and one for higher than median values
sapply(vec, function(x){
if(x < median(na.omit(vec))){low}else{high}
}
)
}
## Actual Work
vare.dist.long %>%
# convert numeric vector to character
mutate(Var1_chr = as.character(Var1)) %>%
# append the envioronmental data
left_join(varechem %>% rownames_to_column %>% dplyr::select(rowname:K),
by = c('Var1_chr' = 'rowname')) %>%
# make new columns with symbols that we display if values are bigger than the median
mutate(highN = medcode_chr(N),
highP = medcode_chr(P, high = "+"),
highK = medcode_chr(K, low = 0, high = 1)) %>%
# make a new name, which is the number, appended to the symbols defined above
unite(Var1_Annotated, Var1, highN, highP, sep = " ", remove = FALSE) %>%
# make sure that newly named vecotr is in the same order as Var1
#(which was ordered for clustering purposes)
mutate(Var1_Annotated = transmit_factor_order(Var1, Var1_Annotated)) %>%
# do the same thing to the character version of Var1, which will be useful downstream
mutate(Var1_chr = transmit_factor_order(Var1, Var1_chr))-> vare.data
vare.data %>%
ggplot(aes(x = Var1_Annotated, y = Var2, fill = value)) + geom_tile() + scale_fill_viridis(direction = 1) +
theme(axis.text.x = element_text(angle = 270, hjust = 0, vjust = 0.5,
face = ifelse(vare.data$highK, "bold", "plain"),
colour = ifelse(vare.data$highK, "red", "blue")
))
Figure 2. Another heatmap of distances between different sites in the varespec data set. This time the x-axis names have been modified to show if nitrogen is high or low (presence or absence of "o"), whether phosphorous is high or low (presence or abscence of "+"), and whether potassium is high (red) or low (blue).
This figure conveys the information that I need, but it's kind of ugly. I'd rather say, place differently colored circles to convey which things have high nitrogen, phosphorous and potassium. I'm thinking something like the following, but actually part of the previous figure.
vare.data %>% dplyr::select(-c(Var2, value)) %>% unique %>% arrange(Var1_Annotated) %>%
mutate(highN = medcode_chr(N, 0, 1),
highP = medcode_chr(P, 0, 1),
highK = medcode_chr(K, 0, 1)) %>%
dplyr::select(-c(N,P,K, Var1, Var1_Annotated)) %>%
gather(key, value, -Var1_chr) %>%
filter(value == 1) %>%
ggplot(aes(x = Var1_chr, y = key, color = key)) + geom_point() +
theme(axis.text.x = element_text(angle = 270, hjust = 0, vjust = 0.5),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.title.x = element_blank()) +
coord_fixed(ratio = 0.3)
Figure 3. Colored points, that I'd like to incorperate into Figure 1 to make it prettier than figure 2 but convey the same information.
Is there some way I can incorporate the colored dots (Figure 3) into the heatmap (Figure 1) so I can show the data about how the sites cluster and concurrently tell information about the different sites, as I do in Figure 2?
Thanks for any advice!
One (rather dirty) option would be to arrange both grobs, align them by vertically, tinker with the grobs vertical position, andedit the x-axis' scales and titles.
library(cowplot)
A <- vare.dist.long %>%
ggplot(aes(x = Var1, y = Var2, fill = value)) +
geom_tile() +
scale_fill_viridis(direction = 1) +
theme(axis.text.x = element_blank(),
axis.title.x = element_blank(),
plot.margin = unit(c(1,1,-1.5,1), "cm") ## Note the -1.5, it tells the grob to print itself 1.5 cm below its normal position.
) #/theme
B <- vare.data %>% dplyr::select(-c(Var2, value)) %>% unique %>%
arrange(Var1_Annotated) %>%
mutate(highN = medcode_chr(N, 0, 1),
highP = medcode_chr(P, 0, 1),
highK = medcode_chr(K, 0, 1)) %>%
dplyr::select(-c(N,P,K, Var1, Var1_Annotated)) %>%
gather(key, value, -Var1_chr) %>%
filter(value == 1) %>%
ggplot(aes(x = Var1_chr, y = key, color = key)) +
geom_point() +
theme(axis.text.x = element_text(angle = 270, hjust = 0, vjust = 0.5),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
plot.margin = unit(c(0,1,1,1), "cm")) +
coord_fixed(ratio = 0.3)
cowplot::plot_grid(A,B, nrow = 2, align = "v")
I searched already a lot for my specific problem but could not find a solution. Although, I think it is quite easy to solve, but I am new to r.
What i want to do: I would like to plot a map of India held in black and white. And I want to plot all the places I visited with dots and the corresponding name. I managed to do all that with the code below.
My problem: How can I write the code in a simpler way for plotting the visited places? I don't want to create all the variables like visit.x and visit.y. Further, I would have to write for every place the code for geom_point and geom_text. When I tried to plot more than one place at once with e.g.
visited <- c("New Delhi", "Rishikesh")
then I got the error message "Aesthetics must be either length 1 or the same as the data".
My question: How can I just store all the visited places in one variable and plot it in one run? Like that I just need one line for geom_point and not for every place I want to plot.
#Load required packages
library(maps)
library(ggmap)
library(ggplot2)
#Dataframe with country relevant information
map <- fortify(map(fill = T, plot = F, region = "India"))
#Places I want to mark on the map
visited <- c("New Delhi")
visited2 <- c("Rishikesh")
visited3 <- c("Agra")
#Extracting long / lat of the places
visit.x <- geocode(visited)$lon
visit.y <- geocode(visited)$lat
visit.x2 <- geocode(visited2)$lon
visit.y2 <- geocode(visited2)$lat
visit.x3 <- geocode(visited3)$lon
visit.y3 <- geocode(visited3)$lat
#Defining font
font = c("Courier")
font.size = 3
#Specifing the look of the map with ggplot2
map_india <- ggplot(data = map, aes(x = long, y = lat, group = group)) +
geom_polygon(fill = "white") +
geom_path(colour = "black") +
theme(panel.background = element_rect(fill = "#000000"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.ticks = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank())
#Plotting the places I want on the map with labels
map_india <- map_india +
geom_point(aes(x = visit.x, y = visit.y)) +
geom_text(data = NULL, x = visit.x - 1, y = visit.y + 0.2, label = "New Delhi", size = font.size, family = font) +
geom_point(aes(x = visit.x2, y = visit.y2)) +
geom_text(data = NULL, x = visit.x2 - 1, y = visit.y2 + 0.2, label = "Rishikesh", size = font.size, family = font) +
geom_point(aes(x = visit.x3, y = visit.y3)) +
geom_text(data = NULL, x = visit.x3, y = visit.y3 + 0.5, label = "Agra", size = font.size, family = font) +
coord_fixed(0.8)
#Creating pdf
pdf("India.pdf", height = 11.69, width = 16.53)
print(map_india)
dev.off()
As #hrbrmstr suggested in the comment above, ggplot2 is designed to work with data.frames, so it works best to keep your data in one throughout. Doing so actually simplifies the code a lot, too:
library(tidyverse) # for ggplot2 and `%>%`
library(ggmap)
library(ggrepel) # for geom_text_repel, though adjust overlaps manually if you prefer
cities <- data_frame(city = c("New Delhi", "Rishikesh", "Agra")) %>% # start data.frame
mutate_geocode(city) # use ggmap function to add lon/lat columns
cities
#> # A tibble: 3 × 3
#> city lon lat
#> <chr> <dbl> <dbl>
#> 1 New Delhi 77.20902 28.61394
#> 2 Rishikesh 78.26761 30.08693
#> 3 Agra 78.00807 27.17667
box <- geocode('India', output = 'more') # get lon/lat for bounding box
box
#> lon lat type loctype address north south east
#> 1 78.96288 20.59368 country approximate india 35.5087 6.753516 97.39556
#> west country
#> 1 68.16289 India
get_stamenmap(bbox = c(left = box$west, # get background tiles, set bounding box
bottom = box$south,
right = box$east,
top = box$north),
maptype = 'toner-background', # set map style
zoom = 5) %>%
ggmap(extent = 'device') + # note switch from %>% to + as moves to ggplot
geom_point(aes(x = lon, y = lat), data = cities) + # add points
geom_text_repel(aes(x = lon, y = lat, label = city), data = cities) # add labels
Adjust as you like.