I'm saving objects to a list and I'd like to clear the plots pane and viewer pane whenever I load those objects. I basically want the back button to grey out after I've gone through the charts in that element of the list.
This is my code
gg <- ggplot(iris, aes(width, length)) + geom_point
l <- list()
l[["element"]] <- gg
I want it so that when I run l$element, it's like I first clicked the broom on the plots and viewer tab in rstudio.
You can do this as long as you store the ggplot inside a custom S3 class whose default print method calls dev.off then plots the enclosed ggplot:
gg_wipe <- function(x) structure(list(plot = x), class = "gg_wipe")
print.gg_wipe <- function(x) {dev.off(); print(x$plot)}
gg <- ggplot(iris, aes(Sepal.Width, Sepal.Length)) + geom_point()
l <- list()
l[["element"]] <- gg_wipe(gg)
l[["element"]]
Or rather than wrapping the plot as Allan did (which is a good idea) you can provide a helper function and then provide different ways to get at it. So this calls the helper function directly
l <- list(
apple=ggplot(data.frame(x=1:3, y=1:3)) + geom_point(aes(x,y)),
banana=ggplot(data.frame(x=1:3, y=3:1)) + geom_point(aes(x,y))
)
clear_and_print <- function(x, ele) {
graphics.off(); invisible(print(x[[deparse(substitute(ele))]]))
}
clear_and_print(l, apple)
You can define some new operators but they have to be syntactically valid. You can't have ^^^ bur you could have %^%
`%^%` <- clear_and_print
l %^% apple
l %^% banana
Or you can create a special class for your container
`$.plot_clear` <- function(x, ele) {
graphics.off(); x[[ele]]
}
class(l) <- "plot_clear"
l$apple
l$banana
Related
In developing an R package, in order to extend the generic plot function, what is the difference between plot.class_name and ggplot.class_name.
library(ggplot2)
add_values <- function(a, b){
out <- list()
class(out) <- "myclass"
c <- a + b
out$a <- a
out$b <- b
out$c <- c
return(out)
}
plot.myclass <- function(x, ...){
cat("Called by plot.")
plot(x$a,x$b)
}
ggplot.myclass <- function(x, ...){
cat("Called by ggplot.")
plot(x$b,x$a)
}
a <- seq(0,10,0.01)
b <- a^2
x <- add_values(a,b)
plot(x) # Prints out: Called by plot.
ggplot(x) # Prints out: Called by ggplot.
For a simple test, it seems both of them work fine. I prefer to use ggplot inside the package. There are a couple of options:
Developing two separate methods for plot.myclass and ggplot.myclass
Use ggplot inside plot.myclass then plot(object) will return ggplot results.
Use ggplot inside ggplot.myclass and do not support plot(object) command.
Does CRAN have any restriction on using any of these options?
I need to loop over files and then create for each file an object:
Here is an example :
filenames <- Sys.glob("/Users/Desktop/*.nwk")
for (i in filenames ) {
print(paste0("Processing the phylogeny: ",i))
p <- a code that generate a figure
}
And then I generate 5 figures that I call with this code :
multiplot(p1,p2,p3,p4,p5 ncol=2, labels=c('A', 'B','C','D','E'))
But I wondered how can I call assign the 1,2 etc values into the variable objects p?
I tried to create a nb=1 object and then assign as p+nb <- a code that generate a figure, but it does not work
There are dedicated packages for plotting/merging multiple plots. patchwork, cowplot, grid, egg, etc.
Use lapply to generate ggplot objects in a list, then use cowplot::plot_grid, something like:
cowplot::plot_grid(
plotlist = lapply(list.files(...), function(i){
#import file
d <- read.table(i)
#plot
ggplot(d, aes(...)) + geom_...
}),
ncol = 2)
You can do that but I'll suggest not to create 5 plot objects in global environment. Store the output of plots in a list.
list_plot <- vector('list', length(filenames))
for (i in seq_along(filenames)) {
cat("\nProcessing the phylogeny: ",filenames[i])
list_plot[[i]] <- a code that generate a figure using filenames[i] to read file
}
do.call(multiplot, c(list_plot, ncol=2, labels=c('A', 'B','C','D','E')))
I found a way by doing :
Phylo_name<-paste0("p",nb,sep="")
eval(call("<-", as.name(Phylo_name), ggtree(tr = phylo,
mapping = aes(color = group)) + geom_tiplab() + theme(legend.position="right")+
scale_color_manual(values=color_vector)))
My goal it to get a list p which contains two graphs p[[1]] and p[[2]].
p[[1]] and p[[2]] are supposed to be a plot with point(10,10) and point(20,20) for each. But after executing below, in the list p, only p[[2]] shows expected graph. P[[1]] graph does not appear.
How to correct to make p[[1]] in the list have point(10,10)?
(It seemd that the variable cordx and cordy are tightly coupled to p[[1]],
so whenever the cordx, cordy are changed, the alredy made p[[1]] is revised everytime.)
library(ggplot2)
xx<-list(10,20);yy<-list(10,20)
p<-list()
for (i in (1:2) ) {
cordy<-yy[[i]];cordx<-xx[[i]] #But,at 2nd loop(that is when i=2),after executing this line, my p[[1]] is affected unexpectedly, containning point (20,20))
p<-ggplot()+geom_point(aes(x=cordx,y=cordy))
p[[i]]<-p # at 1st loop(that is i=1), p[[1]] contains point (10,10) as expected.
}
print(p[[1]])
print(p[[2]])
May I suggest using mapply() to avoid looping?
Here is the code:
library(ggplot2)
xx <- list(10,20)
yy <- list(10,20)
p <- mapply(function(cordx, cordy) { ggplot() + geom_point(aes(x = cordx, y = cordy)) }, xx, yy, SIMPLIFY = FALSE)
print(p[[1]])
print(p[[2]])
What it does: mapply pass each element of xx and yy in the function that creates the plot. The outputs of the function are stored in the object p. SIMPLIFY = FALSE forces p to be a list.
Outputs:
I wonder if it's possible to craft a semi-automatic parsing of 'complicated' mathematical expressions into the axis of ggplot by maintaining some sort of lookup table?
So, for example, for data-mining, I regularly have to produce hundreds of scatterplots, which I want to discuss with colleagues. To do so, I want correct axis-legends, of course - this is rather cumbersome.
Here a simple example of what would like to read out from a database into the labs() by using a formula: expression(paste(delta^{18},"O (\u2030)")
So, what I was wondering is if there's a way to link those labs() to predefined lists or tables in a way like labs(y = list[3])?
This works just fine for simple names like: "Dissolved oxygen saturation / %", but when trying the same for the above, it generates:
paste(delta^{
18
}, "O (‰)")
(including the breaks - which is obviously not what I want)
Thanks,
Alex
You could tinker with the math_format() function from the scales package a bit to take in pre-substituted expressions:
library(patchwork)
library(ggplot2)
splitiris <- split(iris, iris$Species)
# Example expressions
exprs <- list(
substitute(10^.x),
substitute(log[.x]~"(%)"),
substitute(frac(.x, 2))
)
# Near-copy of scales::math_format
math_format2 <- function(expr = subsitute(10^.x), format = force) {
.x <- NULL
subs <- function(x) {
do.call("substitute", list(expr, list(.x = x)))
}
function(x) {
x <- format(x)
ret <- lapply(x, subs)
ret <- as.expression(ret)
ret[is.na(x)] <- NA
names(ret) <- names(x)
ret
}
}
# Generate plots
plots <- lapply(seq_along(splitiris), function(i) {
ggplot(splitiris[[i]], aes(Sepal.Width, Sepal.Length)) +
geom_point() +
scale_x_continuous(labels = math_format2(exprs[[i]]))
})
plots[[1]] + plots[[2]] + plots[[3]]
Created on 2020-05-27 by the reprex package (v0.3.0)
Since I will need to make a lot of different plots in R I'm trying to put some more logic in preparing the data (add column names corresponding to the aesthetics) and less logic in the plot itself.
Consider the following default iris plot:
library(ggplot2)
library(data.table)
scatter <- ggplot(data=iris, aes(x = Sepal.Length, y = Sepal.Width))
scatter + geom_point(aes(color=Species, shape=Species))
Now I make a modified iris data with column names matching to the desired aesthetics:
iris2 <- as.data.table(iris)
iris2 <- iris2[,.(x=Sepal.Length, y=Sepal.Width, color=Species,
shape=Species)]
That I want to plot in a function in such a way that it basically builds the following command only slightly more dynamic, so you use all the aesthetics supplied in the data.
ggplot(data, aes(x=x, y=y)) + geom_point(aes(color=color, shape=shape))
It has been a long time since I read anything about nonstandard evaluation, expressions and quotation and I noticed that there are quite some developments with rlang and quosures (cheatsheet). [This] question was kind of helpful, but it did not resolve the fact that I want to infer the aesthetics from the data.
In the end I have tried a lot of stuff, and looked inside aes. In there I see:
exprs <- rlang::enquos(x = x, y = y, ...)
and I think this is the reason that all attempts that I made like:
ggplot(iris2, aes(x=x, y=y)) +
geom_point(aes(rlang::quo(expr(color=color))))
did not work out since aes is trying to 'enquos' my quosure(s).
QUESTION Is there any way to supply arguments to aes in a dynamic way based on the contents of the data (so you do not know in advance which aesthetics you will need?
If my question is not clear enough, in the end I made something that works, only I have a feeling that this totally not necessary because I don't know/understand the right way to do it. So the stuff below works and is what I have in mind, but what I e.g. don't like is that I had to modify aes:
The block below is stand alone and can be executed without the code chunks above.
library(data.table)
library(ggplot2)
library(rlang)
iris2 <- as.data.table(iris)
iris2 <- iris2[,.(x=Sepal.Length, y=Sepal.Width, color=Species, shape=Species)]
myaes <- function (x, y, myquo=NULL, ...) {
exprs <- rlang::enquos(x = x, y = y, ...)
exprs <- c(exprs, myquo)
is_missing <- vapply(exprs, rlang::quo_is_missing, logical(1))
aes <- ggplot2:::new_aes(exprs[!is_missing], env = parent.frame())
ggplot2:::rename_aes(aes)
}
generalPlot <- function(data, f=geom_point,
knownaes=c('color'=expr(color), 'shape'=expr(shape))){
myquo <- list()
for(i in names(knownaes)){
if(i %in% names(data)){
l <- list(rlang::quo(!!knownaes[[i]]))
names(l) <- i
myquo <- c(myquo, l)
}
}
ggplot(data, aes(x=x, y=y)) +
f(myaes(myquo=myquo))
}
generalPlot(iris2[,.(x, y, color)])
generalPlot(iris2[,.(x, y, color, shape)])
You can use this custom function that parses input data colnames and generates an aes text string that is passed to eval().
generateAES <- function(foo) {
eval(parse(text = paste0("aes(",
paste(
lapply(foo, function(i) paste(i, "=", i)),
collapse = ","),
")"
)))
}
You can use it with:
ggplot(iris2, generateAES(colnames(iris2))) +
geom_point()
Or with pipes:
library(magrittr)
iris2 %>%
ggplot(generateAES(colnames(.))) +
geom_point()
generateAES output is aes like:
Aesthetic mapping:
* `x` -> `x`
* `y` -> `y`
* `colour` -> `color`
* `shape` -> `shape`
That is generated from text string "aes(x = x,y = y,color = color,shape = shape)"
So if your data as a "color" or "shape" column, you just want to map that to the color or shape aesthetic? I think a simpler way to do that would be
generalPlot <- function(data, f=geom_point, knownaes=c('color', 'shape')) {
match_aes <- intersect(names(data), knownaes)
my_aes_list <- purrr::set_names(purrr::map(match_aes, rlang::sym), match_aes)
my_aes <- rlang::eval_tidy(quo(aes(!!!my_aes_list)))
ggplot(data, aes(x=x, y=y)) +
f(mapping=my_aes)
}
Then you can do
generalPlot(iris2[,.(x, y)])
generalPlot(iris2[,.(x, y, color)])
generalPlot(iris2[,.(x, y, color, shape)])
and it doesn't require the additional myaes function.
I'm kind of surprised I had to use eval_tidy but for some reason you can't seem to use !!! with aes().
x <- list(color=sym("color"))
ggplot(iris2, aes(x,y)) + geom_point(aes(!!!x))
# Error: Can't use `!!!` at top level
(Tested with ggplot2_3.1.0)