ggplot2 (version 3) incompatibility with ggmap for geom_density_2d - r

ggplot2 version 3 seems to have an incompatibility with ggmap when using the geom_density2d() function to add a layer. The following code returns an error (though worked with ggplot2 version 2):
# Create a data frame
df <- data.frame(
long = rnorm(50, -122.32, .2),
lat = rnorm(50, 47.6, .2)
)
# Use qmplot to create a base layer of map tiles
base_plot <- qmplot(
data = df,
x = long, # data feature for longitude
y = lat, # data feature for latitude
geom = "blank", # don't display data points (yet)
maptype = "toner-background", # map tiles to query
darken = .7, # darken the map tiles
legend = "topleft" # location of legend on page
)
# Show the map in RStudio
base_plot
# Use ggplot to create a 2d density map (without tiles -- works fine)
ggplot(df, aes(x = long, y = lat)) +
geom_density2d() +
stat_density_2d(
aes(x = long, y = lat, fill = stat(level)), # in v2, fill = ..level..
# Use the computed density to set the fill
alpha = .3,
geom="polygon" # Set the alpha (transparency)
)
# Add 2d density plot on map tiles -- returns an error
base_plot +
geom_density2d() +
stat_density_2d(
aes(x = long, y = lat, fill = stat(level)), # in v2, fill = ..level..
# Use the computed density to set the fill
alpha = .3,
geom="polygon" # Set the alpha (transparency)
)
# Error in width_cm(guide$barwidth %||% theme$legend.key.width) :
# Unknown input
Any guidance on how to use geom_density2d() to add a layer to a qmplot() map would be appreciated!
(Map below of the image created with ggplot2 version 2)

Answered in comments via #Tung: the issue is with ggmap, and the solution is to use the development version of ggmap (devtools::install_github("dkahle/ggmap")

Related

ggplot2 density of one dimension in 2D plot

I would like to plot a background that captures the density of points in one dimension in a scatter plot. This would serve a similar purpose to a marginal density plot or a rug plot. I have a way of doing it that is not particularly elegant, I am wondering if there's some built-in functionality I can use to produce this kind of plot.
Mainly there are a few issues with the current approach:
Alpha overlap at boundaries causes banding at lower resolution as seen here. - Primary objective, looking for a geom or other solution that draws a nice continuous band filled with specific colour. Something like geom_density_2d() but with the stat drawn from only the X axis.
"Background" does not cover expanded area, can use coord_cartesian(expand = FALSE) but would like to cover regular margins. - Not a big deal, is nice-to-have but not required.
Setting scale_fill "consumes" the option for the plot, not allowing it to be set independently for the points themselves. - This may not be easily achievable, independent palettes for layers appears to be a fundamental issue with ggplot2.
data(iris)
dns <- density(iris$Sepal.Length)
dns_df <- tibble(
x = dns$x,
density = dns$y
)%>%
mutate(
start = x - mean(diff(x))/2,
end = x + mean(diff(x))/2
)
ggplot() +
geom_rect(
data = dns_df,
aes(xmin = start, xmax = end, fill = density),
ymin = min(iris$Sepal.Width),
ymax = max(iris$Sepal.Width),
alpha = 0.5) +
scale_fill_viridis_c(option = "A") +
geom_point(data = iris, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_rug(data = iris, aes(x = Sepal.Length))
This is a bit of a hacky solution because it (ab)uses knowledge of how objects are internally parametrised to get what you want, which will yield some warnings, but gets you want you'd want.
First, we'll use a geom_raster() + stat_density() decorated with some choice after_stat()/stage() delayed evaluation. Normally, this would result in a height = 1 strip, but by setting the internal parameters ymin/ymax to infinitives, we'll have the strip extend the whole height of the plot. Using geom_raster() resolves the alpha issue you were having.
library(ggplot2)
p <- ggplot(iris) +
geom_raster(
aes(Sepal.Length,
y = mean(Sepal.Width),
fill = after_stat(density),
ymin = stage(NULL, after_scale = -Inf),
ymax = stage(NULL, after_scale = Inf)),
stat = "density", alpha = 0.5
)
#> Warning: Ignoring unknown aesthetics: ymin, ymax
p
#> Warning: Duplicated aesthetics after name standardisation: NA
Next, we add a fill scale, and immediately follow that by ggnewscale::new_scale_fill(). This allows another layer to use a second fill scale, as demonstrated with fill = Species.
p <- p +
scale_fill_viridis_c(option = "A") +
ggnewscale::new_scale_fill() +
geom_point(aes(Sepal.Length, Sepal.Width, fill = Species),
shape = 21) +
geom_rug(aes(Sepal.Length))
p
#> Warning: Duplicated aesthetics after name standardisation: NA
Lastly, to get rid of the padding at the x-axis, we can manually extend the limits and then shrink in the expansion. It allows for an extended range over which the density can be estimated, making the raster fill the whole area. There is some mismatch between how ggplot2 and scales::expand_range() are parameterised, so the exact values are a bit of trial and error.
p +
scale_x_continuous(
limits = ~ scales::expand_range(.x, mul = 0.05),
expand = c(0, -0.2)
)
#> Warning: Duplicated aesthetics after name standardisation: NA
Created on 2022-07-04 by the reprex package (v2.0.1)
This doesn't solve your problem (I'm not sure I understand all the issues correctly), but perhaps it will help:
Background does not cover expanded area, can use coord_cartesian(expand = FALSE) but would like to cover regular margins.
If you make the 'background' larger and use coord_cartesian() you can get the same 'filled-to-the-edges' effect; would this work for your use-case?
Alpha overlap at boundaries causes banding at lower resolution as seen here.
I wasn't able to fix the banding completely, but my approach below appears to reduce it.
Setting scale_fill "consumes" the option for the plot, not allowing it to be set independently for the points themselves.
If you use geom_segment() you can map density to colour, leaving fill available for e.g. the points. Again, not sure if this is a useable solution, just an idea that might help.
library(tidyverse)
data(iris)
dns <- density(iris$Sepal.Length)
dns_df <- tibble(
x = dns$x,
density = dns$y
) %>%
mutate(
start = x - mean(diff(x))/2,
end = x + mean(diff(x))/2
)
ggplot() +
geom_segment(
data = dns_df,
aes(x = start, xend = end,
y = min(iris$Sepal.Width) * 0.9,
yend = max(iris$Sepal.Width) * 1.1,
color = density), alpha = 0.5) +
coord_cartesian(ylim = c(min(iris$Sepal.Width),
max(iris$Sepal.Width)),
xlim = c(min(iris$Sepal.Length),
max(iris$Sepal.Length))) +
scale_color_viridis_c(option = "A", alpha = 0.5) +
scale_fill_viridis_d() +
geom_point(data = iris, aes(x = Sepal.Length,
y = Sepal.Width,
fill = Species),
shape = 21) +
geom_rug(data = iris, aes(x = Sepal.Length))
Created on 2022-07-04 by the reprex package (v2.0.1)

How do I correct the scale and order of the y axis in R on a barplot

Working with borehole data, attempting to plot the cross section with R. I'm rusty and am having trouble organizing the plot the way I want. From the image, my bar plot is not tracking with y axis values displaying the depth of the borehole, instead it tracks with the Layers (categorical data).
Very similar question was asked here but I could not get the code to work for my situation because my data is formatted differently.
Just to clarify, I want to put the y axis in increasing numerical order, starting at 0, with the categorical layer data mapped to the correct part of that depth.
my code:
g2 <- ggplot(data=df3,
mapping = aes(x=PointID,y=End_Depth,
fill=`Layer`)) +
geom_col(colour="black") +
labs(y="Depth")
The Data
The question you were pointing to contains a very good idea, to use geom_rect instead. You could do something like the following (comments in code)
library(tidyverse)
# just some fake data, similar to yours
foo <- data.frame(id = "id", layer = letters[1:6], depth = c(5,10,12,15,20,25))
foo2 <-
foo %>%
# using lag to create ymin, which is needed for geom_rect
# transforming id into integers so i can add / subtract some x for xmin/xmax
mutate( ymin = lag(depth, default = 0),
id_int = as.integer(factor(id)))
# I am turning off the legend and labelling the layers directly instead
# using geom_text
# this creates a "wrong" y axis title which I'm changing with labs(y = ... )
# the continuous x axis needs to be turned into a fake discrete axis by
# semi-manually setting the breaks and labels
ggplot(foo2) +
geom_rect(aes(xmin = id_int - .5, xmax = id_int +.5,
ymin = ymin, ymax = depth,
fill = layer), show.legend = FALSE) +
geom_text(aes(x = id_int, y = (depth + ymin)/2, label = layer)) +
scale_x_continuous(breaks = foo2$id_int, labels = foo2$id) +
labs(y = "depth")
Created on 2021-10-19 by the reprex package (v2.0.1)

adding convex hull to points plotted on a map

I have managed to get quite a decent map of China now in R and I have managed plot some spatial data onto it using geom_points from two separate csv files. I think there is a way of having all the points in one file and sorting them but I have not gotten that advanced yet.
I need to turn these points into a convex hull. I have tried following steps online but I just can not get it. Here is my code:
#import data
sus_scrofa_lp <- read.csv(fileEncoding="UTF-8-BOM","C:/PhD/data/s-scrofa_lp.csv")
sus_scrofa_ho <- read.csv(fileEncoding="UTF-8-BOM","C:/PhD/data/s-scrofa_ho.csv")
#create a map of china - start
library(maptools)
china_map1 <-readOGR(dsn="C:/PhD/data/bou2_4p.shp")
#SpatialPolygonsDataFrame
class(china_map1)
length(china_map1)
china_map2 <- china_map1#data
head(china_map2)
library(ggplot2)
china_map3 <- fortify(china_map1)
head(china_map3)
china_map2$NAME <- iconv(china_map2$NAME, from = 'GBK') #the original coding format is GBK
#create a map of china - end
ggplot(china_map1, aes(x = long, y = lat, group = group)) +
geom_path(color="grey40") +
geom_polygon(fill = 'beige') +
geom_point(data=sus_scrofa_lp, aes(lon, lat), inherit.aes = FALSE, alpha = 0.5, size = 0.5, color ="red") +
geom_point(data=sus_scrofa_ho, aes(lon, lat), inherit.aes = FALSE, alpha = 0.5, size = 0.5, color ="blue") + coord_equal()
Here is what I have:points plotted on map
here is the data for s-scrofa_ho.csv https://www.dropbox.com/s/0w6iqlky5cbipch/s-scrofa_ho.csv?dl=0
here is the data for s-scrofa_lp.csv https://www.dropbox.com/s/sr49y94rby0o1sm/s-scrofa_lp.csv?dl=0

Add legend to ggmap

I am trying to add a legend to a plot generated by ggmap package in R. The dataset I am working with is
Latitude Longitude amount
61.37072 -152.40442 436774
32.80667 -86.79113 3921030
34.96970 -92.37312 1115087
33.72976 -111.43122 5068957
The code I am using is
library(ggplot2)
library(ggmap)
MyMap <- get_map(location = c(lon = -96.5, lat = 40.68925), zoom = 4,maptype = "terrain", scale = 2)
ggmap(MyMap)+
geom_point(data = data,aes(x = Longitude , y = Latitude ),size=sqrt(data$amount)/800,col='darkred', shape = 19,alpha = .5)
Now I want to add legend to this plot. The legend should show the sizes of the circles on the map correspond to certain amount. How can I do it?
The size argument should be included within the aes() section of the geom_point function, as follows:
plot <- ggmap(MyMap) +
geom_point(data = data,aes(x = Longitude , y = Latitude, size=amount), col='darkred', shape = 19,alpha = .5)
plot
If you want to have further customisation of the scale, you can use the optional argument scale_size_area() to choose the breaks and labels for the legend. For example:
plot + scale_size_area(breaks = c(436774, 1115087, 4000000, 5068957),
labels = c("436774", "1115087", "4000000", "5068957"))
Change Point Size:
If you want to adjust the size of the points, you are better off using the scale_size function, which lets you specify a range:
plot + scale_size(range = c(5,9))

ggplot2 facet plot of shapefile polygons produces strange lines

I'm working to produce a facet/lattice plot of choropleth maps that each show a how different model runs affect one variable being mapped across a number of polygons. The problem is that the output graphic produces strange lines that run between the polygons in each plot (see the graphic below).
While I've manipulated and converted the shapefile into a data frame with appropriate attributes for ggplot2, I'm not familiar with the details of how to use the package and the online documentation is limited for such a complex package. I'm not sure what parameter is causing this issue, but I suspect it may be the aes parameter.
The script:
library(rgdal, tidyr, maptools, ggplot2, dplyr, reshape2)
setwd('D:/path/to/wd')
waterloo <- read.table("waterloo-data.txt", header=TRUE, sep=',', stringsAsFactors=FALSE)
waterloo <- data.frame(waterloo$DAUID, waterloo$LA0km, waterloo$LA4_exp, waterloo$LA20km, waterloo$LA30km, waterloo$LA40km, waterloo$LA50km)
colnames(waterloo) <- c("DAUID", "LA0km", "LA10km","LA20km", "LA30km", "LA40km", "LA50km")
## Produces expenditure measurements by ID variable DAUID, using reshape2/melt
wtidy <- melt(waterloo, id.vars=c("DAUID"), measure.vars = c("LA0km", "LA10km", "LA20km", "LA30km", "LA40km", "LA50km"))
colnames(wtidy) <- c("DAUID", "BufferSize", "Expenditure")
wtidy$DAUID <- as.factor(wtidy$DAUID) # for subsequent join with wtrl_f
### READ SPATIAL DATA ###
#wtrl <- readOGR(".", "Waterloo_DA_2011_new")
wtrl <- readShapeSpatial("Waterloo_DA_2011_new")
wtrl$id <- row.names(wtrl)
wtrl_f <- fortify(wtrl)
wtrl_f <- left_join(wtrl_f, wtrl#data, by="id")
# Join wtrl fortified (wtrl_f) to either twaterloo or wtidy
wtrl_f <- left_join(wtrl_f, wtidy, by="DAUID")
### PLOT SPATIAL DATA ###
ggplot(data = wtrl_f, # the input data
aes(x = long.x, y = lat.x, fill = Variable/1000, group = BufferSize)) + # define variables
geom_polygon() + # plot the DAs
geom_path(colour="black", lwd=0.05) + # polygon borders
coord_equal() + # fixed x and y scales
facet_wrap(~ BufferSize, ncol = 2) + # one plot per buffer size
scale_fill_gradient2(low = "green", mid = "grey", high = "red", # colors
midpoint = 10000, name = "Variable\n(thousands)") + # legend options
theme(axis.text = element_blank(), # change the theme options
axis.title = element_blank(), # remove axis titles
axis.ticks = element_blank()) # remove axis ticks
The output graphic appears as follows:
Strange! I've made good progress but I don't know where ggplot is getting these lines. Any help on this would be appreciated!
PS; as an additional unrelated question, the polygon lines are rather jagged. How would I smooth these lines?
This answer helped me to solve my problem, but not before I made up this minimal example ready to post. I'm sharing it here in case it helps someone solve the same problem faster.
Problem:
I'm trying to make a basic map in R with ggplot2. The polygons are filling wrong, making extra lines.
library("ggplot2")
library("maps")
map <- ggplot(map_data("world", region = "UK"), aes(x = long, y = lat)) + geom_polygon()
map
wrong map image
Solution:
I have to set the aesthetic "group" parameter to put the polygon points in the right order, otherwise ggplot will try to plot a patch of Scotland coastline in the middle of the south coast (for example).
map <- ggplot(map_data("world", region = "UK"), aes(x = long, y = lat, group = group)) + geom_polygon()
map
OK, I managed to resolve this issue by changing the aesthetic group parameter found on page 11 of the ggplot2 manual:
http://cran.r-project.org/web/packages/ggplot2/ggplot2.pdf
The correct parameter is "group" and not the factor that is used to group the plots. The correct ggplot code:
ggplot(data = wtrl_f, # the input data
aes(x = long.x, y = lat.x, fill = Expenditure/1000, group = group)) + # define variables
geom_polygon() + # plot the DAs
geom_path(colour="black", lwd=0.025) + # DA borders
coord_equal() + # fixed x and y scales
facet_wrap(~ BufferSize, ncol = 2) + # one plot per buffer size
scale_fill_gradient2(low = "green", mid = "grey", high = "red", # colors
midpoint = 10000, name = "Expenditures\n(thousands)") + # legend options
theme(axis.text = element_blank(), # change the theme options
axis.title = element_blank(), # remove axis titles
axis.ticks = element_blank()) # remove axis ticks

Resources