Add sequential arrows to a ggplot bubbleplot - r

I have a line plot that I have placed an overlay of bubbles onto. Before I overlaid the bubbles, I was able to connect each point with an arrow to show the sequential relationship using this advice
But now that I have overlaid my bubbles, I still want to connect each bubble by an arrow as previous.
Here is my data:
X <- c(-0.373,-0.256,-0.272,0.048,0.219,0.313,0.209,0.112)
Y <- c(-0.055,-0.091,0.100,0.153,-0.139,-0.004,0.040,-0.004)
Size <- c(37,31,25,10,5,4,6,10)
Label <- c(1,2,3,4,5,6,7,8)
DF <- data.frame(X,Y,Size,Label)
Using the above advice, I can try and draw a plot with arrows connecting each bubble, but the size of the bubbles obscures the arrow heads.
ggplot(DF,aes(x=X, y=Y, size=Size,label=Label),legend=FALSE) +
geom_segment(aes(xend=c(tail(X,n=-1),NA), yend=c(tail(Y,n=-1),NA)),
size=0.3, arrow=arrow(length=unit(0.3,'cm'))) +
geom_point(color='darkblue',fill="red", shape=21) +
geom_text(size=2) +
theme_bw() +
scale_size(range = c(4, 30), name="Size", breaks=c(10,25,50),
limits = c(1, 100))
I would basically like the above plot, but with the arrow heads visible. I know it is possible to write the arrows overtop the bubbles so I can see each arrow, but that is not what I am looking for. What I would like would be an arrow drawn from the outer edge of one bubble to the outer edge of the next bubble. So I need someway to shorten the head of each arrow by the radius of bubble it is pointing to.
And I have no idea why I get the warning at the end
Removed 1 rows containing missing values (geom_segment).

You can start with the following:
Size_penalty <- 1000
X <- c(-0.373,-0.256,-0.272,0.048,0.219,0.313,0.209,0.112)
X_next <- c(X[-1], NA)
Y <- c(-0.055,-0.091,0.100,0.153,-0.139,-0.004,0.040,-0.004)
Y_next <- c(Y[-1], NA)
Arrow_length <- sqrt((X - X_next)^2 + (Y - Y_next)^2)
Size <- c(37,31,25,10,5,4,6,10)
Size_next <- c(Size[-1], NA)
X_begin <- X + Size / Size_penalty * (X_next - X) / Arrow_length
Y_begin <- Y + Size / Size_penalty * (Y_next - Y) / Arrow_length
X_end <- X_next + Size_next / Size_penalty * (X - X_next) / Arrow_length
Y_end <- Y_next + Size_next / Size_penalty * (Y - Y_next) / Arrow_length
Label <- c(1,2,3,4,5,6,7,8)
DF <- data.frame(X, Y, X_begin, Y_begin, X_end, Y_end, Size, Label)
ggplot(DF, aes(x=X, y=Y, size=Size, label=Label),legend=FALSE) +
geom_point(color='darkblue', fill="red", shape=21) +
geom_segment(aes(x=X_begin, y=Y_begin, xend=X_end, yend=Y_end),
size=0.3, arrow=arrow(length=unit(0.3, 'cm'))) +
geom_text(size=4) +
theme_bw() +
scale_size(range = c(4, 30), name="Size", breaks=c(10, 25, 50),
limits = c(1, 60))
Here I use Size / Size_penalty as a proxy to bubble radius, which is obviously quite far from being elegant. But this is the best I can do, since there's a scale_size, so that conversion from size to radius is implicit. All that is left is to find a conversion function like
rad <- function(ggplot_size_after_scaling) {}

Related

Case dependent scaling of plot size in ggplot loop

I am running a several ggplot barplots in a loop, including added text on top of each bar. I have defined plot scale via coord_fixed and expand_limits. Unfortunately, the y-axis differs from plot to plot, so that scale settings will not fit in all cases, i.e. the text gets cut off and/or the axes get compressed. Let me illustrate:
period <- c(rep("A",4),rep("B",4))
group <- rep(c("C","C","D","D"),2)
size <- rep(c("E","F"),4)
value <- c(23,29,77,62,18,30,54,81)
df <- data.frame(period,group,size,value)
library(ggplot2)
for (i in levels(df$group))
{
p <- ggplot(subset(df, group==i), aes(x=size, y=value, fill = period)) +
geom_bar(position="dodge", stat="identity", show.legend=F) +
geom_text(data=subset(df, group==i), aes(x=size, y=value,label=value),
size=10, fontface="bold", position = position_dodge(width=1),vjust = -0.5) +
expand_limits(y = max(df$value)*0.6) +
coord_fixed(ratio = 0.01)
ggsave(paste0("yourfilepath",i,".png"), width=7.72, height=4.5, units="in", p)
}
I would like the settings of coord_fixed and expand_limits to be case sensitive, dependening on value. I have experimented with using e.g. expand_limits(y = max(df$value * ifelse(df$value <= 50, 0.6, 1))), but that doesn't work in the way I had hoped. Any suggestions will be greatly appreciated!
Based on #Z.Lin's comment, I have added the df$value[df$group==i] argument to my ifelse function: expand_limits(y = max(df$value[df$group==i] * ifelse(df$value[df$group==i] <= 50, 5, 8))).

Annotating a polar ggplot with segments - straight lines and unintended coordinate shifts [duplicate]

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()

Restructure geom_tile ggplot2

I have a 4x4 geom_tile plot where the upper right cell contains an additional 4x4 cell. I am wanting the plot to appear as it does below.
The positions are listed inside the cells for clarity, however, the code makes the data value corresponding with the position placed in the cell. The sample code I provided currently produces this:
I am not able to manipulate the data values in terms of position (values for x and y cannot be changed) and the cell containing additional breakdown will not always be in the upper right so a solution that does not hardcode this location in would be appreciated.
If this is possible I would greatly appreciate any assistance you can provide!
x <- c(0,0,1,0.5,0.5,1,1)
y <- c(0,1,0,0.5,1,0.5,1)
data_val <- sample(0:100, 7)
alldata <-data.frame(x, y, data_val)
ggplot(data= alldata, aes(x, y)) +
geom_tile(colour = "black") +
geom_text(aes(label = data_val),colour="white")
This requires a slight change to your notation, so that x and y correctly locate the centre of each square, and an additional width parameter (which with this system really only depends on whether x and y are even or odd). For other quartered squares, use -1/+1 instead of 3/5 as appropriate.
x <- c(0,0,4,3,3,5,5)
y <- c(0,4,0,3,5,3,5)
w <- ifelse(x%%2==0, 4, 2)
data_val <- sample(0:100, 7)
alldata <- data.frame(x, y, w, data_val)
ggplot(data= alldata, aes(x=x, y=y, width=w, height=w)) +
geom_tile(fill = "white", color="black") +
geom_text(aes(label = data_val), colour="black") +
coord_fixed()

R - ggplot2 - Add arrow if geom_errorbar outside limits

I am creating a figure using ggplot and would like to use arrows to indicate where my error bars go beyond the defined axis. For example, I would like to end up with a figure that looks like:
I want R to determine which lower bounds are outside the defined chart range and to add a nice looking arrow (instead of my ugly paint added arrows).
I know there has to be a way to do this. Any ideas? Here is my code to make the above graph without the arrows added by-hand:
#generate data
myData<-data.frame(ALPHA=round(runif(60,.5,.8),2),
error=round(runif(60,.05,.15),2),
formN=rep(1:5,12),
Cat=c(rep("ELL",30),rep("SWD",30)),
grade=rep(c(rep(3,5),rep(4,5),rep(5,5),rep(6,5),rep(7,5),rep(8,5)),2)
)
myData$LCL<-myData$ALPHA-myData$error
myData$UCL<-myData$ALPHA+myData$error
#set error outside of range for example
myData[myData$Cat=="ELL" & formN==1,"LCL"]<-0
library(ggplot2)
ggplot(myData, aes(x=formN, y=ALPHA, colour=Cat)) +
geom_errorbar(aes(ymin=LCL, ymax=UCL), width=.4, position=position_dodge(.5)) +
geom_point(position=position_dodge(.5), size=2) +
labs(x="Form", y="Alpha", title="TITLE") +
geom_line(position=position_dodge(.5), size=.3) +
coord_cartesian(ylim=c(.3, 1)) +
facet_wrap(~grade, ncol=3)
What about this: first create a column to check if the values go beyond your range and if this is the case determine the length from the y-point to the border of the plot.
library(dplyr)
myData_m <- myData %>% mutate(LCL_l = ifelse(LCL < .3, ALPHA - .3, NA), UCL_l = ifelse(UCL > 1, 1 - ALPHA, NA))
In the second step use this variable to add arrows with segment. If there are also values going through the upper limit you can additionally use the other variable ULC_l to add further arrows.
ggplot(myData_m, aes(x=formN, y=ALPHA, colour=Cat)) +
geom_errorbar(aes(ymin=LCL, ymax=UCL), width=.4, position=position_dodge(.5)) +
geom_point(position=position_dodge(.5), size=2) +
labs(x="Form", y="Alpha", title="TITLE") +
geom_line(position=position_dodge(.5), size=.3) +
coord_cartesian(ylim=c(.3, 1)) +
facet_wrap(~grade, ncol=3) +
geom_segment(aes(x = formN - .12, xend = formN - .12, y = ALPHA, yend = ALPHA - LCL_l), arrow = arrow(length = unit(myData_m$LCL_l, "cm")))
P.S.: the -.12 is used to get rid of the dodging effect to the arrows.

march madness brackets with ggplot2

I'm trying to make a march madness bracket with ggplot2 and am having issues annotating the plot. Specifically, I've found that while I have no issues placing team names, I can't get the text to keep an absolute size. Hence, depending on the size of the window from which users view their ggplot object some team names look too big.
To demonstrate what I'm talking about, look at the following two images:
small bracket and medium bracket
In both images the size of the text is unchanged, despite the length of the bars being different. I'd like to know if there's a way to have the size of the text scale with the image, as opposed to being fixed in size. (My code is below; I apologize in advance for how tortuous it may seem.)
### Helper functions
first_evens <- function(x) {seq(from=2,to=2*x,length.out=x)}
first_odds <- function(x) {seq(from=1,to=2*x-1,length.out=x)}
### calculate y-values for horizontal lines:
### this is for top-left corner of the bracket,
### but multiplying sequences by -1 makes these
### values work for bottom right and left corners;
### final round has teams at y=2*off.set
r1.y.width <- 1.5*strheight(s="Virginia Common",units="in") # this effects the width of the first round
r1.y.offset <- 0.125*r1.y.width # this effects distance from y=0
r1.y <- seq(from=r1.y.offset,to=r1.y.offset+r1.y.width,length.out=16)
r2.y <- seq(from=mean(r1.y[1:2]),to=mean(r1.y[15:16]),length.out=8)
r3.y <- seq(from=mean(r2.y[1:2]),to=mean(r2.y[7:8]),length.out=4)
r4.y <- seq(from=mean(r3.y[1:2]),to=mean(r3.y[3:4]),length.out=2)
r5.y <- seq(from=mean(r4.y[1:2]),to=mean(r4.y[1:2]),length.out=1)
r6.y <- 1.5*r1.y.offset
### calculate horizontal bar start and stop coordinates
### note that there are 6 total rounds -- 5 rounds per quadrant
r1.x.width <- 1.25*strwidth("Viriginia Commonwealth","inches") # how long should horizontal lines be?
r1.x.offset <- 1
round.break.points <- -(seq(from=0,to=7*r1.x.width,by=r1.x.width)+r1.x.offset)
r1.x <- round.break.points[7:6]
r2.x <- round.break.points[6:5]
r3.x <- round.break.points[5:4]
r4.x <- round.break.points[4:3]
r5.x <- round.break.points[3:2]
r6.x <- round.break.points[2:1]
### calculate verticals line coordinates: these are based off of
### r1.y values. Round 5 verticals need to connect the four subtrees
### via the top-left <-> bottom-left and top-right <-> bottom-right
r1.verticals.start <- r1.y[first_odds(8)]
r1.verticals.stop <- r1.y[first_evens(8)]
r2.verticals.start <- r2.y[first_odds(4)]
r2.verticals.stop <- r2.y[first_evens(4)]
r3.verticals.start <- r3.y[first_odds(2)]
r3.verticals.stop <- r3.y[first_evens(2)]
r4.verticals.start <- r4.y[first_odds(1)]
r4.verticals.stop <- r4.y[first_evens(1)]
r5.verticals.start <- r5.y[1]
r5.verticals.stop <- -r5.y[1]
empty.bracket <- ggplot() + theme_bw() + theme(axis.line=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank(), axis.title.x=element_blank(), axis.title.y=element_blank(), panel.border=element_blank(), panel.grid.major.x=element_blank(), panel.grid.minor.x=element_blank(), panel.grid.major.y=element_blank(), panel.grid.minor.y=element_blank(), plot.margin=unit(c(0,0,-6,-6),"mm"), text=element_text(size=12,hjust=0,vjust=0)) + coord_cartesian(ylim = c(-1.05*r1.y[16],1.05*r1.y[16]), xlim = c(1.025*r1.x[1],-1.025*r1.x[1]))
### add first round bars, and vertical connectors, make addition of each quadrant verbose
empty.bracket <- empty.bracket + geom_segment(aes(x=r1.x[1],y=r1.y,yend=r1.y,xend=r1.x[2])) + geom_segment(aes(x=r1.x[2],xend=r1.x[2],y=r1.verticals.start,yend=r1.verticals.stop)) + geom_segment(aes(x=r1.x[1],y=-r1.y,yend=-r1.y,xend=r1.x[2])) + geom_segment(aes(x=r1.x[2],xend=r1.x[2],y=-r1.verticals.start,yend=-r1.verticals.stop)) + geom_segment(aes(x=-r1.x[1],y=r1.y,yend=r1.y,xend=-r1.x[2])) + geom_segment(aes(x=-r1.x[2],xend=-r1.x[2],y=r1.verticals.start,yend=r1.verticals.stop)) + geom_segment(aes(x=-r1.x[1],y=-r1.y,yend=-r1.y,xend=-r1.x[2])) + geom_segment(aes(x=-r1.x[2],xend=-r1.x[2],y=-r1.verticals.start,yend=-r1.verticals.stop))
### add second round
empty.bracket <- empty.bracket + geom_segment(aes(x=r2.x[1],y=r2.y,yend=r2.y,xend=r2.x[2])) + geom_segment(aes(x=r2.x[2],xend=r2.x[2],y=r2.verticals.start,yend=r2.verticals.stop)) + geom_segment(aes(x=r2.x[1],y=-r2.y,yend=-r2.y,xend=r2.x[2])) + geom_segment(aes(x=r2.x[2],xend=r2.x[2],y=-r2.verticals.start,yend=-r2.verticals.stop)) + geom_segment(aes(x=-r2.x[1],y=r2.y,yend=r2.y,xend=-r2.x[2])) + geom_segment(aes(x=-r2.x[2],xend=-r2.x[2],y=r2.verticals.start,yend=r2.verticals.stop)) + geom_segment(aes(x=-r2.x[1],y=-r2.y,yend=-r2.y,xend=-r2.x[2])) + geom_segment(aes(x=-r2.x[2],xend=-r2.x[2],y=-r2.verticals.start,yend=-r2.verticals.stop))
### add third round
empty.bracket <- empty.bracket + geom_segment(aes(x=r3.x[1],y=r3.y,yend=r3.y,xend=r3.x[2])) + geom_segment(aes(x=r3.x[2],xend=r3.x[2],y=r3.verticals.start,yend=r3.verticals.stop)) + geom_segment(aes(x=r3.x[1],y=-r3.y,yend=-r3.y,xend=r3.x[2])) + geom_segment(aes(x=r3.x[2],xend=r3.x[2],y=-r3.verticals.start,yend=-r3.verticals.stop)) + geom_segment(aes(x=-r3.x[1],y=r3.y,yend=r3.y,xend=-r3.x[2])) + geom_segment(aes(x=-r3.x[2],xend=-r3.x[2],y=r3.verticals.start,yend=r3.verticals.stop)) + geom_segment(aes(x=-r3.x[1],y=-r3.y,yend=-r3.y,xend=-r3.x[2])) + geom_segment(aes(x=-r3.x[2],xend=-r3.x[2],y=-r3.verticals.start,yend=-r3.verticals.stop))
### add fourth round
empty.bracket <- empty.bracket + geom_segment(aes(x=r4.x[1],y=r4.y,yend=r4.y,xend=r4.x[2])) + geom_segment(aes(x=r4.x[2],xend=r4.x[2],y=r4.verticals.start,yend=r4.verticals.stop)) + geom_segment(aes(x=r4.x[1],y=-r4.y,yend=-r4.y,xend=r4.x[2])) + geom_segment(aes(x=r4.x[2],xend=r4.x[2],y=-r4.verticals.start,yend=-r4.verticals.stop)) + geom_segment(aes(x=-r4.x[1],y=r4.y,yend=r4.y,xend=-r4.x[2])) + geom_segment(aes(x=-r4.x[2],xend=-r4.x[2],y=r4.verticals.start,yend=r4.verticals.stop)) + geom_segment(aes(x=-r4.x[1],y=-r4.y,yend=-r4.y,xend=-r4.x[2])) + geom_segment(aes(x=-r4.x[2],xend=-r4.x[2],y=-r4.verticals.start,yend=-r4.verticals.stop))
### add fifth round: add necessary horizontal bars and then
### vertical bars
empty.bracket <- empty.bracket + geom_segment(aes(x=r5.x[1],y=r5.y,yend=r5.y,xend=r5.x[2])) + geom_segment(aes(x=r5.x[1],y=-r5.y,yend=-r5.y,xend=r5.x[2])) + geom_segment(aes(x=r5.x[2],y=-r5.y, yend=r5.y, xend=r5.x[2])) + geom_segment(aes(x=-r5.x[1],y=r5.y,yend=r5.y,xend=-r5.x[2])) + geom_segment(aes(x=-r5.x[1],y=-r5.y,yend=-r5.y,xend=-r5.x[2])) + geom_segment(aes(x=-r5.x[2],y=-r5.y,yend=r5.y,xend=-r5.x[2]))
### due to symmetry, the 6th (and final round)
empty.bracket <- empty.bracket + geom_segment(aes(x=r6.x[1],y=r6.y,xend=r6.x[2],yend=r6.y)) + geom_segment(aes(x=-r6.x[1],y=-r6.y,xend=-r6.x[2],yend=-r6.y))
### add winner location
empty.bracket <- empty.bracket + geom_segment(aes(x=mean(r6.x),xend=-mean(r6.x),y=0,yend=0))
### put some test labels on the bracket slots
Labels <- c("Alabama", "Alaska", "Arizona", "Arkansas", "Virginia Commonwealth")
TextFrame <- data.frame(X = r1.x[1], Y = sample(r1.y,5), LAB = Labels)
TextFrame <- transform(TextFrame, w = strwidth(LAB, 'inches') + 0.05, h = strheight(LAB, 'inches') + 0.5)
### display results
empty.bracket + geom_rect(data = TextFrame, aes(xmin = X, xmax = X + w, ymin = Y, ymax = Y + h),alpha=0) + geom_text(data=TextFrame,aes(x=X,y=Y,label=LAB),size=rel(3),hjust=0,vjust=0)
I'll take your question in a few separate parts.
Window size:
Hence, depending on the size of the window from
which users view their ggplot object some team names
look too big.
When viewing an R graphic, the text size remains constant, whilst the graphic objects scale to the window. This means that what you see in an R graph window, isn't necessary the same as what you'll get when you print it out (unless you use the savePlot command).
Text scaling:
In both images the size of the text is unchanged, despite the
length of the bars being different. I'd like to know if there's a
way to have the size of the text scale with the image, as opposed to
being fixed in size.
I believe the short answer is no. You specify the text size and it's fixed. However, if outputting to a pdf device, you could scale the text size according to the width of the pdf.

Resources