Edit 2 stat_hex_bin geoms separately ggplot2 - r

I start by giving you my example code:
x <- runif(1000,0, 5)
y <- c(runif(500, 0, 2), runif(500, 3,5))
A <- data.frame("X"=x,"Y"=y[1:500])
B <- data.frame("X"=x,"Y"=y[501:1000])
ggplot() +
stat_bin_hex(data=A, aes(x=X, y=Y), bins=10) +
stat_bin_hex(data=B, aes(x=X, y=Y), bins=10) +
scale_fill_continuous(low="red4", high="#ED1A3A")
It produces the following plot:
Now I want the lower hexagons to follow a different scale. Namely ranging from a dark green to a lighter green. How can I achieve that?
Update:
As you can see from the answers so far, I am asking myself whether there is a solution without using alpha scales. Also, using two plots with no margin or something similar is not an option for my specific application. Though they both are legitimate answers :)

Rather than trying to get two different fill scales in one plot you could alter the colours of the lower values, after the plot has been built. The basic idea is have two plots with the differing fill scales and then copy accross certain details from one plot to the other.
# Base plot
p <- ggplot() +
stat_bin_hex(data=A, aes(x=X, y=Y), bins=10) +
stat_bin_hex(data=B, aes(x=X, y=Y), bins=10)
# Produce two plots with different fill colours
p1 <- p + scale_fill_continuous(low="red4", high="#ED1A3A")
p2 <- p + scale_fill_continuous(low="darkgreen", high="lightgreen")
# Get fill colours for second plot and overwrite the corresponding
# values in the first plot
g1 <- ggplot_build(p1)
g2 <- ggplot_build(p2)
g1$data[[1]][,"fill"] <- g2$data[[1]][,"fill"]
# You can draw this now but there is only one legend
grid.draw(ggplot_gtable(g1))
To have two legends you can join the legends from the two plots together
# Bind the legends from the two plots together
g1 <- ggplot_gtable(g1)
g2 <- ggplot_gtable(g2)
g1$grobs[[grep("guide", g1$layout$name )]] <-
rbind(g1$grobs[[grep("guide", g1$layout$name )]],
g2$grobs[[grep("guide", g2$layout$name )]] )
grid.newpage()
grid.draw(g1)
Giving (from set.seed(10) prior to data generation)

This should provide more or less what you want
ggplot() +
stat_bin_hex(data=A, aes(x=X, y=Y, alpha=..count..), bins=10,fill="green") +
stat_bin_hex(data=B, aes(x=X, y=Y, alpha=..count..), bins=10,fill="red")
To avoid that the grey is disturbing due to the alpha one could underlay the plot with another white plot at the same location and darken the colours a bit, as suggested by the TO in the comments
#just the red to show the impact due to scale_alpha
ggplot() +scale_alpha_continuous(range=c(0.5,1))+ stat_bin_hex(data=A, aes(x=X, y=Y), bins=10,fill="white",show.legend = TRUE) +
+ stat_bin_hex(data=A, aes(x=X, y=Y, alpha=..count..), bins=10,fill="red",show.legend = TRUE) +
+ stat_bin_hex(data=B, aes(x=X, y=Y, alpha=..count..), bins=10,fill="green", show.legend=TRUE)+guides(fill=FALSE, alpha=FALSE)

An alternative, if you want more options to play with the colours, just create two plots and remove all the space between the two plots when combined with grid.arrange().
p1 <- ggplot() + stat_bin_hex(data=B, aes(x=X, y=Y), bins=10) +
scale_fill_continuous(low="red4", high="#ED1A3A") + xlab("") + theme(axis.text.x=element_blank(), axis.ticks.x=element_blank(), plot.margin=unit(c(1,1,-0.5,1), "cm")) + scale_y_continuous(limits = c(2.5, 5.5))
p2 <- ggplot() + stat_bin_hex(data=A, aes(x=X, y=Y), bins=10) + scale_fill_continuous(low="darkgreen", high="green") + theme(plot.margin=unit(c(-0.5,1,1,1), "cm")) + scale_y_continuous(limits = c(-0.5, 2.5))
grid.arrange(p1,p2)

Related

ggplotly with geom_ribbon grouping

I'm having some problems in converting a ggplot in to a plotly object, and retaining the same legend attributes. What I want:
For grouped series, a single line for fit, and faded region for ribbon of same colour, with transparency
No lines at the edge of the ribbon
Grouped legends for the lines, points and ribbons
Here is the code showing the 2 approaches I tried based on this answer:
ggplot: remove lines at ribbon edges
Both have an undesirable effect as you can see when running. Any suggestions would be great :)
library(plotly)
library(ggplot2)
# fake data
set.seed(1)
dt <- data.frame(x=rep(1:7,2), group=rep(letters[1:2], each=7), value=runif(14))
dt$lwr <- dt$value*.9
dt$upr <- dt$value*1.1
# build plot in ggplot, don't want lines at the edge
pl <- ggplot(data=dt, aes(y=value, x=x, group=group, colour=group,
fill=group)) +
geom_point() +
geom_line() +
geom_ribbon(aes(ymin=lwr, ymax=upr), alpha=.3, linetype=0) +
theme_minimal()
# looks ok, no lines at the edges
pl
# lines at edges, single group
ggplotly(pl)
# alternative: try reverting colour to NA
pl2 <- ggplot(data=dt, aes(y=value, x=x, group=group, colour=group,
fill=group)) +
geom_point() +
geom_line() +
geom_ribbon(aes(ymin=lwr, ymax=upr), alpha=.3, colour=NA) +
theme_minimal()
# looks ok
pl2
# no lines, but now not grouped, and some weird naming
ggplotly(pl2)
Thanks, Jonny
EDIT:
Addition to the accepted answer, in functional form
# dd: ggplotly object
library(stringi)
library(rvest)
remove_ggplotly_ribbon_lines <- function(dd){
find <- rvest::pluck(dd$x$data, "fillcolor")
w <- which(!sapply(find, is.null))
for(i in w){
dd$x$data[[i]]$line$color <-
stringi::stri_replace_all_regex(dd$x$data[[i]]$line$color, ",[\\d.]*\\)$", ",0.0)")
}
return(dd)
}
remove_ggplotly_ribbon_lines(ggplotly(pl))
Hi this is more a comment than an answer but I do not have right to post comments.
If you investigate the ggplotly object you will see that it is actually just a list. Changing the right elements of the list helps in controlling plot options.
The solution below just changes the alpha of the lines at ribbon edges. Hope this helps
library(plotly)
set.seed(1)
dt <- data.frame(x=rep(1:7,2), group=rep(letters[1:2], each=7), value=runif(14))
dt$lwr <- dt$value*.9
dt$upr <- dt$value*1.1
# build plot in ggplot, don't want lines at the edge
pl <- ggplot(data=dt, aes(y=value, x=x, group=group, colour=group,
fill=group)) +
geom_point() +
geom_line() +
geom_ribbon(aes(ymin=lwr, ymax=upr), alpha=.3, linetype=0) +
theme_minimal()
# looks ok, no lines at the edges
pl
# no lines at edges
dd = ggplotly(pl)
dd$x$data[[3]]$line$color = "rgba(248,118,109,0.0)"
dd$x$data[[4]]$line$color = "rgba(0,191,196,0.0)"
dd

ggplot2 z order of multiple geoms (background to foreground)

I am trying to plot multiple lines with surrounding area, using ggplot2, with geom_ribbon for the are, and a centerline with geom_line. The values are overlapping, but I'd like each ribbon/line combination to be either bottom or top as a combination.
Here's a reproducible example:
library(ggplot2)
x <- 1:10
y <- c(1+x/100, 2-x, .1*x^2)
data <- data.frame(x=rep(x,3), y=y, lower=y-1, upper=y+1, color=rep(c('green', 'blue', 'yellow'), each=10))
In the example I can get the plot I want by using this code:
ggplot() +
geom_ribbon(data=data[data$color=='green',],aes(x=x, ymin=lower, ymax=upper, fill=paste0('light',color))) +
geom_line(data=data[data$color=='green',],aes(x=x, y=y, col=color)) +
geom_ribbon(data=data[data$color=='blue',],aes(x=x, ymin=lower, ymax=upper, fill=paste0('light',color))) +
geom_line(data=data[data$color=='blue',],aes(x=x, y=y, col=color)) +
geom_ribbon(data=data[data$color=='yellow',],aes(x=x, ymin=lower, ymax=upper, fill=paste0('light',color))) +
geom_line(data=data[data$color=='yellow',],aes(x=x, y=y, col=color)) +
scale_color_identity() +
scale_fill_identity()
But when I keep it simple and us this this code
plot <- ggplot(data=data) +
geom_ribbon(aes(x=x, ymin=lower, ymax=upper, fill=paste0('light',color))) +
geom_line(aes(x=x, y=y, col=color)) +
scale_color_identity() +
scale_fill_identity()
the lines of the background data go over the 'top' ribbons, or if I switch the geom_line and geom_ribbon, my middle-lines are no longer visible.
For this example, the lengthy call works, but in my real data, I have a lot more lines, and I'd like to be able to switch lines from background to foreground dynamically.
Is there any way that I can tell ggplot2 that there is an ordering that has to switch between my different geoms?
P.S. I can't post images yet, sorry if my question seems unclear.
You could save some typing with a loop
ggplot(data=data) +
purrr::map(.x = split(data, f = data$color),
.f = function(d){
list(geom_ribbon(data=d,aes(x=x, ymin=lower, ymax=upper), fill=paste0('light',unique(d$color))),
geom_line(data=d,aes(x=x, y=y), col=unique(d$color)))
})

How to place multiple boxplots in the same column with ggplot(geom_boxplot)

I would like to built a boxplot in which the 4 factors (N1:N4) are overlaid in the same column. For example with the following data:
df<-data.frame(N=N,Value=Value)
Q<-c("C1","C1","C2","C3","C3","C1","C1","C2","C2","C3","C3","Q1","Q1","Q1","Q1","Q3","Q3","Q4","Q4","Q1","Q1","Q1","Q1","Q3","Q3","Q4","Q4")
N<-c("N2","N3","N3","N2","N3","N2","N3","N2","N3","N2","N3","N0","N1","N2","N3","N1","N3","N0","N1","N0","N1","N2","N3","N1","N3","N0","N1")
Value<-c(4.7,8.61,8.34,5.89,8.36,1.76,2.4,5.01,2.12,1.88,3.01,2.4,7.28,4.34,5.39,11.61,10.14,3.02,9.45,8.8,7.4,6.93,8.44,7.37,7.81,6.74,8.5)
with the following (usual) code, the output is 4 box-plots displayed in 4 columns for the 4 variables
ggplot(df, aes(x=N, y=Value,color=N)) + theme_bw(base_size = 20)+ geom_boxplot()
many thanks
Updated Answer
Based on your comment, here's a way to add marginal boxplots. We'll use the built-in mtcars data frame.
First, some set-up:
library(cowplot)
# Common theme elements
thm = list(theme_bw(),
guides(colour=FALSE, fill=FALSE),
theme(plot.margin=unit(rep(0,4),"lines")))
Now, create the three plots:
# Main plot
p1 = ggplot(mtcars, aes(wt, mpg, colour=factor(cyl), fill=factor(cyl))) +
geom_smooth(method="lm") + labs(colour="Cyl", fill="Cyl") +
scale_y_continuous(limits=c(10,35)) +
thm[-2] +
theme(legend.position = c(0.85,0.8))
# Top margin plot
p2 = ggplot(mtcars, aes(factor(cyl), wt, colour=factor(cyl))) +
geom_boxplot() + thm + coord_flip() + labs(x="Cyl", y="")
# Right margin plot
p3 = ggplot(mtcars, aes(factor(cyl), mpg, colour=factor(cyl))) +
geom_boxplot() + thm + labs(x="Cyl", y="") +
scale_y_continuous(limits=c(10,35))
Lay out the plots and add the legend:
plot_grid(plotlist=list(p2, ggplot(), p1, p3), ncol=2,
rel_widths=c(5,1), rel_heights=c(1,5), align="hv")
Original Answer
You can overlay all four boxplots in a single column, but the plot will be unreadable. The first example below removes N as the x coordinate, but keeps N as the colour aesthetic. This results in the four levels of N being plotted at a single tick mark (which I've removed by setting breaks to NULL). However, the plots are still dodged. To plot them one on top of the other, set the dodge width to zero, as I've done in the second example. However, the plots are not readable when they are overlaid.
ggplot(df, aes(x="", y=Value,color=N)) +
theme_bw(base_size = 20) +
geom_boxplot() +
scale_x_discrete(breaks=NULL) +
labs(x="")
ggplot(df, aes(x="", y=Value,color=N)) +
theme_bw(base_size = 20) +
geom_boxplot(position=position_dodge(0)) +
scale_x_discrete(breaks=NULL) +
labs(x="")

positioning labels on geom_bar

I am trying to create a horizontal bar chart with category labels using ggplot.
I have been able to create the plot without hassles, and can put labels on, however I suffer issues with the formatting. Ultimately I would like to have my label within the bar if it fits, otherwise just outside the bar without truncating the label.
The following are what I have tried so far.
Data
dt1 <- data.table(x=c("a","b","c","d","e"), y=c(43,52,296,102,157), y2=c(50,10,100,45,80))
Chart 1
ggplot() + geom_bar(data=dt1, aes(x=x, y=y), stat="identity",fill="red") + coord_flip() +
geom_text(data=dt1, aes(x=x, y=y, label=paste0("$",y," from ",y2," records")),hjust=0)
As you can see below the labels get truncated.
Chart 2
I then came across this question which was helpful and made me realise that I was setting the label position based on my y variable so I have hardcoded it now and use hjust to pad it from the axis.
ggplot() + geom_bar(data=dt1, aes(x=x, y=y), stat="identity",fill="red") + coord_flip() +
geom_text(data=dt1, aes(x=x, y=0, label=paste0("$",y," from ",y2," records")),hjust=-0.1)
But you can see below that only 2 of the labels fit within the bar, so I would prefer the others to be placed at the end, on the outside of the bar like in chart 1.
Is there a programatic way I can get the best of both worlds from chart 1 and chart 2?
Move the hjust into the aes so we may vary off the value, then move it if the bar is a certain way past the max. It’s a bit hacky still, since it makes assumptions about the scaling, but looks pretty good. Divisor may need tweaking:
library(tidyverse)
dt1 <- data.frame(x=c("a","b","c","d","e"), y=c(43,52,296,102,157), y2=c(50,10,100,45,80))
ggplot() +
geom_bar(data=dt1, aes(x=x, y=y), stat="identity",fill="red") +
coord_flip() +
geom_text(
data=dt1,
aes(
x=x, y=y,
label=paste0("$",y," from ",y2," records"),
hjust=ifelse(y < max(dt1$y) / 1.5, -0.1, 1.1), # <- Here lies the magic
),
)
Results in this plot:
Here is one way. It is a bit lengthy approach, but you can subset your data for geom_text. In this way, you can manually assign the position you want for each bar.
ggplot() +
geom_bar(data = dt1, aes(x=x, y=y), stat="identity",fill="red") +
coord_flip() +
geom_text(data = filter(dt1, x == "e" | x == "c"),
aes(x=x, y=0, label=paste0("$",y," from ",y2," records")),hjust = -0.1) +
geom_text(data = filter(dt1, x == "d"),
aes(x=x, y=0, label=paste0("$",y," from ",y2," records")),hjust = - 1.1) +
geom_text(data = filter(dt1, x == "b"),
aes(x=x, y=0, label=paste0("$",y," from ",y2," records")),hjust = - 0.6) +
geom_text(data = filter(dt1, x == "a"),
aes(x=x, y=0, label=paste0("$",y," from ",y2," records")),hjust = - 0.5)
I'm going to misread programmatic as 'pragmatic'. Adding "+ scale_y_continuous(limits=c(0,max(dt1$y)+100))" created sufficient room for the labels. I lack the reputation to upload the plot.
ggplot() + geom_bar(data=dt1, aes(x=x, y=y), stat="identity",fill="red") + coord_flip() + geom_text(data=dt1, aes(x=x, y=y, label=paste0("$",y," from ",y2," records")),hjust=0) + scale_y_continuous(limits=c(0,max(dt1$y)+100))
Edit 2; I altered the code to retrieve the maximum value and add 100 to it. It's still not fitting the plot to include the text specifically but it'll work with fixed labels.

How to combine 2 plots (ggplot) into one plot?

By using R, is it possible to place 2 ggplot together (i.e., on the same plot)? I wish to show a trend from 2 different data frames and instead of putting them one next to the other, I'd like to integrate them together in one plot and only to change the color of one of them (the black dot).
To be more specific, I have the following 2 visuals:
ggplot(visual1, aes(ISSUE_DATE,COUNTED)) + geom_point() + geom_smooth(fill="blue", colour="darkblue", size=1)
and
ggplot(visual2, aes(ISSUE_DATE,COUNTED)) + geom_point() + geom_smooth(fill="red", colour="red", size=1)
They look like this (both have black dots and I'll need to change one of them to something different):
and
Creating a single combined plot with your current data set up would look something like this
p <- ggplot() +
# blue plot
geom_point(data=visual1, aes(x=ISSUE_DATE, y=COUNTED)) +
geom_smooth(data=visual1, aes(x=ISSUE_DATE, y=COUNTED), fill="blue",
colour="darkblue", size=1) +
# red plot
geom_point(data=visual2, aes(x=ISSUE_DATE, y=COUNTED)) +
geom_smooth(data=visual2, aes(x=ISSUE_DATE, y=COUNTED), fill="red",
colour="red", size=1)
however if you could combine the data sets before plotting then ggplot will
automatically give you a legend, and in general the code looks a bit cleaner
visual1$group <- 1
visual2$group <- 2
visual12 <- rbind(visual1, visual2)
p <- ggplot(visual12, aes(x=ISSUE_DATE, y=COUNTED, group=group, col=group, fill=group)) +
geom_point() +
geom_smooth(size=1)
Dummy data (you should supply this for us)
visual1 = data.frame(ISSUE_DATE=runif(100,2006,2008),COUNTED=runif(100,0,50))
visual2 = data.frame(ISSUE_DATE=runif(100,2006,2008),COUNTED=runif(100,0,50))
combine:
visuals = rbind(visual1,visual2)
visuals$vis=c(rep("visual1",100),rep("visual2",100)) # 100 points of each flavour
Now do:
ggplot(visuals, aes(ISSUE_DATE,COUNTED,group=vis,col=vis)) +
geom_point() + geom_smooth()
and adjust colours etc to taste.
Just combine them. I think this should work but it's untested:
p <- ggplot(visual1, aes(ISSUE_DATE,COUNTED)) + geom_point() +
geom_smooth(fill="blue", colour="darkblue", size=1)
p <- p + geom_point(data=visual2, aes(ISSUE_DATE,COUNTED)) +
geom_smooth(data=visual2, fill="red", colour="red", size=1)
print(p)

Resources