Say I have a dataframe:
df <- data.frame(x=1:10, y=4:13)
p <- ggplot(df,aes(x,y)) + geom_point()
Now I want to add many things to this graph, so I use a big paste function and print the output. Just as an example, say I wanted to add the word 'bananas' inside the x axis label.
x <- "bananas"
print(paste0("+ xlab('Price of", x[1], "')"), quote=F)
If I try:
p + print(paste0("+ xlab('Price of", x[1], "')"), quote=F)
then it obviously does not work. But is there a way of adding the output of this function to the ggplot object 'p' without cutting/pasting from the console?
i.e. so we automatically can execute:
p + xlab('Price ofbananas')
If you want to add Price of bananas as the x label, then:
p + xlab(paste0("Price of ", x[1]))
Remember you're adding the xlab, so that should be your outside function. Inside it, you add/create the label you want. No need to print.
Update:
I think what you want is eval(parse(text=xxx)). For example:
add <- paste0("xlab('Price of ", x[1], "')")
p + eval(parse(text=add))
Note that I removed the + from the text, because you need it next to p to connect with eval.
I'm not sure why you would do this, but it works.
Related
so I am struggling to get a plot working like I want. I have a facet_grid where the variables facetted is determined dynamically in a shiny app...
facet_grid(facetFormula, labeller = label_both)
where...
facetFormula <- as.formula(paste(input$filter2Var, "~", input$filter1Var))
this works fine, except that i'd rather a linebreak as the variable "name: value" separator instead of the colon. i've poked around with other arguments (multi_line, sep), using label_both() or label_wrap_gen() or labeller() instead of label_both no parenthesis... and am getting no where, probably stumbling over the already complex issue of dynamic variables to be facetted by. i've tried treating arguments to these various functions with !!sym() or as.formula(), but i really don't know what i am doing and probably messing up several things in trying to just add some simple text wrapping to my facet labels. any help is much appreciated!
UPDATE...
cases <- c("case1_has_long_name", "case2_long_too", "case3_long_as_well", "case4_also_long", "case5_long")
the_first_variable <- cases[round(runif(100,1,3))]
variable_number_two <- cases[round(runif(100,1,5))]
var1 <- "the_first_variable"
var2 <- "variable_number_two"
facetFormula <- as.formula(paste(var1, "~", var2))
myX <- runif(100,0,10)
myY <- runif(100,-5,5)
myData <- data.frame(myX, myY, the_first_variable, variable_number_two)
ggplot(myData, aes(x = myX, y = myY)) +
geom_point(alpha = .5) +
facet_grid(facetFormula,
labeller = label_both)
this generates a plot with my issue, where the facet labels are too big. i just want to learn how to make the labels wrap. was thinking as a simple start, instead of ":" as the separator between variable name and variable value, i could use "\n" as the seperator. the awkwardness of specifying my facet variables as variable themselves comes from them being dynamically defined in a shiny app.
Wrapping the facet labels could be achieved like so:
A labeller takes as its first argument a df with the variable names and labels. In your case a df with one column. The column contains the labels, the column name is the var name.
To wrap the labels I use a wrapper function around label_both where I first wrap the labels before passing the manipulated df to label_both. To make the wrapping work I replaced all underscores by spaces.
library(ggplot2)
my_label <- function(x) {
# Wrap var names
names(x)[[1]] <- stringr::str_wrap(gsub("_", " ", names(x)[[1]]), 10)
# Wrap value labels
x[[1]] <- stringr::str_wrap(gsub("_", " ", x[[1]]), 10)
# Call label both with sep "\n"
label_both(x, sep = "\n")
}
ggplot(myData, aes(x = myX, y = myY)) +
geom_point(alpha = .5) +
facet_grid(facetFormula, labeller = my_label)
A part of my code for ggplot is stored in a character vector. I would like to use this code as an additional geoms for my ggplot.
Example1:
DF=data.frame(x=seq(1:10), y=seq(1:20))
a='geom_line()'# This is a string that should be converted to RCode
So far I tried:
ggplot(DF, aes(x,y))+geom_point()+a
Error: Don't know how to add a to a plot
ggplot(DF, aes(x,y))+geom_point()+as.name(a)
Error: Don't know how to add as.name(a) to a plot
ggplot(DF, aes(x,y))+geom_point()+eval(parse(text=a))
Error in geom_line() + geom_line(y = 1) :
non-numeric argument to binary operator
ggplot(DF, aes(x,y))+geom_point()+deparse(substitute(a))
Error: Don't know how to add deparse(substitute(a)) to a plot
Example 2:
DF=data.frame(x=seq(1:10), y=seq(1:20))
a='geom_line()+geom_line(y=1)'
Probable you are wondering, why I would like to do that in a first place? In a for loop, I created expressions and stored them in a list as characters. Later, I pasted together all expressions into a single string expression. Now, I would like to add this string to a ggplot command. Any suggestions?
Edit: Example 1 was successfully solved. But Example 2 stays unsolved.
the parse function has text argument you need to pass a to. Try:
ggplot(DF, aes(x,y)) + geom_point() + eval(parse(text = a))
More info here:
http://adv-r.had.co.nz/Expressions.html#parsing-and-deparsing
In case of multiple statements, it is possible to deparse the original expression, add the new and then evaluate as a whole
original <- deparse(quote(ggplot(DF, aes(x,y)) + geom_point()))
new_call <- paste(original, '+', a)
eval(parse(text = new_call))
You can also use a function to define these code into a list. Please see: https://homepage.divms.uiowa.edu/~luke/classes/STAT4580/dry.html
Here I cited the related code:
Defining a theme_slopegraph function to do the theme adjustment allows
the adjustments to be easily reused:
theme_slopechart = function(toplabels = TRUE) {
thm <- theme(...)
list(thm, ...) # add multiple codes
#...
}
p <- basic_barley_slopes ## from twonum.R
p + theme_slopechart()
I have an R routine which creates a number of plots from a large set of data. Each plot is labeled with a titles describing the details of the set of points plotted. Unfortunately, I have not been able to use subscripts in the text if I am using paste to combine a complex label. The result is ugly. This is a simplified version of the code using data from R. The title shows the technique I am currently using, without subscripts. The attempt at an improved version is placed either on the x axis or on the plot.
library(ggplot2)
x1 = 1
x2 = 2
list <- c(1:4)
tle <- paste("VGM = ", as.character(list[1]),
"V, VDM = ", as.character(list[2]),
"V, VGF = ", as.character(list[3]),
"V, VDF = ", as.character(list[4]),
"V", sep="")
p <- ggplot(mtcars, aes(x=wt, y=mpg)) +
labs(title=tle) +
geom_point()
p
p + xlab(expression(V[DM])) #works fine
p + xlab(expression(paste(V[DM], "= 3"))) # works fine
# now we would like to use a variable to provide the number
p + xlab(expression(paste(V[DM], "=", x1))) # Just displays "x1", not value of x1
p + xlab(expression(paste(V[DM], "=",
as.character(x1)))) # NO
p + xlab(expression(paste(V[DM], "=",
as.character(as.number(x1))))) # NO
my.xlab1 <- bquote(V[DM] == .(x1))
p + xlab(my.xlab1) # We can see the success here
# A single variable at the end of the expression works
# What if you wanted to display two variables?
my.xlab2 <- bquote(V[GM] == .(x2))
my.xlab3 <- paste(my.xlab1, my.xlab2)
p + xlab(my.xlab3) # doesn't work
# Apparently the expressions cannot be pasted together. Try another approach.
# Place the two expressions separately on the plot. They no longer need to be
# pasted together. It would look better, anyway. Would it work?
p + annotate("text", x=4, y=30, label="Annotate_text", parse=TRUE)
# This is the idea
# p + annotate("text", x=4, y=30, label=bquote(V[DM] == .(x1)), parse=TRUE)
# This is a disaster
# RStudio stops the process with a question mark placed on the console. Appears that
# more input is being requested?
p + geom_text(x=4, y=30, label="Geom_text") # works
p + geom_text(x=4, y=30, label=my.xlab1) # does not accept variables.
I have included comments which describe the problems raised by each attempt. Ideally, the information should probably be placed as an annotation on the plot rather than as a title, but I cannot find a way to do this. Using a subscript turns a character into an expression, and it seems that there is a long list of functions which handle characters but not expressions.
If you want to "paste" two expressions together, you need to have some "operator" join them. There really isn't a paste method for expressions, but there are ways to put them together. First, obviously you could use one bquote() to put both variables together. Either
my.xlab3 <- bquote(V[DM] == .(x1)~ V[GM] == .(x2))
my.xlab3 <- bquote(list(V[DM] == .(x1), V[GM] == .(x2)))
would work. The first puts a space between them, the second puts a comma between them. But if you want to build them separately, you can combine them with another round of bquote. So the equivalent building method for the two above expressions is
my.xlab3 <- bquote(.(my.xlab1) ~ .(my.xlab2))
my.xlab3 <- bquote(list(.(my.xlab1), .(my.xlab2)))
All of those should work to set your xlab() value.
Now, if you also want to get annotate to work, you can "un-parse" your expression and then have R "re-parse" it for you and you should be all set. Observe
p + annotate("text", x=4, y=30, label=deparse(my.xlab3), parse=TRUE)
I am working with a data set where I have a few variables that I'm constantly graphing that come out of CSV files with a specific naming convention. So for example I'll have something like:
p <- ggplot(plot_df, aes(x=ms, y=bandwidth)) + geom_line()
And by default, the graph's x and y titles will come out 'ms' and 'bandwidth', respectively. What I'd like to be able to do is to define a function that has a list of these mappings like:
"bandwidth"->"Bandwidth (GB/s)"
"ms"->"Time (ms)"
so I can feed p to a function that will essentially execute:
p + xlab("Time (ms)") + ylab("Bandwidth GB/s")
automatically without me having to keep specifying what the labels should be. In order to do this, I need to somehow get access to the x and y titles as a string. I'm having a hard time figuring out how I can get this information out of p.
EDIT:
I guess typically the y axis comes out as 'value' because of melt, but for argument's sake, let's just say I'm doing the x axis for now.
They are stored in p$mapping (look at str(p))
Unless you want to do something really fancy with string manipulation, a look up table might be the best option for converting between variable names and correct labels
eg
d <- data.frame(x = 1:5, z = 12)
p <- ggplot(d, aes(x=x, y = z)) + geom_point()
labels.list <- list('x' = 'X (ms)', 'z' = 'Hello something')
p + xlab(labels.list[[as.character(p$mapping$x)]]) +
ylab(labels.list[[as.character(p$mapping$y)]])
You could write this into a function
label_nice <- function(ggplotobj, lookup) {
.stuff <- lapply(ggplotobj$mapping, as.character)
nice <- setNames(lookup[unlist(.stuff)], names(.stuff))
labs(nice)
}
# this will give you the same plot as above
p + label_nice(p, labels.list)
I'm having trouble passing a POSIXct stored in a variable as the xmin/xmax for geom_rect. I've tried to construct a free-standing example without trivializing what I'm trying to do...
The idea is to take a ggplot2 plot object, whose x is a POSIXt, and "zoom in" on a particular range in time. The zoom is in the top 80% and the entire series is in the bottom 20% with an indicator as to what part is zoom'd on top.
My problem is that I can't seem to get the xmin/xmax passed into geom_rect -- each thing I've tried (other than assembling the plot by hand instead of function) gives me a different error. I've tries using aes(), aes_string(), passing as parameters instead of aesthetics, passing just strings, etc.
The example below tells me:
Error in eval(expr, envir, enclos) : object 'lims' not found
I think my problem is that the variables I'm using to set the aesthetics aren't in scope when the aesthetics get processed, but I can't figure out how else to go about it. Help.
library(ggplot2)
subplot <- function(x, y) viewport(layout.pos.col=x, layout.pos.row=y)
vplayout <- function(x, y) {
grid.newpage()
pushViewport(viewport(layout=grid.layout(y,x)))
}
anm_zoom <- function(limits, p) {
lims <- as.POSIXct(limits)
limlab <- paste(lims, collapse=" to ")
top <- p + scale_x_datetime(limlab, limits=lims, expand=c(0,0))
bottom <- p;
bottom <- bottom + opts(title="")
bottom <- bottom + opts(legend.position="none")
bottom <- bottom + opts(axis.title.y=theme_blank())
bottom <- bottom + scale_x_datetime("", expand=c(0,0))
bottom <- bottom + geom_rect(aes(xmin=lims[1], xmax=lims[2]),
ymin=-Inf, ymax=Inf, fill="grey80", alpha=0.01)
## Render the plots
vplayout(1,5)
print(top, vp=subplot(1,c(1,2,3,4)))
print(bottom, vp=subplot(1,5))
}
pdate <- seq.POSIXt(from=as.POSIXct("2010-09-09 00:00"),
to=as.POSIXct("2010-09-10 23:59"), by="2 mins")
var1 <- rnorm(length(pdate))
var2 <- rnorm(length(pdate))
df1 <- data.frame(pdate, var1, var2)
dm <- melt(df1, id="pdate")
p <- ggplot(dm) + aes(x=pdate, y=value) + stat_summary(fun.y="sum", geom="line")
anm_zoom(c("2010-09-09 12:15", "2010-09-09 12:30"), p)
Hmmmm, I think you need a new aes function that is a bit like aes (in that it doesn't try to parse its arguments) and a bit like aes_string (in that it evaluates its arguments immediately in the local environment):
aes_now <- function(...) {
structure(list(...), class = "uneval")
}
Then
bottom <- bottom + geom_rect(aes_now(xmin=lims[1], xmax=lims[2]),
ymin=-Inf, ymax=Inf, fill="grey80", alpha=0.01)
gives you what you want.
Rewrite: from hadley's answer
Owing to the updates of newer version ggplot2 by hadley,
a more intuitive way dealing with non-std evaluation of ggplot() in function is using aes_q() as following:
xminName <- substitute(lims[1]); xmaxName <- substitute(lims[2])
bottom <- bottom +
geom_rect(aes_q(xmin=xminName, xmax=xmaxName),
ymin=-Inf, ymax=Inf, fill="grey80", alpha=0.01)
you just need to change the name of your function argument limits as i think it is creating a scoping conflict. i just changed it to limits1 and also the first line in your code to read lims = as.POSIXct(limits1) and it works perfectly. check it out!!