Conditionally change panel background with facet_grid? - r

I'm using the "tips" data set in ggplot2. If I do
sp = ggplot(tips,aes(x=total_bill, y = tip/total_bill)) +
geom_point(shape=1) +
facet_grid(sex ~ day)
The plot comes out fine. But I now want to change the panel background for just the plots under "Fri". Is there a way to do this?
Even better, can I conditionally change colors by passing parameters? For example if more than 3 points are below 0.1, then change panel background (for just that panel) to a certain color while all others remain the default light grey?

The general rule for doing anything in ggplot2 is to,
Create a data frame that encodes the information you want to plot
Pass that data frame to a geom
This is made a bit more complicated in this case because of the particular aspect of the plot you want to alter. The Powers That Be designed ggplot2 in a way that separates data elements of the plot (i.e. geom's) from non-data elements (i.e. theme's), and it so happens that the plot background falls under the "non-data" category.
There is always the option of modifying the underlying grid object manually but this is tedious and the details may change with different versions of ggplot2. Instead, we'll employ the "hack" that Hadley refers to in this question.
#Create a data frame with the faceting variables
# and some dummy data (that will be overwritten)
tp <- unique(tips[,c('sex','day')])
tp$total_bill <- tp$tip <- 1
#Just Fri
ggplot(tips,aes(x=total_bill, y = tip/total_bill)) +
geom_rect(data = subset(tp,day == 'Fri'),aes(fill = day),xmin = -Inf,xmax = Inf,
ymin = -Inf,ymax = Inf,alpha = 0.3) +
geom_point(shape=1) +
facet_grid(sex ~ day)
#Each panel
ggplot(tips,aes(x=total_bill, y = tip/total_bill)) +
geom_rect(data = tp,aes(fill = day),xmin = -Inf,xmax = Inf,
ymin = -Inf,ymax = Inf,alpha = 0.3) +
geom_point(shape=1) +
facet_grid(sex ~ day)

I cannot comment yet.. so here is an additional answer to joran his answer.
If you are having trouble with the transparency setting, like setting alpha = 0.2 but not noticing any difference, it might be because of the data that you give to ggplot.
"Thanks for clarifying your question. This was puzzling to me, so I went to google, and ended up learning something new (after working around some vagaries in their examples). Apparently what you are doing is drawing many rectangles on top of each other, effectively nullifying the semi-transparency you want. So, the only ways to overcome this are to hard-code the rectangle coordinates in a separate df"
This answer comes from
geom_rect and alpha - does this work with hard coded values?

Related

r ggplot fill the background of selected facets [duplicate]

I'm using the "tips" data set in ggplot2. If I do
sp = ggplot(tips,aes(x=total_bill, y = tip/total_bill)) +
geom_point(shape=1) +
facet_grid(sex ~ day)
The plot comes out fine. But I now want to change the panel background for just the plots under "Fri". Is there a way to do this?
Even better, can I conditionally change colors by passing parameters? For example if more than 3 points are below 0.1, then change panel background (for just that panel) to a certain color while all others remain the default light grey?
The general rule for doing anything in ggplot2 is to,
Create a data frame that encodes the information you want to plot
Pass that data frame to a geom
This is made a bit more complicated in this case because of the particular aspect of the plot you want to alter. The Powers That Be designed ggplot2 in a way that separates data elements of the plot (i.e. geom's) from non-data elements (i.e. theme's), and it so happens that the plot background falls under the "non-data" category.
There is always the option of modifying the underlying grid object manually but this is tedious and the details may change with different versions of ggplot2. Instead, we'll employ the "hack" that Hadley refers to in this question.
#Create a data frame with the faceting variables
# and some dummy data (that will be overwritten)
tp <- unique(tips[,c('sex','day')])
tp$total_bill <- tp$tip <- 1
#Just Fri
ggplot(tips,aes(x=total_bill, y = tip/total_bill)) +
geom_rect(data = subset(tp,day == 'Fri'),aes(fill = day),xmin = -Inf,xmax = Inf,
ymin = -Inf,ymax = Inf,alpha = 0.3) +
geom_point(shape=1) +
facet_grid(sex ~ day)
#Each panel
ggplot(tips,aes(x=total_bill, y = tip/total_bill)) +
geom_rect(data = tp,aes(fill = day),xmin = -Inf,xmax = Inf,
ymin = -Inf,ymax = Inf,alpha = 0.3) +
geom_point(shape=1) +
facet_grid(sex ~ day)
I cannot comment yet.. so here is an additional answer to joran his answer.
If you are having trouble with the transparency setting, like setting alpha = 0.2 but not noticing any difference, it might be because of the data that you give to ggplot.
"Thanks for clarifying your question. This was puzzling to me, so I went to google, and ended up learning something new (after working around some vagaries in their examples). Apparently what you are doing is drawing many rectangles on top of each other, effectively nullifying the semi-transparency you want. So, the only ways to overcome this are to hard-code the rectangle coordinates in a separate df"
This answer comes from
geom_rect and alpha - does this work with hard coded values?

Possible to force non-occurring elements to show in ggplot legend?

I'm plotting a sort of chloropleth of up to three selectable species abundances across a research area. This toy code behaves as expected and does almost what I want:
library(dplyr)
library(ggplot2)
square <- expand.grid(X=0:10, Y=0:10)
sq2 <- square[rep(row.names(square), 2),] %>%
arrange(X,Y) %>%
mutate(SPEC = rep(c('red','blue'),len=n())) %>%
mutate(POP = ifelse(SPEC %in% 'red', X, Y)) %>%
group_by(X,Y) %>%
mutate(CLR = rgb(X/10,0,Y/10)) %>% ungroup()
ggplot(sq2, aes(x=X, y=Y, fill=CLR)) + geom_tile() +
scale_fill_identity("Species", guide="legend",
labels=c('red','blue'), breaks=c('#FF0000','#0000FF'))
Producing this:
A modified version properly plots the real map, appropriately mixing the RGBs to show the species proportions per map unit. But given that mixing, the real data does not necessarily include the specific values listed in breaks, in which case no entry appears in the legend for that species. If you change the last line of the example to
labels=c('red','blue','green'), breaks=c('#FF0000','#0000FF','#00FF00'))
you get the same legend as shown, with only 'red' and 'blue' displayed, as there is no green in it. Searching the data for each max(Species) and assigning those to the legend is possible but won't make good legend keys for species that only occur in low proportions. What's needed is for the legend to display the idea of the entities present, not their attested presences -- three colors in the legend even if only one species is detected.
I'd think that scale_fill_manual() or the override.aes argument might help me here but I haven't been able to make any combination work.
Edit: Episode IV -- A New Dead End
(Thanks #r2evans for fixing my omission of packages.)
I thought I might be able to trick the legend by mutating a further column into the df in the processing pipe called spCLR to represent the color ('#FF0000', e.g.) that codes each entry's species (redundant info, but fine). Now the plotting call in my real version goes:
df %>% [everything] %>%
ggplot(aes(x = X, y = Y, height = WIDTH, width = WIDTH, fill = CLR)) +
geom_tile() +
scale_fill_identity("Species", guide="legend",
labels=spCODE, breaks=spCLR)
But this gives the error: Error in check_breaks_labels(breaks, labels) : object 'spCLR' not found. That seems weird since spCLR is indeed in the pipe-modified df, and of all the values supplied to the ggplot functions spCODE is the only one present in the original df -- so if there's some kind of scope problem I don't get it. [Re-edit -- I see that neither labels nor breaks wants to look at df$anything. Anyway.]
I assume (rightly?) there's some way to make this one work [?], but it still wouldn't make the legend show 'red', 'blue' and 'green' in my toy example -- which is what my original question is really about -- because there is still no actual green-data present in that. So to reiterate, isn't there any way to force a ggplot2 legend to show the things you want to talk about, rather than just the ones that are present in the data?
I have belatedly discovered that my question is a near-duplicate of this. The accepted answer there (from #joran) doesn't work for this but the second answer (from #Axeman) does. So the way for me to go here is that the last line should be
labels=c('red','blue','green'), limits=c('#FF0000','#0000FF','#00FF00'))
calling limits() instead of breaks(), and now my example and my real version work as desired.
I have to say I spent a lot of time digging around in the ggplot2 reference without ever gaining a suspicion that limits() was the correct alternative to breaks() -- which is explicitly mentioned in that ref page while limits() does not appear. The ?limits() page is quite uninformative, and I can't find anything that lays out the distinctions between the two: when this rather than that.
I assume from the heatmap use case that you have no other need for colour mapping in the chart. In this case, a possible workaround is to leave the fill scale alone, & create an invisible geom layer with colour aesthetic mapping to generate the desired legend instead:
ggplot(sq2, aes(x=X, y=Y)) +
geom_tile(aes(fill = CLR)) + # move fill mapping here so new point layer doesn't inherit it
scale_fill_identity() + # scale_*_identity has guide set to FALSE by default
# add invisible layer with colour (not fill) mapping, within x/y coordinates within
# same range as geom_tile layer above
geom_point(data = . %>%
slice(1:3) %>%
# optional: list colours in the desired label order
mutate(col = forcats::fct_inorder(c("red", "blue", "green"))),
aes(colour = col),
alpha = 0) +
# add colour scale with alpha set to 1 (overriding alpha = 0 above),
# also make the shape square & larger to mimic the default legend keys
# associated with fill scale
scale_color_manual(name = "Species",
values = c("red" = '#FF0000', "blue" = '#0000FF', "green" = '#00FF00'),
guide = guide_legend(override.aes = list(alpha = 1, shape = 15, size = 5)))

color cells of facet_grid according to cluster [duplicate]

I'm using the "tips" data set in ggplot2. If I do
sp = ggplot(tips,aes(x=total_bill, y = tip/total_bill)) +
geom_point(shape=1) +
facet_grid(sex ~ day)
The plot comes out fine. But I now want to change the panel background for just the plots under "Fri". Is there a way to do this?
Even better, can I conditionally change colors by passing parameters? For example if more than 3 points are below 0.1, then change panel background (for just that panel) to a certain color while all others remain the default light grey?
The general rule for doing anything in ggplot2 is to,
Create a data frame that encodes the information you want to plot
Pass that data frame to a geom
This is made a bit more complicated in this case because of the particular aspect of the plot you want to alter. The Powers That Be designed ggplot2 in a way that separates data elements of the plot (i.e. geom's) from non-data elements (i.e. theme's), and it so happens that the plot background falls under the "non-data" category.
There is always the option of modifying the underlying grid object manually but this is tedious and the details may change with different versions of ggplot2. Instead, we'll employ the "hack" that Hadley refers to in this question.
#Create a data frame with the faceting variables
# and some dummy data (that will be overwritten)
tp <- unique(tips[,c('sex','day')])
tp$total_bill <- tp$tip <- 1
#Just Fri
ggplot(tips,aes(x=total_bill, y = tip/total_bill)) +
geom_rect(data = subset(tp,day == 'Fri'),aes(fill = day),xmin = -Inf,xmax = Inf,
ymin = -Inf,ymax = Inf,alpha = 0.3) +
geom_point(shape=1) +
facet_grid(sex ~ day)
#Each panel
ggplot(tips,aes(x=total_bill, y = tip/total_bill)) +
geom_rect(data = tp,aes(fill = day),xmin = -Inf,xmax = Inf,
ymin = -Inf,ymax = Inf,alpha = 0.3) +
geom_point(shape=1) +
facet_grid(sex ~ day)
I cannot comment yet.. so here is an additional answer to joran his answer.
If you are having trouble with the transparency setting, like setting alpha = 0.2 but not noticing any difference, it might be because of the data that you give to ggplot.
"Thanks for clarifying your question. This was puzzling to me, so I went to google, and ended up learning something new (after working around some vagaries in their examples). Apparently what you are doing is drawing many rectangles on top of each other, effectively nullifying the semi-transparency you want. So, the only ways to overcome this are to hard-code the rectangle coordinates in a separate df"
This answer comes from
geom_rect and alpha - does this work with hard coded values?

Vertical dodge in geom_jitter

I have an ethogram-like ggplot where I plot the value of a factor quadrant (1 to 4), which is plotted for each frame of a movie (frameID). The color is given by 3 animals that are being tracked.
I am fairly satisfied with the graph but the amount of points makes it difficult to see, even with alpha. I was wondering how to add position_dodge in a way that doesn't destroy the plot.
ggplot(dataframe) ,
aes(frameID, quadrant, color=animal)) +
geom_jitter(alpha=0.5) +
scale_color_manual(values = c("#1334C1","#84F619", "#F43900")) +
theme_classic()+
theme(legend.position = 'none')
This link has useful info about dodging using geom_point.
R: How to spread (jitter) points with respect to the x axis?
I can change to geom_point with height, which works but it produces something awful.
+ geom_point(position = position_jitter(w = 0, h = 2))
Update
Data lives in GitHub
Lowering the alpha or changing size helps, adds trouble when rescaling the image.
https://github.com/matiasandina/MLA2_Tracking/blob/master/demo_data/sample_data.csv
Update 2022
It's been a while since I posted this initially, my original thoughts changed and are better reflected here, but I am still looking for a ggplot2 version of doing this!

Customize linetype in ggplot2 OR add automatic arrows/symbols below a line

I would like to use customized linetypes in ggplot. If that is impossible (which I believe to be true), then I am looking for a smart hack to plot arrowlike symbols above, or below, my line.
Some background:
I want to plot some water quality data and compare it to the standard (set by the European Water Framework Directive) in a red line. Here's some reproducible data and my plot:
df <- data.frame(datum <- seq.Date(as.Date("2014-01-01"),
as.Date("2014-12-31"),by = "week"),y=rnorm(53,mean=100,sd=40))
(plot1 <-
ggplot(df, aes(x=datum,y=y)) +
geom_line() +
geom_point() +
theme_classic()+
geom_hline(aes(yintercept=70),colour="red"))
However, in this plot it is completely unclear if the Standard is a maximum value (as it would be for example Chloride) or a minimum value (as it would be for Oxygen). So I would like to make this clear by adding small pointers/arrows Up or Down. The best way would be to customize the linetype so that it consists of these arrows, but I couldn't find a way.
Q1: Is this at all possible, defining custom linetypes?
All I could think of was adding extra points below the line:
extrapoints <- data.frame(datum2 <- seq.Date(as.Date("2014-01-01"),
as.Date("2014-12-31"),by = "week"),y2=68)
plot1 + geom_point(data=extrapoints, aes(x=datum2,y=y2),
shape=">",size=5,colour="red",rotate=90)
However, I can't seem to rotate these symbols pointing downward. Furthermore, this requires calculating the right spacing of X and distance to the line (Y) every time, which is rather inconvenient.
Q2: Is there any way to achieve this, preferably as automated as possible?
I'm not sure what is requested, but it sounds as though you want arrows at point up or down based on where the y-value is greater or less than some expected value. If that's the case, then this satisfies using geom_segment:
require(grid) # as noted by ?geom_segment
(plot1 <-
ggplot(df, aes(x=datum,y=y)) + geom_line()+
geom_segment(data = data.frame( df$datum, y= 70, up=df$y >70),
aes(xend = datum , yend =70 + c(-1,1)[1+up]*5), #select up/down based on 'up'
arrow = arrow(length = unit(0.1,"cm"))
) + # adjust units to modify size or arrow-heads
geom_point() +
theme_classic()+
geom_hline(aes(yintercept=70),colour="red"))
If I'm wrong about what was desired and you only wanted a bunch of down arrows, then just take out the stuff about creating and using "up" and use a minus-sign.

Resources