I am revising a problem I posted prevously Limited number of geoms in ggforce?. I thought then that I was mistaken but I can now reconstruct it more clearly:
I would like to plot N circles one on top of the other. No matter how many circles I want to plot, circle #11-N are plotted underneath the first 10 circles. This demonstartes the problem:
library(tidyverse)
library(ggforce)
circles <- data.frame(
x0 = seq(1, 30),
y0 = seq(1, 30),
r = 2
)
ggplot() +
geom_circle(aes(x0 = x0, y0 = y0, r = r), fill = "grey", data = circles) +
coord_fixed()
Therefore when I would like to plot concentric circles, the first 10 circles hide all the rest.
I can code a workaround by first plotting the 11-N circles and then the first ten but its not elegant
This is a bug in ggforce: it groups the points in the circles with strings that don't sort into the same order as the original circles.
To get a version that fixes the bug, use
remotes::install_github("dmurdoch/ggforce#sortorder")
This will require you to have package build tools installed. If you don't have those, you could watch the web site https://github.com/thomasp85/ggforce/issues/224 to see when the fix gets incorporated into the CRAN build of the package.
I can only offer a slightly hacky workaround that may not perform very well for large datasets. Loop through each of the rows creating a single circle then add them all together to make a single object to plot.
library(tidyverse)
library(ggforce)
circles <- data.frame(
x0 = seq(1, 30),
y0 = seq(1, 30),
r = 2
)
mygeom_circle <- function(onerow) {
geom_circle(aes(x0 = x0, y0 = y0, r = r), fill = "grey", data = onerow)
}
gs <- sapply(1:nrow(circles), function(r) mygeom_circle(circles[r,])) # loop through one by one making single circles
gg <-
ggplot() +
gs + # add the single circles in the order they were made
coord_fixed()
gg
Related
I see from here (http://sape.inf.usi.ch/quick-reference/ggplot2/shape) the set of possible shapes. If I wanted to define new shapes, is it possible? For example, suppose I wanted to use a 7-sided polygon with an optional fill aesthetic -- is there a way to tell ggplot about that shape?
I feel constrained by this set of possibilities:
library(tidyverse)
dat <- tibble(p = c(0:25, 32:127),
x = p %% 16,
y = p %/% 16)
ggplot(dat, aes(x, y)) +
geom_text(aes(label = p), size = 3, nudge_y = -.25) +
geom_point(aes(shape = p), size = 5, fill = "red") +
scale_shape_identity() +
theme_void()
Yes, it's possible to do this in one of several ways. Unless you have an svg file of a 7-sided polygon available, one quick solution would be to define this shape as a grob and plot it using geom_grob from package ggpmisc. This keeps things in vector format.
Creating the heptagon is the hard part:
library(ggplot2)
library(dplyr)
library(grid)
library(ggpmisc)
# Make heptagon
septs <- seq(0, 2 * pi, length.out = 8)
devratio <- dev.size()[2]/dev.size()[1]
heptagon <- linesGrob(x = unit(0.5 + 0.2 * devratio * sin(septs), "npc"),
y = unit(0.5 + 0.2 * cos(septs), "npc"),
gp = gpar(lwd = 2))
The plot itself is straightforward:
# Plot 10 random points with the heptagon
set.seed(69)
tibble(x = rnorm(10), y = rnorm(10), shape = list(heptagon)) %>%
ggplot() +
geom_grob(aes(x, y, label = shape))
As you can see from this example, custom shapes aren't necessarily all that easy to use, since the shape has to be defined pointwise by the user, it is difficult to match its size and lineweight to existing points, and the user would have to define what/where their fill is, etc. I don't think it's an omission from ggplot to not have a simple interface for creating custom shapes - ggplot has great extensibility for advanced users, and it's not clear that you could have a useful shape-creating interface for beginners. Meanwhile there are more than enough shapes to provide informative plots for all but the most niche applications.
Maybe not exactly what you're looking for but let me suggest three packages:
ggimage - allows you to use images, like a .png file, for your points. See https://mran.microsoft.com/snapshot/2018-04-02/web/packages/ggimage/vignettes/ggimage.html
ggpattern - allows you to add different fills to your graphics. See https://github.com/coolbutuseless/ggpattern
emoGG - uses emojis for your points. See https://github.com/dill/emoGG
I'm trying to replicate the image below in R (Original post). I have seen similar posts (Post 1 and Post 2) but none similar to this plot. I'm just wondering if anyone knows how to do something similar in R. There's a couple of observations:
Bubbles do not overlap
Smaller bubbles tend to be closer to the axis (but not always!)
Bubbles are in two categories
I'm sure that data from Post 1 would be helpful!
https://docs.google.com/spreadsheets/d/11nq5AK3Y1uXQJ8wTm14w9MkZOwAxHHTyPyEvUxCeGVc/edit?usp=sharing
Thank you so much,
Ok so this is just a starting point that people could use to formulate a better answer to the question. It uses the packcircles package to (surprisingly) pack circles. It doesn't qualify all of your criteria, but can serve as a useful starting point. We're just going to pretend that the eruptions column from the faithful dataset is your time variable.
library(packcircles)
#> Warning: package 'packcircles' was built under R version 4.0.2
library(ggplot2)
library(scales)
library(ggrepel)
# Setup some data, suppose we'd like to label 5 samples
set.seed(0)
faith2 <- faithful
faith2$label <- ""
faith2$label[sample(nrow(faith2), 5)] <- LETTERS[1:5]
# Initialise circle pack data
init <- data.frame(
x = faith2$eruptions,
y = runif(nrow(faith2)),
areas = rescale(faith2$waiting, to = c(0.01, 0.1))
)
# Use the repelling layout
res <- circleRepelLayout(
init,
xlim = range(init$x) + c(-1, 1),
ylim = c(0, Inf),
xysizecols = c(1, NA, 3),
sizetype = "radius",
weights = 0.1
)
# Prepare for ggplot2
df <- circleLayoutVertices(res$layout)
df <- cbind(df, faith2[df$id,])
This is showing that the circles are reasonably placed with respect to our fake time variable.
# Plot
ggplot(df, aes(x, y, group = id)) +
geom_polygon(aes(fill = eruptions,
colour = I(ifelse(nzchar(label), "black", NA)))) +
scale_fill_viridis_c() +
coord_equal()
And this is showing that the circle size is reasonably corresponding to a different variable.
ggplot(df, aes(x, y, group = id)) +
geom_polygon(aes(fill = waiting,
colour = I(ifelse(nzchar(label), "black", NA)))) +
scale_fill_viridis_c() +
coord_equal()
Created on 2020-07-11 by the reprex package (v0.3.0)
There are few flaws in this, notably it doesn't satisfy the 2nd criterion (circles aren't hugging the axis). Also, for reasons beyond my understanding, the packcircles layout couldn't place about 12% of datapoints, which are assigned NaN in df. Anyway, hopefully somebody smarter than me will do a better job at this.
This question already has an answer here:
ggplot2: connecting points in polar coordinates with a straight line 2
(1 answer)
Closed 2 years ago.
I am trying to use ggplot to draw a radar-chart following the guidelines from the Grammar of Graphics. I am aware of the ggradar package but based on the grammar it looks like coord_polar should be enough here. This is the pseudo-code from the grammar:
So I thought something like this may work, however, the contour of the area chart is curved as if I used geom_line:
library(tidyverse)
dd <- tibble(category = c('A', 'B', 'C'), value = c(2, 7, 4))
ggplot(dd, aes(x = category, y = value, group=1)) +
coord_polar(theta = 'x') +
geom_area(color = 'blue', alpha = .00001) +
geom_point()
While I understand why geom_line draws arcs once in coord_polar, my understanding of the explanation from the Grammar of Graphics is that there may be an element/geom area that could plot straight lines:
here is one technical detail concerning the shape of Figure 9.29. Why
is the outer edge of the area graphic a set of straight lines instead
of arcs? The answer has to do with what is being measured. Since
region is a categorical variable, the line segments linking regions
are not in a metric region of the graph. That is, the segments of the
domain between regions are not measurable and thus the straight lines
or edges linking them are arbitrary and perhaps not subject to
geometric transformation. There is one other problem with the
grammatical specification of this figure. Can you spot it? Undo the
polar trans- formation and think about the domain of the plot. We
cheated.
For completeness, this question derives from this other question I asked about plotting in polar system.
tl;dr we can write a function to solve this problem.
Indeed, ggplot uses a process called data munching for non-linear coordinate systems to draw lines. It basically breaks up a straight line in many pieces, and applies the coordinate transformation on the individual pieces instead of merely the start- and endpoints of lines.
If we look at the panel drawing code of for example GeomArea$draw_group:
function (data, panel_params, coord, na.rm = FALSE)
{
...other_code...
positions <- new_data_frame(list(x = c(data$x, rev(data$x)),
y = c(data$ymax, rev(data$ymin)), id = c(ids, rev(ids))))
munched <- coord_munch(coord, positions, panel_params)
ggname("geom_ribbon", polygonGrob(munched$x, munched$y, id = munched$id,
default.units = "native", gp = gpar(fill = alpha(aes$fill,
aes$alpha), col = aes$colour, lwd = aes$size * .pt,
lty = aes$linetype)))
}
We can see that a coord_munch is applied to the data before it is passed to polygonGrob, which is the grid package function that matters for drawing the data. This happens in almost any line-based geom for which I've checked this.
Subsequently, we would like to know what is going on in coord_munch:
function (coord, data, range, segment_length = 0.01)
{
if (coord$is_linear())
return(coord$transform(data, range))
...other_code...
munched <- munch_data(data, dist, segment_length)
coord$transform(munched, range)
}
We find the logic I mentioned earlier that non-linear coordinate systems break up lines in many pieces, which is handled by ggplot2:::munch_data.
It would seem to me that we can trick ggplot into transforming straight lines, by somehow setting the output of coord$is_linear() to always be true.
Lucky for us, we wouldn't have to get our hands dirty by doing some deep ggproto based stuff if we just override the is_linear() function to return TRUE:
# Almost identical to coord_polar()
coord_straightpolar <- function(theta = 'x', start = 0, direction = 1, clip = "on") {
theta <- match.arg(theta, c("x", "y"))
r <- if (theta == "x")
"y"
else "x"
ggproto(NULL, CoordPolar, theta = theta, r = r, start = start,
direction = sign(direction), clip = clip,
# This is the different bit
is_linear = function(){TRUE})
}
So now we can plot away with straight lines in polar coordinates:
ggplot(dd, aes(x = category, y = value, group=1)) +
coord_straightpolar(theta = 'x') +
geom_area(color = 'blue', alpha = .00001) +
geom_point()
Now to be fair, I don't know what the unintended consequences are for this change. At least now we know why ggplot behaves this way, and what we can do to avoid it.
EDIT: Unfortunately, I don't know of an easy/elegant way to connect the points across the axis limits but you could try code like this:
# Refactoring the data
dd <- data.frame(category = c(1,2,3,4), value = c(2, 7, 4, 2))
ggplot(dd, aes(x = category, y = value, group=1)) +
coord_straightpolar(theta = 'x') +
geom_path(color = 'blue') +
scale_x_continuous(limits = c(1,4), breaks = 1:3, labels = LETTERS[1:3]) +
scale_y_continuous(limits = c(0, NA)) +
geom_point()
Some discussion about polar coordinates and crossing the boundary, including my own attempt at solving that problem, can be seen here geom_path() refuses to cross over the 0/360 line in coord_polar()
EDIT2:
I'm mistaken, it seems quite trivial anyway. Assume dd is your original tibble:
ggplot(dd, aes(x = category, y = value, group=1)) +
coord_straightpolar(theta = 'x') +
geom_polygon(color = 'blue', alpha = 0.0001) +
scale_y_continuous(limits = c(0, NA)) +
geom_point()
I want to plot the (spatial) correlation pattern between two variables and overlapped by countries borders. I use borders() function with ggplot but the xlim and ylim didn't give the exact limit I expect, instead, they give all the country. I want to have the exact limits to overlap the data.
library(ncdf4)
library(ggplot2)
library(reshape)
library(maps)
library(stringr)
library(reshape2)
library(mapdata)
library(maptools)
ncfile <- nc_open("/Volumes/KIT/CHIRPS/index/correlation/correlation.amm.allindex.nc")
lon <- ncvar_get(ncfile, "lon")
lat <- ncvar_get(ncfile, "lat")
pr <- ncvar_get(ncfile, "prptotcorrel")
ret <- list ("lat"=lat, "lon"=lon,"pr"= pr)
##
str(ret)
######## melt function for pr
melt_pr <- function(L) {
dimnames(L$pr) <- list(lon = L$lon, lat = L$lat)
rett <- melt(L$pr, value.name ="pr")
}
######
mpr <- melt_pr(ret)
head(mpr)
#
xlims=range(mpr$lon); xlims
ylims=range(mpr$lat); ylims
gcorrelation=ggplot(data = mpr, aes(x = lon, y = lat, fill = pr)) +
geom_tile() +
borders('world', xlim=xlims, ylim=ylims, colour='black', size=.2) +
coord_fixed(xlim=xlims, ylim=ylims) +
labs(x = "Longitude", y = "Latitude") +
scale_fill_gradient2(low = "blue", high = "red", mid = "white",
midpoint = 0, limit = c(-1,1), na.value = NA, name="correlation") +
theme_bw() +
coord_fixed(1.3)
gcorrelation
I expect to have country borders that fit exactly the data.
Here is what I'd like the plot to look like. Here is the image with the countries; I want just to overlap with the exact limit of shaded data.
Welcome to stack overflow. Without having a sample of your data (as code), it will be hard to help. I believe that coord_cartesian() will do what you want instead of coord_fixed()
Instead of coord_fixed() or coord_cartesian(), use the xlim and ylim parameters of coord_map().
coord_map() projects a portion of the earth, which is approximately spherical, onto a flat 2D plane using any projection defined by the mapproj package.
(As #yake84 suggested, it would be best to present a simplified version of your problem that contains code and data so people can run and test their solutions.)
Taking a step back, if you are developing fresh code, I'd avoid outdated packages like reshape, reshape2, and some of the spatial functions. Instead build on their replacements (often developed by the same people) such as tidyr, sf, and the associated functions in ggplot2, such as ggsf().
Hi #yake84 and #wibeasley, sorry I'm pretty new on Stack overflow, thanks for your suggestions, coord_map() gave the right expected map. Thanks also for the packages suggestion.
I want to make a number of symmetrical histograms to show butterfly abundance through time. Here's a site that shows the form of the graphs I am trying to create: http://thebirdguide.com/pelagics/bar_chart.htm
For ease, I will use the iris dataset.
library(ggplot2)
g <- ggplot(iris, aes(Sepal.Width)) + geom_histogram(binwidth=.5)
g + coord_fixed(ratio = .003)
Essentially, I would like to mirror this histogram below the x-axis. Another way of thinking about the problem is to create a horizontal violin diagram with distinct bins. I've looked at the plotrix package and the ggplot2 documentation but don't find a solution in either place. I prefer to use ggplot2 but other solutions in base R, lattice or other packages will be fine.
Without your exact data, I can only provide an approximate coding solution, but it is a start for you (if you add more details, I'll be happy to help you tweak the plot). Here's the code:
library(ggplot2)
noSpp <- 3
nTime <- 10
d <- data.frame(
JulianDate = rep(1:nTime , times = noSpp),
sppAbundance = c(c(1:5, 5:1),
c(3:5, 5:1, 1:2),
c(5:1, 1:5)),
yDummy = 1,
sppName = rep(letters[1:noSpp], each = nTime))
ggplot(data = d, aes(x = JulianDate, y = yDummy, size = sppAbundance)) +
geom_line() + facet_grid( sppName ~ . ) + ylab("Species") +
xlab("Julian Date")
And here's the figure.