Is there a way to plot both horizontal and vertical point ranges together on the same plot in ggplot. I understand that geom_pointrange(...) plots vertical point ranges, and that horizontal point ranges can be generated with coord_flip(...), but I'm interested in putting both together on the same plot.
set.seed(1)
df <- data.frame(x=sample(1:10,10),y=sample(1:10,10), x.range=1, y.range=2)
library(ggplot2)
ggplot(df) +
geom_pointrange(aes(x=x, y=y, ymin=y=y.range, ymax=y+y.range))
I'm looking for something like this:
ggplot(df) +
geom_pointrange(aes(x=x, y=y,
ymin=y-y.range, ymax=y+y.range,
xmin=x-x.range, xmax=x+x.range))
Which of course produces the same output as above because the xmin and xmax arguments are ignored. Evidently, there is (was) a function geom_hpointrange(...) in ggExtra, but this package has been pulled as far as I can tell.
Is geom_errorbarh what you are looking for?
ggplot(data = df, aes(x = x, y = y)) +
geom_pointrange(aes(ymin = y - y.range, ymax = y + y.range)) +
geom_errorbarh(aes(xmax = x + x.range, xmin = x - x.range, height = 0))
you can also call geompoint_range twice
ggplot(df, aes(x=x, y=y)) +
geom_pointrange(aes(ymin=y-y.range, ymax=y+y.range)) +
geom_pointrange(aes(xmin=x-x.range, xmax=x+x.range))
Related
I would like to jitter two geoms by the same amount. Consider the following minimal example:
library(ggplot2)
pdat <- data.frame(x = c(1,1,2,2,4,4,8,8),
y = c(1,1.1,2,2.2,3,3.3,4,4.4),
ymin = c(1,1.1,2,2.2,3,3.3,4,4.4)-.9^(0:7),
ymax = c(1,1.1,2,2.2,3,3.3,4,4.4)+.9^(0:7),
colour = as.factor(rep(1:2,4)))
ggplot(pdat, aes(x=x,y=y,ymin=ymin,ymax=ymax,color=colour)) +
geom_linerange(position='jitter') + geom_point(position='jitter')
ggplot(pdat, aes(x=jitter(x),y=y,ymin=ymin,ymax=ymax,color=colour)) +
geom_linerange() + geom_point()
which produces the following plots:
In both cases, the jittering is random across geoms (the points and lineranges are in different locations), whereas I would like them to be consistent for each data point (the points in the middles of the corresponding lineranges). Is this possible?
Note that I would not consider manually adding noise to the x variable to be a solution as this would destroy the ability to apply coordinate transformations. E.g., defining pdat$x2 <- pdat$x+rnorm(8)/10,
ggplot(pdat, aes(x=x2,y=y,ymin=ymin,ymax=ymax,color=colour)) +
geom_linerange() + geom_point()
looks good, but then the variance of the jitter is subject to any subsequent transformations as can be seen in
ggplot(pdat,aes(x=x2,y=y,ymin=ymin,ymax=ymax,color=colour)) +
geom_linerange() + geom_point() + scale_x_log10()
Using position_jitter function, you can add a seed value to get reproducible jitter effect:
library(ggplot2)
ggplot(pdat, aes(x = x, y = y, ymin = ymin, ymax = ymax, color = colour))+
geom_point(position = position_jitter(seed = 123, width =0.2))+
geom_linerange(position = position_jitter(seed = 123, width = 0.2))
Does it answer your question ?
This question already has an answer here:
draw straight line between any two point when using coord_polar() in ggplot2 (R)
(1 answer)
Closed 2 years ago.
I am making a polar violin plot. I would like to add lines and labels to the plot to annotate what each spoke means.
I'm running into two problems.
The first is that when I try to create line segments, if x != xend, then the segments are drawn as curves rather than as lines.
For example:
data.frame(
x = rnorm(1000),
spoke = factor(sample(1:6, 1000, replace=T))
) %>%
ggplot(aes(x = spoke, fill=spoke, y = x)) +
geom_violin() +
coord_polar() +
annotate("segment", x=1.1, xend=1.3, y=0, yend=3, color="black", size=0.6) +
theme_minimal()
The second problem that arises occurs when I try to add an annotation between the last spoke and the first. In this case, the annotation causes the coordinate scale to shift, so that spokes are no longer evenly distributed.
See as here:
data.frame(
x = rnorm(1000),
spoke = factor(sample(1:5, 1000, replace=T))
) %>%
ggplot(aes(x = spoke, fill=spoke, y = x)) +
geom_violin() +
coord_polar() +
scale_x_discrete(limits = 1:5) +
annotate("segment", x=5.9, xend=5.7, y=0, yend=3, color="black", size=0.6) +
theme_minimal()
Any assistance is greatly appreciated!
(PS: I do understand that there are perceptual issues with plots like these. I have a good reason...)
You want an 'generic annotation' as shown here
You basically have to overlay your plots and not use the layer facility, if you don't want to exactly calculate the distance in radians of each x for each y.
With cowplot
require(ggplot2) #again, you should specify your required packages in your question as well
require(cowplot)
my_dat <- data.frame(x = rnorm(1000),
spoke = factor(sample(1:6, 1000, replace=T)))
my_annot <- data.frame(para = c('start','end'), x = c(0,0.4), y = c(0,0.2))
#first point x/y = c(0,0) because this makes positioning easier
When I edited your question and removed the piping - that was not only a matter of good style, but also makes it much easier to then work with your different plots. So - I would suggest you should remove the pipe.
p1 <- ggplot(my_dat, aes(x = spoke, fill=spoke, y = x)) +
geom_violin() +
theme_minimal()+
coord_polar()
p2 <- ggplot(my_annot) +
geom_line(aes(x,y)) +
coord_cartesian(xlim = c(0,2), ylim =c(0,2)) +
# the limits change the length of your line too
theme_void()
ggdraw() +
draw_plot(p1) +
draw_plot(p2, x = 0.55, y = 0.6)
Obviously - you can now play around with both length of your line and its position within draw_plot()
I have faced a weird behaviour of geom_col and it drives me crazy: basically it does not plot all the data points. When I add geom_point() I clearly see that they are not all represented with a bar.
MWE :
x = sample(1:2000, size = 600, replace = FALSE)
y = 1:600
ggplot(data.frame(x = x, y = y), aes(x,y)) + geom_col() + geom_point()
It is actually plotting all vertical lines, the problem is that it is too thin to show. If you zoom in you will see all the lines.
Try to make the width of the lines thicker, e.g.
ggplot(data.frame(x = x, y = y), aes(x, y)) + geom_col(width = 3) + geom_point()
I would like to create a function that produce a ggplot graph.
data1 <- data.table(x=1:5, y=1:5, z=c(1,2,1,2,1))
data2 <- data.table(x=1:5, y=11:15, z=c(1,2,1,2,1))
myfun <- function(data){
ggplot(data, aes(x=x, y=y)) +
geom_point() +
geom_text(aes(label=y), y=3) +
facet_grid(z~.)
}
myfun(data2)
It is supposed to label some text on the graph. However, without knowing the data in advance I am unable to adjust the positions of text vertically manually. Especially I don't want the label to move positions with data: I want it always stays at about 1/4 vertically of the plots. (top-mid)
How can I do that?
Is there a function that returns the y.limit.up and y.limit.bottom then I can assign y = (y.limit.up + y.limit.bottm) / 2 or something.
Setting either x or y position in geom_text(...) relative to the plot scale in a facet is actually a pretty big problem. #agstudy's solution works if the y scale is the same for all facets. This is because, in calculating range (or max, or min, etc), ggplot uses the unsubsetted data, not the data subsetted for the appropriate facet (see this question).
You can achieve what you want using auxiliary tables, though.
data1 <- data.table(x=1:5, y=1:5, z=c(1,2,1,2,1))
data2 <- data.table(x=1:5, y=11:15, z=c(1,2,1,2,1))
myfun <- function(data){
label.pos <- data[,ypos:=min(y)+0.75*diff(range(y)),by=z] # 75% to the top...
ggplot(data, aes(x=x, y=y)) +
geom_point() +
# geom_text(aes(label=y), y=3) +
geom_text(data=label.pos, aes(y=ypos, label=y)) +
facet_grid(z~., scales="free") # note scales = "free"
}
myfun(data2)
Produces this.
If you want scales="fixed", then #agstudy's solution is the way to go.
You can do this for example:
ggplot(data2, aes(x=x)) +
geom_point(aes(y=y)) +
geom_text(aes(label=y, y=mean(range(y)))) +
facet_grid(z~.)
Or fix y limits manually:
scale_y_continuous(limits = c(10, 15))
#user890739 :
with geom_density you can estimate an ypos variable like this :
data<-dplyr::mutate(group_by(data, z), ypos=max(density(y)$y)*.75*nrow(data))
Then plot the result :
ggplot(data, aes(x=x)) +
stat_density(aes(y=..density..)) +
geom_text(aes(label=y, y=ypos)) +
facet_grid(z~., scales="free")
I've become quite fond of boxplots in which jittered points are overlain over the boxplots to represent the actual data, as below:
set.seed(7)
l1 <- gl(3, 1, length=102, labels=letters[1:3])
l2 <- gl(2, 51, length=102, labels=LETTERS[1:2]) # Will use this later
y <- runif(102)
d <- data.frame(l1, l2, y)
ggplot(d, aes(x=l1, y=y)) +
geom_point(position=position_jitter(width=0.2), alpha=0.5) +
geom_boxplot(fill=NA)
(These are particularly helpful when there are very different numbers of data points in each box.)
I'd like to use this technique when I am also (implicitly) using position_dodge to separate boxplots by a second variable, e.g.
ggplot(d, aes(x=l1, y=y, colour=l2)) +
geom_point(position=position_jitter(width=0.2), alpha=0.5) +
geom_boxplot(fill=NA)
However, I can't figure out how to dodge the points by the colour variable (here, l2) and also jitter them.
Here is an approach that manually performs the jittering and dodging.
# a plot with no dodging or jittering of the points
dp <- ggplot(d, aes(x=l1, y=y, colour=l2)) +
geom_point(alpha=0.5) +
geom_boxplot(fill=NA)
# build the plot for rendering
foo <- ggplot_build(dp)
# now replace the 'x' values in the data for layer 1 (unjittered and un-dodged points)
# with the appropriately dodged and jittered points
foo$data[[1]][['x']] <- jitter(foo$data[[2]][['x']][foo$data[[1]][['group']]],amount = 0.2)
# now draw the plot (need to explicitly load grid package)
library(grid)
grid.draw(ggplot_gtable(foo))
# note the following works without explicitly loading grid
plot(ggplot_gtable(foo))
I don't think you'll like it, but I've never found a way around this except to produce your own x values for the points. In this case:
d$l1.num <- as.numeric(d$l1)
d$l2.num <- (as.numeric(d$l2)/3)-(1/3 + 1/6)
d$x <- d$l1.num + d$l2.num
ggplot(d, aes(l1, y, colour = l2)) + geom_boxplot(fill = NA) +
geom_point(aes(x = x), position = position_jitter(width = 0.15), alpha = 0.5) + theme_bw()
It's certainly a long way from ideal, but becomes routine pretty quickly. If anyone has an alternative solution, I'd be very happy!
The new position_jitterdodge() works for this. However, it requires the fill aesthetic to tell it how to group points, so you have to specify a manual fill to get uncolored boxes:
ggplot(d, aes(x=l1, y=y, colour=l2, fill=l2)) +
geom_point(position=position_jitterdodge(width=0.2), alpha=0.5) +
geom_boxplot() + scale_fill_manual(values=rep('white', length(unique(l2))))
I'm using a newer version of ggplot2 (ggplot2_2.2.1.9000) and I was struggling to find an answer that worked for a similar plot of my own. #John Didon's answer produced an error for me; Error in position_jitterdodge(width = 0.2) : unused argument (width = 0.2). I had previous code that worked with geom_jitter that stopped working after downloading the newer version of ggplot2. This is how I solved it below - minimal-fuss code....
ggplot(d, aes(x=l1, y=y, colour=l2, fill=l2)) +
geom_point(position = position_jitterdodge(dodge.width = 1,
jitter.width = 0.5), alpha=0.5) +
geom_boxplot(position = position_dodge(width = 1), fill = NA)
Another option would be to use facets:
set.seed(7)
l1 <- gl(3, 1, length=102, labels=letters[1:3])
l2 <- gl(2, 51, length=102, labels=LETTERS[1:2]) # Will use this later
y <- runif(102)
d <- data.frame(l1, l2, y)
ggplot(d, aes(x=l1, y=y, colour=l2)) +
geom_point(position=position_jitter(width=0.2), alpha=0.5) +
geom_boxplot(fill=NA) +
facet_grid(.~l2) +
theme_bw()
Sorry, donĀ“t have enough points to post the resulting graph.