questions on understanding the function of extracting grobs structure using sapply - r

I am trying to understand the following segment, especially the one, sapply(tmp$grobs, function(x) x$name
require(gridExtra)
require(ggplot2)
my_hist <- ggplot(diamonds, aes(clarity, fill=cut)) + geom_bar()
tmp <- ggplot_gtable(ggplot_build(my_hist))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
I think this function is used to extract the name of tmp$grobs, which is shown as follows
Then I tried. First question, how to understand the structure, like [[1]]; further, why (tmp$grobs[1])$name just does not work.

this isn't an answer to your question, but a coding suggestion
require(ggplot2)
my_hist <- ggplot(diamonds, aes(clarity, fill=cut)) + geom_bar()
tmp <- ggplotGrob(my_hist)
legend <- tmp$grobs[[grep("guide-box", tmp$layout$name)]]

Related

ggplot2: manual scale values are not saved when creating plots in a for loop

I would like to create plots of line segments by looping over different data-sets, with the values of the scale for linetype set manually. However, using the for loop, only the scale of the last plot in the loop is used for all the other plots, despite defining a separate scale for each plot.
The behavior appears in the following minimal example:
library(ggplot2)
DF <- list(
DF1 = data.frame(x=1:10, y=1:10, type=as.factor(c(rep(0,5),rep(1,5)))),
DF2 = data.frame(x=1:10, y=1:10, type=as.factor(sort(rep(1:5,2))))
)
linetype <- list(
linetype1 <- 4:5,
linetype2 <- 1:5
)
plotlist <- list()
for(i in 1:2){
plotlist[[i]] <- ggplot(DF[[i]], aes(x, y, linetype=type)) +
geom_line() +
scale_linetype_manual(values=linetype[[i]])
}
Then, when printing the plot
plotlist[[1]]
it shows the lines of the first plot with the linetype values of the second plot.
The plot I am after is:
which can be created using the same code outside the for loop:
i <- 1
plotlist[[i]] <- ggplot(DF[[i]], aes(x, y, linetype=type)) +
geom_line() +
scale_linetype_manual(values=linetype[[i]])
Where does this behavior come from? Is there any way around it? I'm at a loss here, thanks for your help!
Edit:
I can get the expected list using lapply:
plotlist <- lapply(1:2, function(i){
ggplot(DF[[i]], aes(x, y, linetype=type)) +
geom_line() +
scale_linetype_manual(values=linetype[[i]]
})
But I still do not understand why the expected behavior doesn't happen in the for loop. My guess is that the scale value is only evaluated when the graph is printed; and at that time, only the last scale is available in the environment. This is hardly a good explanation at all and I would like to know the details of this behavior.

Ggplot with more than two legends

I have a data.frame which I'd like to scatter plot using ggplot.
The data have 3 factors whose levels I'd like to show in the legend, although the color of the points will only be according to one of these factors (df$group below).
Here's what I have so far:
set.seed(1)
df <- data.frame(x=rnorm(100),y=rnorm(100),
group=LETTERS[sample(5,100,replace=T)],
type=letters[sample(3,100,replace=T)],
background=sample(4,100,replace=T),stringsAsFactors=F)
df$group <- factor(df$group,LETTERS[1:5])
df$type <- factor(df$type,;etters[1:3])
df$background <- factor(df$background,c(1:4))
I manually specify colors:
require(RColorBrewer)
require(scales)
all.colors <- hcl(h=seq(0,(12-1)/(12),length=12)*360,c=100,l=65,fixup=TRUE)
group.colors <- all.colors[1:5]
type.colors <- all.colors[6:8]
background.colors <- all.colors[9:12]
This is what I have for showing the 3 factors in the legend (df$group and df$type):
require(ggplot2)
ggplot(df,aes(x=x,y=y,colour=group,fill=type,alpha=background))+geom_point(cex=2,shape=1,stroke=1)+
theme_bw()+theme(strip.background=element_blank())+scale_color_manual(drop=FALSE,values=group.colors,name="group")+
guides(fill=guide_legend(override.aes=list(colour=type.colors,pch=0)))
So my question is how to get background.colors appear in the legend under "background" rather than the gray scale colors chosen by default that currently appear there.
ggplot(df,aes(x=x, y=y, colour=group, fill=type, alpha=background))+
geom_point(cex=2, shape=1, stroke=1) +
theme_bw() +
theme(strip.background=element_blank()) +
scale_color_manual(drop=FALSE, values=group.colors, name="group") +
guides(fill=guide_legend(override.aes=list(colour=type.colors,pch=0)),
alpha=guide_legend(override.aes=list(colour=background.colors,pch=0)))

marrangeGrob but can't add legends

I used marrangeGrob() instead of facet_wrap() to produce my plots from a list of plots. However, I can't seem to add a legend.
I already extracted my legend using
g_legend<-function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)}
However, I am unable to add it it my plot.
Does anyone know a way?
Here's an example using the built-in diamonds data frame:
library(ggplot2)
library(gridExtra)
library(dplyr)
g_legend<-function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)}
First we'll create two plots to lay out together:
df <- count(diamonds, cut)
p1 = ggplot(df, aes(x=cut, y=n, label=format(n, big.mark=","), fill=cut)) +
geom_bar(stat="identity") +
geom_text(aes(y=0.5*n), colour="white") +
coord_flip() +
theme(legend.position="bottom")
p2 = ggplot(diamonds %>% sample_n(1000), aes(x=carat, y=price, colour=cut)) +
geom_point()
Now save the legend from p1 as a separate grob:
leg = g_legend(p1)
Lay out the two plots side-by-side using arrangeGrob and then use marrangeGrob to lay out the two-plot layout and the legend beneath it. Note that we also remove the legends from the original plots.
marrangeGrob(grobs=list(
arrangeGrob(grobs=lapply(list(p1,p2), function(p) {
p + guides(colour=FALSE, fill=FALSE)
}), ncol=2),
leg), ncol=1, nrow=2, heights=c(20,1))

How to set width of y-axis tick labels in ggplot2 in R?

I would like to left align the plot panels in a vertical array of ggplot2 graphs in R. The maximum width of the y-axis tick labels varies from graph to graph, breaking this alignment, as shown in the sample code below.
I've tried various plot, panel, and axis.text margin options without success, and have not been able to find an option for controlling the width of the y-axis tick labels.
Guidance appreciated.
#install.packages(c("ggplot2", "gridExtra", "reshape2"), dependencies = TRUE)
require(ggplot2)
require(gridExtra)
require(reshape2)
v <- 1:5
data1 <- data.frame(x=v, y=v)
data2 <- data.frame(x=v, y=1000*v)
plot1 <- ggplot(data=melt(data1, id='x'), mapping=aes_string(x='x', y='value')) + geom_line()
plot2 <- ggplot(data=melt(data2, id='x'), mapping=aes_string(x='x', y='value')) + geom_line()
grid.arrange(plot1, plot2, ncol=1)
You can use function plot_grid() from library cowplot to align plots
# install.packages(c("ggplot2", "cowplot", "reshape2"), dependencies = TRUE)
library(cowplot)
plot_grid(plot1,plot2,ncol=1,align="v")
would this something like that work for you:
data1$Data <- "data1"
data2$Data <- "data2"
data3 <- rbind(data1, data2)
ggplot(data=data3, aes(x=x, y=y)) + geom_line() + facet_grid(Data~., scales = "free_y")
like this? (code below)
# install.packages(c("ggplot2", "gridExtra", "reshape2"), dependencies = TRUE)
require(ggplot2)
require(gridExtra)
require(reshape2)
v <- 1:5
data1 <- data.frame(x=v, y=v)
data2 <- data.frame(x=v, y=1000*v)
plot1 <- ggplot(data=melt(data1, id='x'), mapping=aes_string(x='x', y='value')) + geom_line() + scale_y_continuous(breaks=NULL)
plot2 <- ggplot(data=melt(data2, id='x'), mapping=aes_string(x='x', y='value')) + geom_line() + scale_y_continuous(breaks=c(1000,2000))
grid.arrange(plot1, plot2, ncol=1)

How to control the range of values in horizontal Stacked Bar Plot using R

Following is the code which gives a stacked bar chart with Timeline values from 0-300. But I want it to be from 1-24.
R code:
library(ggplot2)
dataFrame <- data.frame(sr=c(1:72),
hours=c(1:24),
mode=factor(c(""),levels = c("SecureMessaging","WebLogs","IVR")),
status=factor(c("Inactive"),levels = c("Active","Inactive")))
dataFrame$mode[1:24] <- "SecureMessaging"
dataFrame$mode[25:48] <- "WebLogs"
dataFrame$mode[49:72] <- "IVR"
dataFrame$status[2] <- "Active"
dataFrame$status[7] <- "Active"
dataFrame$status[24] <- "Active"
dataFrame$status[2+24] <- "Active"
dataFrame$status[12+24] <- "Active"
dataFrame$status[15+24] <- "Active"
dataFrame$status[3+48] <- "Active"
dataFrame$status[5+48] <- "Active"
dataFrame <- na.omit(dataFrame)
plot <- ggplot(data=dataFrame, aes(x=mode, y=hours, fill=status)) + geom_bar(stat="identity")
plot <- plot+coord_flip()
plot <- plot+ggtitle("Data Analytics")
plot <- plot+xlab("Mode")
plot <- plot+ylab("Time Line")
print(plot)
Technically not a direct answer as I'm suggesting an alternate implementation using geom_tile:
gg <- ggplot(data=dataFrame, aes(y=mode, x=factor(hours)))
gg <- gg + geom_tile(aes(fill=status))
gg <- gg + coord_equal()
gg <- gg + labs(x="Mode", y="Time Line", title="Data Analytics")
gg <- gg + theme_bw()
gg <- gg + theme(panel.grid=element_blank())
gg <- gg + theme(panel.border=element_blank())
gg <- gg + theme(legend.position="bottom")
gg
Bar chart with proper x-axis:
ggplot(dataFrame, aes(x=hours,y=status, fill=status))+ geom_bar(stat='identity')+ facet_grid(mode~.)
If you'd like to cut the result at 24 (in the range 0,300) then add before print(plot) the following line. The result is however not perfect yet, because the "inactive" bars stop before 24.
plot <- plot+ylim(0,24)
However, I am not sure if you wanted this. Probably you are not happy with the scaling from 0-300 that r applies and want to have it 0-24. In that case, try the following. "1" should replace "hours" in your plot function as indicated here:
plot <- ggplot(data=dataFrame, aes(x=mode, 1, fill=status)) + geom_bar(stat="identity")

Resources