y axis limits with bar graphs - r

I can display a bar graph, but the y axis always starts at zero, which doesn't make sense with this data:
y <- data.frame(x=c("a","b","c","d","e","f"), y=c(500,501,502,503,504,505))
ggplot(y, aes(x=x, y=y)) +
stat_summary(fun.y=mean, geom="bar")
Ideally, R would automatically set axis limits like it usually does. If I try to set manually, like as follows, my bars disappear. Any idea why?
y <- data.frame(x=c("a","b","c","d","e","f"), y=c(500,501,502,503,504,505))
ggplot(y, aes(x=x, y=y)) +
stat_summary(fun.y=mean, geom="bar") +
scale_y_continuous(limits=c(490,510))

I think you can use ylim as follows:
y <- data.frame(x=c("a","b","c","d","e","f"), y=c(500,501,502,503,504,505))
ggplot(y, aes(x=x, y=y)) +
stat_summary(fun.y=mean, geom="bar") + coord_cartesian(ylim=c(490,510))
The resulting output is as follows:

Related

Is there a way to scale x due to a right justified graph

Say I have a graph based on the following data:
library(tidyverse)
a <- tibble(x=1:10, y=.75^x)
ggplot(a, aes(x=x, y = y))+
geom_line()
https://i.imgur.com/fkA91EP.png
This is obviously an exponential decay graph, which would mean technically speaking it's all "squished" to the left or left justified or whatever it's called, so to "unsquish it" it's clear I would add a scale_x_log10() function to it.
ggplot(a, aes(x=x, y = y))+
geom_line()+
scale_x_log10()
https://i.imgur.com/fMd46UY.png
The problem I'm having is I want to do that for a graph that is right justified.
Example graph:
a <- tibble(x=1:10, y=x^10)
ggplot(a, aes(x=x, y = y))+
geom_line()
https://i.imgur.com/QBxmnwx.png
Is there a way to do this with ggplot?
You could try a different transformation for the x-axis, e.g.:
ggplot(a, aes(x=x, y = y))+ scale_x_continuous(trans = modulus_trans(2)) + geom_line()

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)))
})

Edit 2 stat_hex_bin geoms separately ggplot2

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)

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.

Seemingly incorrect guide color when geom_line() and geom_segment() are used together

I'm drawing some line segments on top of a plot that uses geom_line(). Surprisingly, the guide (legend) colors for geom_line() are drawn as the color of the last element I add to the plot - even if it is not the geom_line(). This looks like a bug to me, but it could be expected behavior for some reason I don't understand.
#Set up the data
require(ggplot2)
x <- rep(1:10, 2)
y <- c(1:10, 1:10+5)
fac <- gl(2, 10)
df <- data.frame(x=x, y=y, fac=fac)
#Draw the plot with geom_segment second, and the guide is the color of the segment
ggplot(df, aes(x=x, y=y, linetype=fac)) +
geom_line() +
geom_segment(aes(x=2, y=7, xend=7, yend=7), colour="red")
Whereas if I add the geom_segment first, the colors on the guide are black as I would expect:
ggplot(df, aes(x=x, y=y, linetype=fac)) +
geom_segment(aes(x=2, y=7, xend=7, yend=7), colour="red") +
geom_line()
Feature or bug? If the first, can someone explain what's happening?
Feature(ish). The guide that is drawn is a guide for linetype. But, it has to be drawn in some color to be seen. When the color is not specified by an aesthetic mapping, ggplot2 draws it in a color that is consistent with the plot. I'm speculating that the default is whatever last color was used. That is why you are seeing differences when you plot them in a different order.
However, you can control these details of the legend.
ggplot(df, aes(x=x, y=y, linetype=fac)) +
geom_line() +
geom_segment(aes(x=2, y=7, xend=7, yend=7), colour="red") +
scale_linetype_discrete(guide=guide_legend(override.aes=aes(colour="blue")))

Resources