colour geom_rect under certain condition - r

I´ve got the following code:
ggplot(dummy$Crustacean) +
geom_rect(
aes(
xmin = char2num(sites_fct) - 0.4,
xmax = char2num(sites_fct) + 0.4,
ymin = ifelse(trophic == "Crustacean", 0.01, 1),
ymax = summed_tu),
colour = 'black', alpha =0.7) +
labs(y= expression("Summed TU"[EC10-QSAR]), x= "Sampling sites")+
scale_y_log10(limits = c(0.0001, 1)) +
# Fake discrete axis
scale_x_continuous(labels = sort(unique(dummy$Crustacean$sites_fct)), breaks = 1:9) +
# before the dot means vertical plotting
facet_grid(dummy$Crustacean$metrics_fct ~ dummy$Crustacean$trophic) +
theme_bw()+
# facet_grid box colour
theme(strip.background.x = element_rect(colour = "black", fill = "white"),
strip.background.y = element_blank(), strip.text.y = element_blank())+
theme(axis.text.x = element_text(size=10, margin =margin(0,0,0,0), angle =45, vjust = 1, hjust=1),
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.line.x = element_line(color = 'black', size=0.5),
axis.line.y = element_blank())
which give as uutput the following figure:
I need to change the colour of those boxes y > 0.01 in order to have this desire output:
I found several post about background (quite useful for the future) but I cloud not find something like my example.
Thanks!

OP, this should probably help you. You're trying to draw what appears to be a column or bar chart. In this case, it's probably best to use geom_col instead of geom_rect. With geom_col you only have to supply an x aesthetic (discrete value), and a y aesthetic for the height of the bar. You have not shared your data, but it seems the x axis is categorical already in your dataset, right?
Here's a reprex:
library(ggplot2)
set.seed(1234)
df <- data.frame(x=LETTERS, y=rnorm(26))
ggplot(df, aes(x,y)) +
geom_col(
aes(fill=ifelse(y>0, 'positive', 'negative')),
color='black', alpha=0.8
) +
scale_fill_manual(name='Value', values=c('positive'='orange', 'negative'='gray'))
What's going on here is that we only have to supply x and y to get the bars in the correct place and set the height. For the fill of each of the bars, you can actually just set the label to be "positive" or "negative" (or whatever your desired label would be) on the fly via an ifelse statement. Doing this alone will result in creating a legend automatically with fill colors chosen automatically. To fix a particular set of colors, I'm setting that manually via scale_fill_manual() and supplying a named vector to the values argument.
In your case, you can probably do something similar for geom_rect. That is, you could just try specifying fill= inside aes() and following a similar manner to here if you want... but I'd recommend switching to use geom_col, as it is most appropriate for what you're doing.
EDIT
As OP indicated in the comment, in the original question on which this is based, geom_rect is required since the bars minimum is not always the same number. The ymin aesthetic changes, so it makes sense to use geom_rect here.
The brute force way is to still use ifelse statements inside aes() for fill. It get's a bit dodgey, but it gets the job done:
ggplot(df) +
geom_rect(
aes(
xmin = char2num(sites) - 0.4,
xmax = char2num(sites) + 0.4,
ymin = ifelse(trop == "pt", 0.1, 1),
ymax = conc,
fill = ifelse(trop == "pt",
ifelse(conc > 0.1, 'positive', 'negative'),
ifelse(conc > 1, 'positive', 'negative'))
),
colour = 'black', alpha = 0.8
) +
scale_y_log10() +
# Fake discrete axis
scale_x_continuous(labels = sort(unique(df$sites)),
breaks = 1:3) +
scale_fill_manual(name='Conc', values=c('positive'='orange', 'negative'='gray')) +
facet_grid(. ~ trop) +
theme_bw()
To complete the setup, you may want to adjust the order of the items in the legend and avoid some of that kind of icky nested ifelse stuff. In that case, you can always do the checking outside the ggplot call. If you have more than the two values for df$trop, you can consider creating the df$conc_min column via a merge with another dataset, but it works just fine here.
df$conc_adjust <- char2num(df$sites)
df$conc_min <- ifelse(df$trop=='pt', 0.1, 1)
df$status <- ifelse(df$conc > df$conc_min, 'positive', 'negative')
# levels of the factor = the order appearing in the legend
df$status <- factor(df$status, levels=c('positive', 'negative'))
ggplot(df) +
geom_rect(
aes(
xmin = conc_adjust - 0.4,
xmax = conc_adjust + 0.4,
ymin = conc_min,
ymax = conc,
fill = status
),
colour = 'black', alpha = 0.8
) +
scale_y_log10() +
# Fake discrete axis
scale_x_continuous(labels = sort(unique(df$sites)),
breaks = 1:3) +
scale_fill_manual(name='Conc', values=c('positive'='orange', 'negative'='gray')) +
facet_grid(. ~ trop) +
theme_bw()

Related

ggplot2: combine fill and alpha legends

There are many questions out there pertaining to this topic, but none of the answers I have tried have worked for me so far.
I have a plot that is a heatmap with fill and alpha mapped to different values, i.e. different variables in my data create different colors and alpha values. I want to get a finished product here to see if this figure is worthwhile, so let's not discuss whether this is a good idea at the moment.
What I want to do is combine my fill and alpha legend such that I have the four different transparencies of blue, the four different transparencies of red, and for yellow. I can get those legends separately, or just one of them, but not two in one.
My best guess for code thus far has been
dummy <- data.frame(model=c(rep("X",23),rep("Y",23)),
longvarname=rep(c("CBH","NDMI","CovType","CH","CBD","NDVI_NF_750","Slope","TPI_Valley_1200", "TPI_Ridge_1200",
"TPI_Ridge_100","TPI_Valley_100", "TSHarv","Treat","RxBurn",
"TSTreat","TSRx","Deficit","SpecHumid","MaxRH","MinTemp","MaxTemp", "MaxGustDir", "MaxGustSpd"),2),
vargrp=rep(c(rep("Veg",6), rep("Topo",5), rep("Mgmt",5),rep("Clim",7)),2),
value=runif(46, min=0, max=1),
binary_slope=sample(c("negative","positive", "zero"), 46, replace=TRUE))
ggplot(dummy, aes(x=model, y=longvarname)) +
geom_tile(aes(fill=binary_slope, alpha=value))+
scale_alpha_binned(breaks=c(0.4, 0.6, 0.8, 1))+
facet_grid(vargrp~., scales='free_y', space="free_y")+
xlab("Model")+
ylab("Variable")+
scale_fill_manual(values=c("midnightblue","yellow1","red4"))+
# guides(fill=guide_legend(override.aes = list(fill=c(rep("#191970",4),
# rep("#FFEA00",4),
# rep("#8b0000",4)),
# alpha=rep(c(0.4,0.6,0.8,1),3))))+
theme(panel.background = element_blank(),
axis.text.x = element_text(angle = 45, hjust=1),
strip.text.y = element_blank(),
axis.ticks = element_blank())
The above code produces both legends which you can see in the example I attached. If you uncomment the guides() lines, the error I am getting is Error in [[<-.data.frame(*tmp*, i, value = c("#191970", "#191970", :
replacement has 12 rows, data has 3.
But most of my efforts have just resulted in only the fill legend at alpha=1. Another thought I had which I thought might get me there was in guides(), putting the alpha hex codes in front of each color hex code and then making alpha guide = "none", but no dice.
Thanks very much for your help!
Instead of making use of both fill and alpha one option would be to make use of just fill like so:
Add a column with your desired fill colors to your dataset using e.g. a left_join.
Manually compute your alpha levels using e.g. cut.
Adjust the transparency of th colors according to the alpha values using colorspace::adjust_transparency
Map the resulting colors on the fill aes and make use of scale_fill_identity. Add guide=guide_legend to get a legend.
library(ggplot2)
library(dplyr)
library(colorspace)
cols <- c(negative = "midnightblue", positive = "yellow1", zero = "red4")
cols <- tibble::enframe(cols, name = "binary_slope", value = "fill")
dummy <- left_join(dummy, cols, by = "binary_slope")
dummy <- mutate(dummy,
alpha = cut(value, breaks = c(0, 0.4, 0.6, 0.8, 1), labels = c(0.4, 0.6, 0.8, 1)),
alpha = as.numeric(as.character(alpha)),
fill = colorspace::adjust_transparency(fill, alpha)
)
ggplot(dummy, aes(x = model, y = longvarname)) +
geom_tile(aes(fill = fill)) +
scale_fill_identity(guide = guide_legend()) +
facet_grid(vargrp ~ ., scales = "free_y", space = "free_y") +
xlab("Model") +
ylab("Variable") +
theme(
panel.background = element_blank(),
axis.text.x = element_text(angle = 45, hjust = 1),
strip.text.y = element_blank(),
axis.ticks = element_blank()
)

R - ggplot2 - Add arrow if geom_errorbar outside limits when x-axis is a factor variable

I want to use geom_segment to replace error bars with arrows when the error exceeds a certain limit. I found a previous post that addresses this question: R - ggplot2 - Add arrow if geom_errorbar outside limits
The code works well, except that my x-axis is a factor variable instead of a numeric variable. Using position_dodge within the geom_segment statement makes the arrows start in the correct location, but it doesn't change the terminal point (xend) and all arrows point towards one central point on the x-axis instead of going straight up from the origins.
Instead of recoding the x-axis to be numeric (I will use this code to create many plots that have a range of x-axis values, with the last numeric value always ending in "+"), is there a way to correct this within geom_segment?
Code used:
data$OR.95U_u = ifelse(data$OR.95U > 10, 10 , NA)
ggplot(data, aes(x = numAlleles, y = OR, fill = Outcome)) +
geom_bar(position = position_dodge(.5), stat = "identity", width = .4, color = "black") + geom_hline(yintercept = 1, linetype = "dashed", color = "black") +
ylim(0,10) + geom_errorbar(aes(ymin=OR.95L, ymax=OR.95U), width=.2,position=position_dodge(.5)) +
theme(legend.key = element_blank(), text = element_text(size = 11.5), legend.title = element_blank()) +
labs(x = "Number of rare alleles") +
scale_fill_manual(values=c("chocolate1","coral1", "red2", "darkred")) +
geom_segment(aes(x = numAlleles, xend = numAlleles, y = OR, yend = OR.95U_u), position = position_dodge(.5), arrow = arrow(length = unit(0.3, "cm")))
Resulting figure
Ok, after investigating a bit, I didn't find a clean way of doing this, at it seems that position_dodge only change the x aes, and not the xend aes. position_nudge also don't work here, as it moves all the arrows at the same time.
So I came with a dirty way of doing this. All we need is create a new variable with the desired xend position for the geom_segment. I try and came with a semi-automtized way of doing it, for any number of levels of the coloring variable, and also created a reproducible dataset to work with, as I'm sure this could be improved a lot by people with more knowledge than me.
The code has inline comments expalining the steps:
library(tidyverse)
# dummy data (tried to replicate your plot data more or less accurately)
df <- tibble(
numAlleles = rep(c("1", "2+"), each = 4),
Outcome = rep(LETTERS[1:4], 2),
OR = c(1.4, 1.5, 1.45, 2.3, 3.8, 4.2, 4.0, 1.55),
OR.95U = c(1.9,2.1,1.9,3.8,12,12,12,12),
OR.95L = c(0.9, 0.9, 0.9, 0.8, NA, NA,NA,NA)
) %>%
mutate(
OR.95U_u = if_else(OR.95U > 10, 10, NA_real_)
)
# as it seems that position_dodge in a geom_segment only "dodge" the x aes and
# not the xend aes, we need to supply a custom xend. Also, we need to try
# to automatize the position, for more classes or different dodge widths.
# To do that, lets start with some parameters:
# position_dodge width
position_dodge_width <- 0.5
# number of bars per x axis class
bars_per_class <- length(unique(df$Outcome))
# total space available per class. In discrete vars, this is 1 au (arbitrary unit)
# for each class, but position_dodge only use the fraction of that unit
# indicated in the width parameter, so we need to calculate the real
# space available:
total_space_available <- 1 * position_dodge_width
# now we calculate the real bar width used by ggplot in these au, dividing the
# space available by the number of bars to plot for each class
bar_width_real <- (total_space_available / bars_per_class)
# position_dodge with discrete variables place bars to the left and to the right of the
# class au value, so we need to know when to place the xend to the left or
# to the right. Also, the number of bars has to be taken in to account, as
# in odd number of bars, one is located on the exact au value
if (bars_per_class%%2 == 0) {
# we need an offset, as bars are wider than arrows, and we want them in the
# middle of the bar
offset_segment <- bar_width_real / 2
# offset modifier to know when to substract or add the modifier
offset_modifier <- c(rep(-1, bars_per_class%/%2), rep(1, bars_per_class%/%2))
# we also need to know how meny bars to the left and how many to the right,
# but, the first bar of each side is already taken in account with the offset,
# so the bar modifier has to have one bar less for each side
bar_width_modifier <- c(seq((bars_per_class%/%2-1), 0), seq(0, (bars_per_class%/%2-1)))
} else {
# when odd number of columns, the offset is the same as the bar width
offset_segment <- bar_width_real
# and the modifiers have to have a middle zero value for the middle bar
offset_modifier <- c(rep(-1, bars_per_class%/%2), 0, rep(1, bars_per_class%/%2))
bar_width_modifier <- c(seq((bars_per_class%/%2-1), 0), 0, seq(0, (bars_per_class%/%2-1)))
}
# finally we create the vector of xend values needed:
df %>%
mutate(
numAlleles_u = as.numeric(as.factor(numAlleles)) + offset_modifier*(offset_segment + (bar_width_modifier*bar_width_real))
)
ggplot(df, aes(x = numAlleles, y = OR, fill = Outcome)) +
geom_bar(
position = position_dodge(position_dodge_width), stat = "identity",
width = 0.4, color = "black"
) +
geom_hline(yintercept = 1, linetype = "dashed", color = "black") +
ylim(0,10) +
geom_errorbar(
aes(ymin=OR.95L, ymax=OR.95U), width=.2,position=position_dodge(position_dodge_width)
) +
theme(
legend.key = element_blank(), text = element_text(size = 11.5),
legend.title = element_blank()
) +
labs(x = "Number of rare alleles") +
scale_fill_manual(values=c("chocolate1","coral1", "red2", "darkred")) +
geom_segment(
aes(x = numAlleles, xend = numAlleles_u, y = OR, yend = OR.95U_u),
position = position_dodge(position_dodge_width), arrow = arrow(length = unit(0.3, "cm"))
)
And the plot:
We can check that for three levels discrete variables also works:
df_three_bars <- df %>% filter(Outcome != 'D')
bars_per_class <- length(unique(df_three_bars$Outcome))
total_space_available <- 1 * position_dodge_width
bar_width_real <- (total_space_available / bars_per_class)
if (bars_per_class%%2 == 0) {
offset_segment <- bar_width_real / 2
offset_modifier <- c(rep(-1, bars_per_class%/%2), rep(1, bars_per_class%/%2))
bar_width_modifier <- c(seq((bars_per_class%/%2-1), 0), seq(0, (bars_per_class%/%2-1)))
} else {
offset_segment <- bar_width_real
offset_modifier <- c(rep(-1, bars_per_class%/%2), 0, rep(1, bars_per_class%/%2))
bar_width_modifier <- c(seq((bars_per_class%/%2-1), 0), 0, seq(0, (bars_per_class%/%2-1)))
}
df_three_bars <- df_three_bars %>%
mutate(
numAlleles_u = as.numeric(as.factor(numAlleles)) + offset_modifier*(offset_segment + (bar_width_modifier*bar_width_real))
)
ggplot(df_three_bars, aes(x = numAlleles, y = OR, fill = Outcome)) +
geom_bar(
position = position_dodge(position_dodge_width), stat = "identity",
width = 0.4, color = "black"
) +
geom_hline(yintercept = 1, linetype = "dashed", color = "black") +
ylim(0,10) +
geom_errorbar(
aes(ymin=OR.95L, ymax=OR.95U), width=.2,position=position_dodge(position_dodge_width)
) +
theme(
legend.key = element_blank(), text = element_text(size = 11.5),
legend.title = element_blank()
) +
labs(x = "Number of rare alleles") +
scale_fill_manual(values=c("chocolate1","coral1", "red2", "darkred")) +
geom_segment(
aes(x = numAlleles, xend = numAlleles_u, y = OR, yend = OR.95U_u),
position = position_dodge(position_dodge_width), arrow = arrow(length = unit(0.3, "cm"))
)

Adding legend for combo bar and line graph -- ggplot ignoring commands

I am trying to make a bar chart with line plots as well. The graph has created fine but the legend does not want to add the line plots to the legend.
I have tried so many different ways of adding these to the legend including:
ggplot Legend Bar and Line in Same Graph
None of which have worked. show.legend also seems to have been ignored in the geom_line aes.
My code to create the graph is as follows:
ggplot(first_q, aes(fill = Segments)) +
geom_bar(aes(x= Segments, y= number_of_new_customers), stat =
"identity") + theme(axis.text.x = element_blank()) +
scale_y_continuous(expand = c(0, 0), limits = c(0,3000)) +
ylab('Number of Customers') + xlab('Segments') +
ggtitle('Number Customers in Q1 by Segments') +theme(plot.title =
element_text(hjust = 0.5)) +
geom_line(aes(x= Segments, y=count) ,stat="identity",
group = 1, size = 1.5, colour = "darkred", alpha = 0.9, show.legend =
TRUE) +
geom_line(aes(x= Segments, y=bond_count)
,stat="identity", group = 1, size = 1.5, colour = "blue", alpha =
0.9) +
geom_line(aes(x= Segments, y=variable_count)
,stat="identity", group = 1, size = 1.5, colour = "darkgreen",
alpha = 0.9) +
geom_line(aes(x= Segments, y=children_count)
,stat="identity", group = 1, size = 1.5, colour = "orange", alpha
= 0.9) +
guides(fill=guide_legend(title="Segments")) +
scale_color_discrete(name = "Prod", labels = c("count", "bond_count", "variable_count", "children_count)))
I am fairly new to R so if any further information is required or if this question could be better represented then please let me know.
Any help is greatly appreciated.
Alright, you need to remove a little bit of your stuff. I used the mtcars dataset, since you did not provide yours. I tried to keep your variable names and reduced the plot to necessary parts. The code is as follows:
first_q <- mtcars
first_q$Segments <- mtcars$mpg
first_q$val <- seq(1,nrow(mtcars))
first_q$number_of_new_costumers <- mtcars$hp
first_q$type <- "Line"
ggplot(first_q) +
geom_bar(aes(x= Segments, y= number_of_new_costumers, fill = "Bar"), stat =
"identity") + theme(axis.text.x = element_blank()) +
scale_y_continuous(expand = c(0, 0), limits = c(0,3000)) +
geom_line(aes(x=Segments,y=val, linetype="Line"))+
geom_line(aes(x=Segments,y=disp, linetype="next line"))
The answer you linked already gave the answer, but i try to explain. You want to plot the legend by using different properties of your data. So if you want to use different lines, you can declare this in your aes. This is what get's shown in your legend. So i used two different geom_lines here. Since the aes is both linetype, both get shown at the legend linetype.
the plot:
You can adapt this easily to your use. Make sure you using known keywords for the aesthetic if you want to solve it this way. Also you can change the title names afterwards by using:
labs(fill = "costum name")
If you want to add colours and the same line types, you can do customizing by using scale_linetype_manual like follows (i did not use fill for the bars this time):
library(ggplot2)
first_q <- mtcars
first_q$Segments <- mtcars$mpg
first_q$val <- seq(1,nrow(mtcars))
first_q$number_of_new_costumers <- mtcars$hp
first_q$type <- "Line"
cols = c("red", "green")
ggplot(first_q) +
geom_bar(aes(x= Segments, y= number_of_new_costumers), stat =
"identity") + theme(axis.text.x = element_blank()) +
scale_y_continuous(expand = c(0, 0), limits = c(0,3000)) +
geom_line(aes(x=Segments,y=val, linetype="solid"), color = "red", alpha = 0.4)+
geom_line(aes(x=Segments,y=disp, linetype="second"), color ="green", alpha = 0.5)+
scale_linetype_manual(values = c("solid","solid"),
guide = guide_legend(override.aes = list(colour = cols)))

Add vertical lines at multiple datetimes of outlier points using ggplot2

I have a dataframe like this
PDATETIME <- c("2017-02-23 06:08:39","2017-02-25 15:31:50","2017-03-06 17:11:57","2017-03-15 01:23:51",
"2017-03-16 15:54:35","2017-03-16 23:48:14","2017-03-18 02:57:41","2017-03-20 05:12:33")
DELTA <- c(2.5,8,3.5,4.5,5.5,8.3,3.3,4)
Type <- c(NA,"Outlier",NA,NA,NA,"Outlier",NA,NA)
df <- data.frame(PDATETIME,DELTA,Type)
df$PDATETIME <- as.POSIXct(df$PDATETIME,format="%Y-%m-%d %H:%M:%S")
I am trying to draw vertical lines at the outlier points using ggplot2
library(ggplot2)
library(ggrepel)
ggplot(data = df, aes(PDATETIME,DELTA ))+
ggtitle("Outlier Analysis") +
theme(axis.text.x = element_text(angle=90, vjust=1),plot.title = element_text(size = rel(1))) +
geom_point(colour="black") +
geom_vline(aes(xintercept=df$PDATETIME[which(df$Type %in% "Outlier")],linetype=4, colour="black")) +
geom_text_repel(aes(PDATETIME, DELTA,
label = Type),
size =4,
fontface = 'bold',
color = 'red',
box.padding = 0.5,
point.padding = 0.5,
segment.color = 'darkblue',
segment.size = 0.5,
arrow = arrow(length = unit(0.01, 'npc'))) +
xlab("PDATETIME")+
ylab("DELTA")
It throws an error "Error: A continuous variable can not be mapped to linetype"
The outlier points are at 2017-02-25 15:31:50, 2017-03-16 23:48:14
What am I missing here? Could someone point me in the right direction?
linetype and color are not varying so you can move it outside aes. Also I recommend that you modify your code to:
geom_vline(data = df[which(df$Type %in% "Outlier"),],
aes(xintercept = PDATETIME),
linetype = 4, colour = "black")
We don't need aes inside geom_vline, try:
geom_vline(xintercept = df$PDATETIME[ which(df$Type %in% "Outlier") ], linetype = 4, colour = "black")
You need to move linetype and colour out of aes:
geom_vline(aes(xintercept=df$PDATETIME[which(df$Type %in% "Outlier")]),linetype=4, colour="black")
You would only want linetype and/or colour inside of aes() if you want them to vary according to some variable, like df$type.

ggplot in R - stop error bars going below zero

I'm plotting the modelled population density of several bird species +/- standard error. Because the y variable is density, values of less than zero make no sense, I want to truncate the error bars so they don't go below zero. However, I'm having trouble doing this.
This code works fine, but as you can see for Black Kite the error bars go below zero:
bird.plot.data <- data.frame(species = rep(c("Black kite", "Cormorant","Goosander"),2),
Restored = c(rep("YES",3), rep("NO",3)),
est.count = c(1.48, 3.12, 20.0, 0, 5.18, 2.11),
std.err = c(1.78, 1.78, 1.39, 0, 0.66, 1.02))
bird.plot <- ggplot(data = bird.plot.data, aes(x = Restored))+
facet_wrap(~ species, scales = "free_y")+
geom_col(aes(y = est.count, fill = Restored), position = position_dodge())+
geom_errorbar(aes(ymax = est.count + std.err, ymin = est.count - std.err ))+
scale_fill_manual(values = c("darkgreen", "olivedrab1"))+
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.title.x = element_blank())+
ylab("Estimated density (birds/km\U00B2)")
last_plot()
I've tried a couple of options. The most obvious one would be to modify the ymin of the error bars themselves to be no lower than zero. However, this messes up the error bars completely and I'm not sure why:
b.p.mod <- ggplot(data = bird.plot.data, aes(x = Restored))+
facet_wrap(~ species, scales = "free_y")+
geom_col(aes(y = est.count, fill = Restored), position = position_dodge())+
geom_errorbar(aes(ymax = est.count + std.err, ymin = max(est.count - std.err, 0)))+
scale_fill_manual(values = c("darkgreen", "olivedrab1"))+
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.title.x = element_blank())+
ylab("Estimated density (birds/km\U00B2)")
last_plot()
Another option would be to limit the y axis to 0 so the error bar is not shown below zero. However, the cropping method
b.p.mod2 <- bird.plot + ylim(0,NA)
last_plot()
removes the error bar completely, which I don't want. The zooming method
b.p.mod3 <- bird.plot + coord_cartesian(ylim = c(0,NA))
last_plot() # Produces error
Doesn't let me leave the upper end unspecified, which is important as different species have very different densities.
Thoughts? My preferred solution would be to work out why the first option is creating such odd results.
I know this is an old post but maybe somebody will stumble upon the same problem.
For me:
geom_errorbar(aes(ymax = est.count + std.err, ymin = ifelse(est.count - std.err < 0, 0, est.count - std.err)))
works perfectly fine :)

Resources