How to put labels of legend inside plot in ggplot2 - r

Context: R/ggplot2.
Is there an automated way (or even a manual way) to put the legend factors inside the plot like the energies here (Co, 4,6,10,...), instead of having them in a regular legend box next to the plot ?
Source: Radiation Oncology Physics: A Handbook for Teachers and Students, EB. Podgorsak

So this seems close. I'd characterize this as "semi-automatic": there's definitely some tweaking needed, but most of the work is done for you...
The tricky bit is not placing the text labels (geom_text(...)), but creating the breaks in the plotted curves. This is done with geom_rect(...), where the width of the rectangles are set to the maximum label width, as determined using strwidth(...).
# create sample data
df <- data.frame(x=rep(seq(0,20,.01),5),k=rep(1:5,each=2001))
df$y <- with(df,x*exp(-x/k))
library(ggplot2)
eps.x <- max(strwidth(df$k)) # maximum width of legend label
eps.y <- eps.x*diff(range(df$y))/diff(range(df$x))
ggplot(df,aes(x,y))+
geom_line(aes(group=factor(k)))+
geom_rect(data=df[df$x==5,],
aes(xmax=x+eps.x, xmin=x-eps.x, ymax=y+eps.y, ymin=y-eps.y),
fill="white", colour=NA)+
geom_text(data=df[df$x==5,],aes(x,y,label=k))+
theme_bw()
If you want to color the lines too:
ggplot(df,aes(x,y))+
geom_line(aes(color=factor(k)))+
geom_rect(data=df[df$x==5,],
aes(xmax=x+eps.x, xmin=x-eps.x, ymax=y+eps.y, ymin=y-eps.y),
fill="white", colour=NA)+
geom_text(data=df[df$x==5,],aes(x,y,label=k), colour="black")+
scale_color_discrete(guide="none")+
theme_bw()

Related

ggplot: Adding labels to lines, not endpoints

Looking for some gglot help here, for a pretty non-standard plot type. Here is code for one sample graph, but the final product will have dozens like this.
library(ggplot2)
library(data.table)
SOURCE <- c('SOURCE.1','SOURCE.1','SOURCE.2','SOURCE.2')
USAGE <- rep(c('USAGE.1','USAGE.2'),2)
RATIO <- c(0.95,0.05,0.75,0.25)
x <- data.table(SOURCE,USAGE,RATIO)
ggplot(x, aes(x=SOURCE,y=RATIO,group=USAGE)) +
geom_point() +
geom_line() +
geom_label(aes(label=USAGE))
This produces a graph with two lines, as desired. But the label geom adds text to the endpoints. What we want is the text to label the line (appearing once per line, around the middle). The endpoints will always have the same labels so it just creates redundancy and clutter (each graph will have different labels). See the attached file, mocked up in a paint programme (ignore the font and size):
I know we could use geom_line(aes(linetype=USAGE)), but we prefer not to rely on legends due to the sheer number of graphs required and because each graph is quite minimal as the vast majority will have just the two lines and the most extreme cases will only have four.
(Use of stacked bars deliberately avoided.)
You can achieve this with annotate and can move the label around by changing the x and y values.
library(ggplot2)
#library(data.table)
SOURCE<-c('SOURCE.1','SOURCE.1','SOURCE.2','SOURCE.2')
USAGE<-rep(c('USAGE.1','USAGE.2'),2)
RATIO<-c(0.95,0.05,0.75,0.25)
#x<-data.table(SOURCE,USAGE,RATIO)
df <- data.frame(SOURCE, USAGE, RATIO)
ggplot(df, aes(x=SOURCE,y=RATIO,group=USAGE)) +
geom_point() +
geom_line() +
#geom_label(aes(label=USAGE))+
annotate('text', x=1.5, y=1, label = USAGE[1])+
annotate('text', x=1.5, y=0.25, label = USAGE[2])

R ggplotly Header Area Not Uniform

I have a ggplot with facet_wrap of about 22 different plots. I'm trying to make them interactive using ggplotly, but for some reason certain rows of plots have their header area get larger to the point where I barely see the graph. It looks like all the plots in the same row have the same gray sized area. I'm just trying to generate the plot, but keep the gray title area the same size. Any help would be greatly appreciated. I've tried to look at the panel options, but couldn't find anything that would do what I needed, but I'm not sure if I'm just missing something.
p <- ggplot(data = df, aes(value, fill = FIELD))+
geom_histogram()+
facet_wrap(~variable, scales='free_x')
ggplotly(p)
I was able to replicate the error with...
library(titanic)
library(reshape2)
titanic_long <- melt(titanic_train)
p<-ggplot(data=titanic_long, aes(value))+
geom_histogram(aes(fill=Sex))+
facet_wrap(~variable, scales='free_x')
ggplotly(p)

ggplot grid with constant x-axis scale but varying axis limits

Take a look at the following plotting code:
library(ggplot2)
library(cowplot)
a <- data.frame(a1=1:10, a2=1:10)
b <- data.frame(b1=1:5, b2=2*(1:5))
aplot <- ggplot(a, aes(x=a1, ymin=0, ymax=12)) +
geom_line(aes(y=a2))
bplot <- ggplot(b, aes(x=b1, ymin=0, ymax=12)) +
geom_line(aes(y=b2))
plot_grid(aplot,bplot, ncol=2)
It yields two side-by-side plots of identical dimensions showing similar lines. But the x-axis scales are rather different. In fact, the second line has twice the slope of the first.
I am looking for a way to plot this figure so that the width of a plot is scaled by the limits of its x-axis, so that the slopes can be compared visually. The real plots I am interested in visualizing are five in number and will lack y-axis labels except for the leftmost. I can use grid.arrange() to plot them all in a row with whatever widths I want, but the problem is that I don't know what width to assign to each panel to make sure they come out right (the panel width has to be large enough to accommodate the plot margins, the y-axis tick marks, and the y-axis text). I can set the margins myself and account for them in my panel widths, but I cannot find a good way to figure out how wide (e.g. in cm) the y-axis text is.
You can use the rel_widths option in plot_grid to achieve this. You need to calculate the relative size you want each plot to be using the ratio of the ranges xmax-xmin of each panel. But, there is an extra catch. rel_widths sets the relative width of the whole panel, including the margins. So we also need to account for the margins in calculating the relative size. In the following code, adding an offset value of 2 to the numerator and denominator of relative.size works for this. But note that this offset value may change if you alter the size of the margins.
aplot <- ggplot(a, aes(x=a1, ymin=0, ymax=12, xmin=0, xmax=max(a$a1))) +
geom_line(aes(y=a2))
bplot <- ggplot(b, aes(x=b1, ymin=0, ymax=12, xmin=0, xmax=max(b$b1))) +
geom_line(aes(y=b2))
relative.size <- (2+max(b$b1)) / (2+max(a$a1)) # the addition of 2 here is to account for the plot margins
plot_grid(aplot,bplot, ncol=2, rel_widths=c(1,relative.size), align = "h")
gives

Can the minimum y-value be adjusted when using scales = "free" in ggplot?

Using the following data set:
day <- gl(8,1,48,labels=c("Mon","Tues","Wed","Thurs","Fri","Sat","Sun","Avg"))
day <- factor(day, level=c("Mon","Tues","Wed","Thurs","Fri","Sat","Sun","Avg"))
month<-gl(3,8,48,labels=c("Jan","Mar","Apr"))
month<-factor(month,level=c("Jan","Mar","Apr"))
snow<-gl(2,24,48,labels=c("Y","N"))
snow<-factor(snow,levels=c("Y","N"))
count <- c(.94,.95,.96,.98,.93,.94,.99,.9557143,.82,.84,.83,.86,.91,.89,.93,.8685714,1.07,.99,.86,1.03,.81,.92,.88,.9371429,.94,.95,.96,.98,.93,.94,.99,.9557143,.82,.84,.83,.86,.91,.89,.93,.8685714,1.07,.99,.86,1.03,.81,.92,.88,.9371429)
d <- data.frame(day=day,count=count,month=month,snow=snow)
I like the y-scale in this graph, but not the bars:
ggplot()+
geom_line(data=d[d$day!="Avg",],aes(x=day, y=count, group=month, colour=month))+
geom_bar(data=d[d$day=="Avg",],aes(x=day, y=count, fill=month),position="dodge", group=month)+
scale_x_discrete(limits=levels(d$day))+
facet_wrap(~snow,ncol=1,scales="free")+
scale_y_continuous(labels = percent_format())
I like the points, but not the scale:
ggplot(data=d[d$day=="Avg",],aes(x=day, y=count, fill=month,group=month,label=month),show_guide=F)+
facet_wrap(~snow,ncol=1,scales="free")+
geom_line(data=d[d$day!="Avg",],aes(x=day, y=count, group=month, colour=month), show_guide=F)+
scale_x_discrete(limits=levels(d$day))+
scale_y_continuous(labels = percent_format())+
geom_point(aes(colour = month),size = 4,position=position_dodge(width=1.2))
How to combine the desirable qualities in the above graphs?
Essentially, I'm asking: How can I graph the points with a varied y-max while setting the y-min to zero?
Note: The solution that I'm aiming to find will apply to about 27 graphs built from one dataframe. So I'll vote up those solutions that avoid alterations to individual graphs. I'm hoping for a solution that applies to all the facet wrapped graphs.
Minor Questions (possibly for a separate post):
- How can I add a legend to each of the facet wrapped graphs? How
can I change the title of the legend to read "Weekly Average"? How
can the shape/color of the lines/points be varied and then reported
in one single legend?
there's expand_limits(y=0), which essentially adds a dummy layer with invisible geom_blank only to stretch the scales.

Fix for overflowing x-axis text in ggplot2

I've created custom, two level x-axis entries that tend to work pretty well. The only problem is that when my y-axis, proportion, is close to one, these axis entries spill onto the chart area. When I use vjust to manually alter their vertical position, part of each entry is hidden by the chart boundary.
Any suggestions for how to make chart boundaries that dynamically adjust to accommodate large y-axis values and the full text of each entry (without running on to the chart).
Have a look at the following example:
library(ggplot2)
GroupType <- rep(c("American","European"),2)
Treatment <- c(rep("Smurf",2),rep("OompaLoompa",2))
Proportion <- rep(1,length(GroupType))
PopulationTotal <- rep(2,length(GroupType))
sampleData <- as.data.frame(cbind(GroupType,Treatment,Proportion,PopulationTotal))
hist_cut <- ggplot(sampleData, aes(x=GroupType, y=Proportion, fill=Treatment, stat="identity"))
chartCall<-expression(print(hist_cut + geom_bar(position="dodge") + scale_x_discrete(breaks = NA) +
geom_text(aes(label = paste(as.character(GroupType),"\n[N=",PopulationTotal,"]",sep=""),y=-0.02),size=4) + labs(x="",y="",fill="")
))
dev.new(width = 860, height = 450)
eval(chartCall)
Any thoughts about how I can fix the sloppy x-axis text?
Many thanks in advance,
Aaron
Unfortunately you have to manage the y axis yourself - there's currently no way for ggplot2 to figure out how much extra space you need because the physical space required depends on the size of the plot. Use, e.g., expand_limits(y = -0.1) to budget a little extra space for the text.

Resources