How to dynamically set theme objects in ggplot2? - r

I'm trying to define this function to easily set a custom theme to a ggplot, but I'm having trouble using the functions like element_blank() dynamically.. For example, specifying whether or not to have vertical gridlines as a TRUE/FALSE argument:
qplot(data=df, x=quarter, y=value, geom="line") +
style(grid.vertical = F)
style = function (grid.vertical = T) {
(theme_foundation() +
theme(
panel.grid.major.x = ifelse(grid.vertical == T, element_line(), element_blank())
))}
produces..
Error in (function (el, elname) :
Element panel.grid.major.x must be a element_line object.
Is there a way to make this work?

It seems that the error is produced by an unpleasant peculiarity of ifelse. I tried the workaround from the accepted answer there, but that did not help. So I guess the simplest way would be to switch to the regular if, though the code doesn't seem to be particularly neat.
style.if = function (grid.vertical = T) {
t <- theme()
if (grid.vertical)
t <- t + theme(panel.grid.major.x = element_line(colour = 'red'))
else t <- t + theme(panel.grid.major.x = element_blank())
t
}
ggplot(mtcars, aes(x=wt, y=mpg)) + geom_line() + style.if()
ggplot(mtcars, aes(x=wt, y=mpg)) + geom_line() + style.if(F)

This has to do with how the ggplot code is checking for types:
...
else if (!inherits(el, eldef$class) && !inherits(el, "element_blank")) {
stop("Element ", elname, " must be a ", eldef$class,
" object.")
}
Since we're using the ifelse function, I think this dynamic type checking voodoo will look at ifelse as being the "element" here, so the conditions are never met since it doesn't inherit from element_blank. To solve this, just use regular if/else expression instead of ifelse:
style = function (grid.vertical = T) {
(theme_foundation() +
theme(
panel.grid.major.x = if(grid.vertical == T) element_line() else element_blank()
))}
I had the exact same problem and this solution worked for me.

Related

How to specify arguments in ggplot function?

I faced a problem that R does't see the argument I specify in ggplot function. Here is the code I use:
s_plot <- function(data, aaa, bbb,){
ggplot(data, aes(x = aaa, y = bbb))+geom_point(size=2, shape=8, col="red")
}
As a result I got an error:
object aaa not found
What's the problem? How to resolve it?
Thanks a lot.
UPD:
Sorry, but I provide you with the simplest example and it doesn't translate the whole problem.
Here is the full code I use:
s_plot <- function(data, n_after, perc_cap, n_xlab, n_ylab, x_min){
ggplot(data, aes(x={{n_after}}, y={{perc_cap}})) + geom_point(size=2, shape=8, col="red")+
xlab(n_xlab)+ ylab(n_ylab)+xlim(x_min, 1.1*max(data$n_after))+ ylim(0, 1.1*max(data$perc_cap))+
geom_text(aes(x=n_after, y=perc_cap, label = NAME), hjust=0, vjust=-1.5)+
geom_vline(xintercept=8, col = "darkgreen",lty=2, size=1)+
geom_text(aes(x=8, label=label, y=20), colour="steelblue", angle=90, hjust=-1)+
theme(axis.title.y = element_text(size=15),
axis.title.x = element_text(size=15))
As you may see n_after and perc_cap are mentioned in several places. And this probably is the source of problem. How to resolve it in this particular case?
You can use the {{ }} operator. As in the example:
f <- function (data, x) {ggplot(data= data, aes(x={{x}})) + geom_bar()}
f(mtcars, gear)

Annotate exponential function ggplot2

I would just like to simply add an annotation to my ggplot with the exponential function on it like this graph:
excel graph
Here is the data:Data
Here is the code I used thus far:
dfplot<-ggplot(data, aes(dilution.factor,Concentation)) +
geom_point(size=3)+ geom_smooth(method="auto",se=FALSE, colour="black")+
scale_y_continuous(breaks=seq(0,14,by=2))
dfplot2<-dfplot+labs(x=('Dilution Factor'), y=expression('Concentration' ~
(ng/mu*L)))+
theme_bw() + theme(panel.border = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.text = element_text(colour="black"),
axis.line = element_line(colour = "black"))
dfplot3<- dfplot2+annotate("text", x=3, y=10, label = "R^2 == 1",parse=TRUE)
dfplot3
dfplot4<-dfplot3+annotate("text", x=3, y=11, label =
as.character(expression("y=13.048e^-{0.697x}" ,parse=TRUE)))
dfplot4
I can get all the way up to putting the r^2 value (dfplot3)dfplot3
For some reason I cannot get it to add the exponential equation in. I keep getting this error:
Error: Aesthetics must be either length 1 or the same as the data (1): label
What am i doing wrong?
Not quite sure about the as.character(expression()) syntax you are using, but when you are parsing annotation text, ggplot2 doesn't understand the 'human' style notation shortcut of placing a number next to a letter 13.084e, you need to tell it explicitly this is multiplication. You also need == instead of =.
annotate("text", x=3, y=11, label = "y == 13.048*e^-{0.697*x}", parse =TRUE)
Edit: I see that you have included parse = TRUE inside the expression call, I think this is a mistake. To do it with expression you would write the following, but this is not in fact necessary:
annotate("text", x=3, y=11, label = as.character(expression("y == 13.048*e^-{0.697*x}")), parse = T)

How does one pass additional parameters to a ggplot generated inside a function?

I write many packages where the generic plot is a ggplot2. A ggplot call, unlike other R function calls, is layered so one could end up with several options (separated by + signs) to get a plot looking just right. However, I don't want someone to suffer through my pre-defined options and would like them to be able to customize it without re-writing my function from scratch. How do I accomplish this?
With a traditional function, I could use the three dot operator to pass optional arguments. This seems harder with a ggplot.
Reproducible example
f <- function(df) {
custom_plot <- ggplot(df, aes(mpg, disp, color = gear)) +
geom_point(size = 3) +
theme(panel.background = element_blank(), panel.grid.major = element_blank(),
panel.grid.minor = element_blank(), panel.border = element_blank(),
axis.line = element_line(colour = "black"))
return(custom_plot)
}
To generate a plot
f(mtcars)
produces this:
How do I generalize this function such that someone could pass additional or different options to this plot (especially in cases where it is the generic plot)?
If my function were defined as:
f <- function(df, ...)
How would I pass those in to my ggplot call?
The plots returned by your functions should be modifiable for anyone who knows ggplot- unless you can think of specific cases that can't be fixed by using +, a better solution might be to do as little theming and customization as possible, and let people add to the plots themselves.
All of these customizations work fine, for example:
mtplot <- f(mtcars)
mtplot + theme_bw()
mtplot + scale_colour_gradientn(colours=c("red", "purple"))
mtplot + labs(title="Add a title!")
mtplot + geom_point(size=5)

What are good ways to preset plotting options for ggplot in R

Im having a bunch of lengthy repetitive ggplot2 instructions throughout a file. So far i've been using variables control preset values keeping graphs uniform, having the configuration in one spot (e.g. to change the color of all graphs).
What i'm really looking for is a good way to preset those instructions so i don't have to write the statements all over. As an example, i define somewhere top in the file:
OPTAX=theme_text(angle=30, hjust=1, vjust=1, size=8)
Where the plot statements is, i use
P<-ggplot(
data=MYDATA,
aes(x=Topics, y=value, fill=variable)) +
....
opts(axis.text.x=OPTAX)
)
I would like to avoid writing the axis.text.x=MYVAR part and write something like
... + opts(MYOPTS) + ...
or avoid the opts statement at all and write
... + ALLMYOPTS + ...
so all my options are predefined in one statement.
As a second thing it would be good to override the statement. Something like
opts(MYOPTS, axis.text.x=theme_text(angle=60)
would be great, so i can keep the preset but still use custom options.
You can wrap up all sorts of ggplot configuration - not just opts - and apply it to multiple graphs, by using a list:
myPrettyOptions = list(
opts(axis.text.x=OPTAX),
facet_wrap(~Topic),
limits(x=c(1,2)),
scale_x_discrete(expand=c(0,0,5))
)
Then use this in multiple locations: (think of the space you will save!):
ggplot(blah) + myPrettyOptions
# in the second plot we can override the options:
ggplot(foo) + myPrettyOptions + opts(axis.text.x=theme_text(angle=60)
You can take this even further and prepare everything except the data:
graphtemplate = ggplot(blah) + myPrettyOptions
graphtemplate %+% data1
graphtemplate %+% data2
Note the use of the %+% operator.
An even more terse approach involves using the theme_update, theme_get, and theme_set methods.
old.theme <- theme_update(axis.text.x = theme_text(angle=30, hjust=1, vjust=1, size=8))
qplot(1,1)
If you want to revert to the old theme use, simply,
theme_set(old.theme)
qplot(1,1)
This will work:
ALLMYOPTS <- opts(axis.text.x = theme_text(angle=30, hjust=1, vjust=1, size=8))
P + ALLMYOPTS
Put every parameter in an object first, ex:
a<- theme(axis.title.y = element_text(angle = 0))
b<- theme(panel.background = element_rect(fill = "#e4e4e4")) + theme(axis.title.y = element_text(angle = 0, size=rel(1.5)))+ theme(axis.title.x=element_text(size=rel(1.5))) + theme(legend.text=element_text(size=rel(1.5)))+ theme(legend.title=element_text(size=rel(1.5), face = "italic")) + theme(plot.title = element_text(size = rel(1.5))) + theme(axis.text = element_text(size=rel(1.0)))
c<- theme(axis.line.x=element_line(size=0.5, colour="black"))
d<- theme(axis.line.y=element_line(size=0.5, colour="black"))
e<- theme(legend.box.just = c("top"), legend.position="bottom",legend.justification=c(1,1), legend.direction="vertical", legend.box="horizontal") + theme(panel.grid.minor.y = element_blank())
f<- ggplot(blah) + a +b +c +d +e
f

How can I remove the legend title in ggplot2?

I have a question concerning the legend in ggplot2.
Say I have a hypothetical dataset about mean carrot length for two different colours at two farms:
carrots<-NULL
carrots$Farm<-rep(c("X","Y"),2)
carrots$Type<-rep(c("Orange","Purple"),each=2)
carrots$MeanLength<-c(10,6,4,2)
carrots<-data.frame(carrots)
I make a simple bar plot:
require(ggplot2)
p<-ggplot(carrots,aes(y=MeanLength,x=Farm,fill=Type)) +
geom_bar(position="dodge") +
opts(legend.position="top")
p
My question is: is there a way to remove the title ('Type') from the legend?
Thanks!
I found that the best option is to use + theme(legend.title = element_blank()) as user "gkcn" noted.
For me (on 03/26/15) using the previously suggested labs(fill="") and scale_fill_discrete("") remove one title, only to add in another legend, which is not useful.
You can modify the legend title by passing it as the first parameter to a scale. For example:
ggplot(carrots, aes(y=MeanLength, x=Farm, fill=Type)) +
geom_bar(position="dodge") +
theme(legend.position="top", legend.direction="horizontal") +
scale_fill_discrete("")
There is also a shortcut for this, i.e. labs(fill="")
Since your legend is at the top of the chart, you may also wish to modify the legend orientation. You can do this using opts(legend.direction="horizontal").
You can use labs:
p + labs(fill="")
The only way worked for me was using legend.title = theme_blank() and I think it is the most convenient variant in comparison to labs(fill="") and scale_fill_discrete(""), which also could be useful in some cases.
ggplot(carrots,aes(y=MeanLength,x=Farm,fill=Type)) +
geom_bar(position="dodge") +
opts(
legend.position="top",
legend.direction="horizontal",
legend.title = theme_blank()
)
P.S. There are more useful options in documentation.
You've got two good options already, so here's another using scale_fill_manual(). Note this also lets you specify the colors of the bars easily:
ggplot(carrots,aes(y=MeanLength,x=Farm,fill=Type)) +
geom_bar(position="dodge") +
opts(legend.position="top") +
scale_fill_manual(name = "", values = c("Orange" = "orange", "Purple" = "purple"))
If you are using the up-to-date (As of January 2015) version of ggplot2 (version 1.0), then the following should work:
ggplot(carrots, aes(y = MeanLength, x = Farm, fill = Type)) +
geom_bar(stat = "identity", position = "dodge") +
theme(legend.position="top") +
scale_fill_manual(name = "", values = c("Orange" = "orange", "Purple" = "purple"))
#pascal 's solution in a comment to set the name argument of a scale function, such as scale_fill_discrete, to NULL, is the best option for me. It allows removing the title together with the blank space that would remain if you used "", while at the same time allowing the user to selectively remove titles, which is not possible with the theme(legend.title = element_blank()) approach.
Since it is buried in a comment, I am posting it as an answer to potentially increase its visibility, with kudos to #pascal.
TL;DR (for the copy-pasters):
scale_fill_discrete(name = NULL)

Resources