I have a plot in R to analyse the functional behaviour of this function :
-2.5*log10(x) in different regions , the inputs are like this
curve(-2.5*log10(x),0,5,main="Behaviour of magnification function",col=2)
abline(v=0,col=3)
abline(v=1,col=4)
abline(h=0,col=8)
Now I would like to shade those regions on the right of the v=1 line and left with two different shades, but can't figure it out how to do. the polygon function not giving the shade , please help
thanks,
Ayan
You can use the panel.first argument to draw in the background.
Define a function to do the drawing:
fnShade = function(v,colL,colR) {
p = par("usr") # limits of the plot
rect(p[1],p[3],v,p[4],col=colL,border=NA)
rect(v,p[3],p[2],p[4],col=colR,border=NA)
abline(v=v,col=4)
}
Then use it
curve(-2.5*log10(x),0,5,main="Behaviour of magnification function",col=2,
panel.first=fnShade(1,5,"grey80"))
You can add your other ablines into the function if you like. And it should be easy to extend to draw 4 rectangles - one for each quardant - if desired.
Here is a ggplot solution to the problem, using annotate:
First, save the results from curve in df:
df <- curve(-2.5*log10(x),0,5,main="Behaviour of magnification function",col=2)
Then use geom_vline to add the vertical lines, and annotate to add a rect geom to the data for the shaded areas. We use annotate, because we do not want to use data from the original data frame as suggested in this answer.
plot <- ggplot(data.frame(df)) +
geom_line(aes(x, y), size = 1) +
geom_vline(xintercept = 1, color = "blue", size = 1) +
geom_hline(yintercept = 0, color ="grey", size = 1) +
geom_vline(xintercept = 0, color = "green", size = 1) +
annotate("rect", xmin = 1, xmax = Inf, ymax = Inf, ymin = -Inf, fill = "blue", alpha = 0.2) +
annotate("rect", xmin = -Inf, xmax = 1, ymax = Inf, ymin = -Inf, fill = "green", alpha = 0.2) +
labs(title = "Behaviour of magnification function\n") +
theme_bw()
plot
Related
I am trying to reproduce a plot using ggplot.
The code I got from the textbook:
skeptic<-c(1,1.171,1.4,1.8,2.2,2.6,3,3.4,3.8,3.934,4.2,
4.6,5,5.4,5.8,6.2,6.6,7,7.4,7.8,8.2,8.6,9)
effect<-c(-.361,-.327,-.281,-.200,-.120,-.039,.041,.122,.202,.229,.282,
.363,.443,.524,.604,.685,.765,.846,.926,1.007,1.087,1.168,1.248)
llci<-c(-.702,-.654,-.589,-.481,-.376,-.276,-.184,-.099,-.024,0,.044,.105,
.161,.212,.261,.307,.351,.394,.436,.477,.518,.558,.597)
ulci<-c(-.021,0,.028,.080,.136,.197,.266,.343,.428,.458,.521,.621,.726,.836,
.948,1.063,1.180,1.298,1.417,1.537,1.657,1.778,1.899)
plot(x=skeptic,y=effect,type="l",pch=19,ylim=c(-1,1.5),xlim=c(1,6),lwd=3,
ylab="Conditional effect of disaster frame",
xlab="Climate Change Skepticism (W)")
points(skeptic,llci,lwd=2,lty=2,type="l")
points(skeptic,ulci,lwd=2,lty=2,type="l")
abline(h=0, untf=FALSE,lty=3,lwd=1)
abline(v=1.171,untf=FALSE,lty=3,lwd=1)
abline(v=3.934,untf=FALSE,lty=3,lwd=1)
text(1.171,-1,"1.171",cex=0.8)
text(3.934,-1,"3.934",cex=0.8)
The exemplary plot is
I have tried ggplot but I am struggling with the vertical and horizontal dashed line. Could anybody reproduce the plot using ggplot? And I have a follow-up question. How can I mark the area of x < 3.934 and x > 1.171? Thank you!
Here is a way to reproduce the posted graph.
library(ggplot2)
library(magrittr)
library(tidyr)
df1 <- data.frame(skeptic, effect, llci, ulci)
vlines <- data.frame(x = c(0, 1.171, 3.934))
vertices <- data.frame(xmin = 1.171, xmax = 3.934,
ymin = -Inf, ymax = Inf)
brks <- names(df1)[-1]
df1 %>%
pivot_longer(-skeptic, names_to = "line") %>%
ggplot(aes(skeptic, value)) +
geom_rect(data = vertices,
mapping = aes(xmin = xmin, xmax = xmax,
ymin = ymin, ymax = ymax),
fill = "blue", alpha = 0.2,
inherit.aes = FALSE) +
geom_line(aes(size = line, linetype = line)) +
geom_hline(yintercept = 0, linetype = "dotted") +
geom_vline(data = vlines,
mapping = aes(xintercept = x),
linetype = "dotted") +
geom_text(data = subset(vlines, x != 0),
mapping = aes(x = x, label = x),
y = -0.75,
hjust = 0, vjust = 1) +
scale_size_manual(breaks = brks, values = c(1, 0.5, 0.5)) +
scale_linetype_manual(breaks = brks, values = c("solid", "dashed", "dashed")) +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
Constructing on your specific question (horizontal and vertical lines and area) as you said you got already the remaining parts right.
Use geom_hline for horizontal line and geom_vline for vertical one. linetype="dashed" will render dashed lines. As you didn't tell how you want the area rendered, here is my guess, a vertical grayed area extending horizontally from abcissa of your vertical lines and vertically from min effect to max effect (Inf values) drawn using a geom_rect.
ggplot(data.frame(skeptic,effect))+
geom_line(aes(skeptic,effect))+
geom_rect(aes(xmin=1.171,xmax=3.934,ymin=-Inf,ymax=Inf),fill="lightgray")+
geom_hline(yintercept=0,linetype="dashed") +
geom_vline(xintercept=c(1.171,3.934),linetype="dashed")
I am trying to create a ggplot histogram with a density overlay, where the alpha changes past the number 1. An example can be seen on 538 under the Every outcome in our simulations section. The alpha differs based on the electoral vote count. I am close to getting a similar graph but I cannot figure out how to get the density and histogram to work together.
Code
library(data.table)
library(ggplot2)
dt <- data.table(ratio = rnorm(10000, mean = .5, sd = 1))
dt[, .(ratio,
al = (ratio >= 1))] %>%
ggplot(aes(x = ratio, alpha = al)) +
geom_histogram(aes(), bins = 100,
fill = 'red') +
geom_density(aes(),size = 1.5,
color = 'blue') +
geom_vline(xintercept = 1,
color = '#0080e2',
size = 1.2) +
scale_alpha_discrete(range = c(.65, .9))
This attempt correctly changes alpha past 1 as desired but the density estimate is not scaled.
dt[, .(ratio,
al = (ratio >= 1))] %>%
ggplot(aes(x = ratio)) +
geom_histogram(aes(y = ..density.., alpha = al), bins = 100,
fill = 'red') +
geom_density(aes(y = ..scaled..),size = 1.5,
color = 'blue',) +
geom_vline(xintercept = 1,
color = '#0080e2',
size = 1.2) +
scale_alpha_discrete(range = c(.65, .9))
This attempt correctly scales the density curve, but now the geom_histogram is calculated separately for values under 1 and above 1. I want them calculated as one group.
What am I missing?
The reason why knowing your theme is important is that there's an easy shortcut to this, which is not using alpha, but just drawing a semitransparent rectangle over the left half of your plot:
library(data.table)
library(ggplot2)
library(dplyr)
data.table(ratio = rnorm(10000, mean = .5, sd = 1)) %>%
ggplot(aes(x = ratio)) +
geom_histogram(aes(y = ..density..), bins = 100,
fill = 'red') +
geom_line(aes(), stat = "density", size = 1.5,
color = 'blue') +
geom_vline(xintercept = 1,
color = '#0080e2',
size = 1.2) +
annotate("rect", xmin = -Inf, xmax = 1, ymin = 0, ymax = Inf, fill = "white",
alpha = 0.5) +
theme_bw()
Splitting into two groups and using alpha is possible, but it basically requires you to precalculate the histogram and the density curve. That's fine, but it would be an awful lot of extra effort for very little visual gain.
Of course, if theme_josh has a custom background color and zany gridlines, this approach may not be quite so effective. As long as you set the fill color to the panel background you should get a decent result. (the default ggplot panel is "gray90" or "gray95" I think)
I am trying to add four transparent bands to my ggplot for the following y ranges:
y<2 & y>1.5
y<1.5 & y>1
y<1 & y>0.5
y<0.5 & y>0
I don't want the ranges to overlap as that changes the colour that I'm assigning each band (as they're transparent).
I can sort of get the effect I'm after using geom_area (see code below), but they overlap, which changes the colour.
I'm wondering if there is a better way to get the bands specifically in the areas I want?
df <- data.frame(y1=rep(1.99, 100),
y2=rep(1.49, 100),
y3=rep(0.99, 100),
y4=rep(0.49, 100),
x =1:100)
ggplot(aes(x=x), data = df) +
geom_area(aes(y=ifelse(y1<2 & y1>1.5, y1, 0)), data=df, fill="yellow", alpha = 0.3) +
geom_area(aes(y=ifelse(y2<1.5 & y2>1, y2, 0)), data=df, fill="darkgoldenrod1", alpha = 0.3) +
geom_area(aes(y=ifelse(y3<1 & y3>0.5, y3, 0)), data=df, fill="darkorange1", alpha = 0.3) +
geom_area(aes(y=ifelse(y4<0.5 & y4>0, y4, 0)), data=df, fill="darkred", alpha = 0.3) +
theme_classic()
Also, potentially separate question, is there a way to make the fill color go all the way to the axis rather than just leaving a white buffer space around it?
Use geom_rect before plotting any points
ggplot() +
geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = 1.5, ymax = 2), fill="yellow", alpha = 0.3) +
geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = 1, ymax = 1.5), fill="darkgoldenrod1", alpha = 0.3) +
geom_point(data = df, aes(x = x, y = y1)) +
theme_classic()
See geom_rect and alpha - does this work with hard coded values? for getting alpha to work correctly with the rectangles.
I am trying to annotate a ggplot histogram with a shaded rectangle, and am trying to figure out if there is a way to pass an argument to ymax that will dynamically scale the rectangle to the ymax of the plotting area.
I can achieve the desired effect by hard coding the ymax value of the annotate() to be greater than the plot ymax then 'cropping' back the plot using coord_cartesian() shown in the example below. However, this requires me to know a priori what the max of the histogram will be, which of course will change if I adjust binwidth. There is some way to scale ymax dynamically?
ggplot(
data = mtcars,
aes(
x=mpg
)
)+
geom_histogram(
binwidth = 3,
fill = "gray63"
)+
annotate(
"rect",
xmin = 21,
xmax = 22,
ymin = 0,
ymax = 10, #hardcode ymax to be > plot ymax
fill = "gray18",
alpha = 0.5
)+
theme_bw()
coord_cartesian(
ylim = c(0,8) #crop back to plot ymax
)
Many position arguments can accept Inf or -Inf, setting them to whatever is the highest/lowest value currently shown. That's set by the plot limits, not necessarily the data. You can then drop the coord_cartesian bit, because you don't need to hard-code the limits any more.
library(ggplot2)
ggplot(mtcars, aes(x = mpg)) +
geom_histogram(binwidth = 3, fill = "gray63") +
annotate("rect", xmin = 21, xmax = 22, ymin = 0, ymax = Inf, fill = "gray18", alpha = 0.5)
If, for whatever reason, you needed the plot to show a higher limit, you can see that Inf will then adjust accordingly:
ggplot(mtcars, aes(x = mpg)) +
geom_histogram(binwidth = 3, fill = "gray63") +
annotate("rect", xmin = 21, xmax = 22, ymin = 0, ymax = Inf, fill = "gray18", alpha = 0.5) +
ylim(0, 12)
I'm using ggplot, and am trying to add a ribbon in the form of a simple rectangle to a barplot I have. The idea is to show a cutoff below a certain value.
The barplot is fine but I can't quite get the ribbon right - I'd like it displayed a little wider but it seems to be limited to the width of the barplot data.
I tried using xmin and xmax but that doesn't increase the width of the shaded area.
Is there a way of explicitly controlling the width of geom_ribbon?
# Where df is a data frame containing the data to plot
library(cowplot)
ggplot(df, aes(x=treatments, y=propNotEliminated)) +
geom_ribbon(aes(xmin=0, xmax=21, ymin=0, ymax=20)) + # the xmin and xmax don't do what I'd expect
geom_bar(stat="identity", fill="white", colour="black", size=1) +
theme_cowplot()
Why not use geom_rect?
ggplot(mtcars, aes(factor(cyl))) +
geom_bar() +
geom_rect(xmin = 0, xmax = Inf, ymin = 0, ymax = 1, fill = "blue") +
geom_rect(xmin = 1, xmax = 3, ymin = 1, ymax = 2, fill = "red") +
geom_rect(xmin = 1 - 0.5, xmax = 3 + 0.5, ymin = 2, ymax = 3, fill = "green")
After you're satisfied with the placement, put geom_bar last.