Related
I am using the ggpattern package in R for the first time and I can't get the image to work in my plot. A simplified version of my code is here:
x = seq(-1.5, 3.5, 0.1)
y = c( rep(1.0, 22), rep(0.2, 12), rep(0.7, 7), rep(1,10))
ref = data.frame(x = x, y = y)
library(dplyr)
library(ggplot2)
library(ggpattern)
coral = system.file("Coral", "coral.jpg", package="ggpattern")
p = ggplot(ref, aes(x = x, y = y))+
scale_y_reverse(lim = c(1, 0))+
theme_classic()+
geom_ribbon_pattern(aes(x = x, ymin = 1, ymax = y),
color = "darkblue",
fill = NA,
size = 1.5,
pattern = 'image',
pattern_type = 'squish',
pattern_filename = coral) +
geom_ribbon(aes(x = x, ymin = 0, ymax = y), fill = "lightblue")
When I run this code, all I get is this:
Plot with missing image fill
But with this code, I think the white area under the curve should be filled in with the coral image. Does anyone know what I'm doing wrong here? I have searched StackOverflow and GitHub and cannot find an answer.
I think the problem here is that there is no "coral.jpg" file in the img folder of ggpattern.
When i edit your code with one of the images present in the folder, it works fine.
x = seq(-1.5, 3.5, 0.1)
y = c( rep(1.0, 22), rep(0.2, 12), rep(0.7, 7), rep(1,10))
ref = data.frame(x = x, y = y)
library(dplyr)
library(ggplot2)
library(ggpattern)
coral = system.file("img", "magpie.jpg", package="ggpattern")
p = ggplot(ref, aes(x = x, y = y))+
scale_y_reverse(lim = c(1, 0))+
theme_classic()+
geom_ribbon_pattern(aes(x = x, ymin = 1, ymax = y),
color = "darkblue",
fill = NA,
size = 1.5,
pattern = 'image',
pattern_type = 'squish',
pattern_filename = coral) +
geom_ribbon(aes(x = x, ymin = 0, ymax = y), fill = "lightblue")
p
I mean, I'd want to paint only the square area P1 X (Q1-Q2).
Not the trapezoid (P2+P1) X (Q1-Q2/2).
Here's code that I used. I used ggplot and dplyr. How can I solve this problem?
How can I paint the only square area not the trapezoied area!!!!
library(ggplot2)
library(dplyr)
supply <- Hmisc::bezier(x = c(1, 8, 9),
y = c(1, 5, 9)) %>%
as_data_frame()
demand <- Hmisc::bezier(c(1, 3, 9),
c(9, 3, 1)) %>%
as_data_frame()
fun_supply <- approxfun(supply$x, supply$y, rule = 2)
fun_supply(c(2, 6, 8))
fun_demand <- approxfun(demand$x, demand$y, rule = 2)
intersection_funs <- uniroot(function(x) fun_supply(x) - fun_demand(x), c(1, 9))
intersection_funs
y_root <- fun_demand(intersection_funs$root)
curve_intersect <- function(curve1, curve2) {
# Approximate the functional form of both curves
curve1_f <- approxfun(curve1$x, curve1$y, rule = 2)
curve2_f <- approxfun(curve2$x, curve2$y, rule = 2)
# Calculate the intersection of curve 1 and curve 2 along the x-axis
point_x <- uniroot(function(x) curve1_f(x) - curve2_f(x),
c(min(curve1$x), max(curve1$x)))$root
# Find where point_x is in curve 2
point_y <- curve2_f(point_x)
# Finish
return(list(x = point_x, y = point_y))
}
intersection_xy <- curve_intersect(supply, demand)
intersection_xy
intersection_xy_df <- intersection_xy %>% as_data_frame()
demand2 <- Hmisc::bezier(c(1.5, 3.5, 9.5),
c(9.5, 3.5, 1.5)) %>%
as_data_frame()
supply2 <- Hmisc::bezier(c(1,7,8),
c(3,7,11)) %>%
as_data_frame()
#Make a data frame of the intersections of the supply curve and both demand curves
intersections <- bind_rows(curve_intersect(supply, demand),
curve_intersect(supply2, demand2))
plot_labels <- data_frame(label = c("S", "D","S[1]","D[1]"),
x = c(9, 1, 6.5, 3),
y = c(8, 8, 8, 8))
ggplot(mapping = aes(x = x, y = y)) +
geom_path(data = supply, color = "#0073D9", size = 1, linetype = "dashed") +
geom_path(data = demand, color = "#FF4036", size = 1, linetype = "dashed") +
geom_path(data = demand2, color = "#FF4036", size = 1) +
geom_path(data = supply2, color = "#0073D9", size = 1) +
geom_segment(data = intersections,
aes(x = x, y = 0, xend = x, yend = y), lty = "dotted") +
geom_segment(data = intersections,
aes(x = 0, y = y, xend = x, yend = y), lty = "dotted") +
geom_segment(data = intersections,
aes(x = x, y = y, xend = x, yend= y), lty = "dotted") +
geom_point(data = intersections, size = 3) +
geom_text(data = plot_labels,
aes(x = x, y = y, label = label), parse = TRUE) +
scale_x_continuous(expand = c(0, 0), breaks = intersections$x,
labels = expression(Q[1], Q[2])) +
scale_y_continuous(expand = c(0, 0), breaks = intersections$y,
labels = expression(P[1], P[2]))+
labs(x = "Quantity", y = "Price") +
geom_area(data =intersections, fill="#9999FF", alpha=0.5) +
theme_classic() +
coord_equal()
Could you help me to paint the area that I mentioned.
You might try adding geom_rect(data=intersections[1,], aes(xmin=0, xmax=x, ymin=0, ymax=y),fill='green', alpha=0.5) to your plot call.
So we have:
ggplot(mapping = aes(x = x, y = y)) +
geom_path(data = supply, color = "#0073D9", size = 1, linetype = "dashed") +
geom_path(data = demand, color = "#FF4036", size = 1, linetype = "dashed") +
geom_path(data = demand2, color = "#FF4036", size = 1) +
geom_path(data = supply2, color = "#0073D9", size = 1) +
geom_segment(data = intersections,
aes(x = x, y = 0, xend = x, yend = y), lty = "dotted") +
geom_segment(data = intersections,
aes(x = 0, y = y, xend = x, yend = y), lty = "dotted") +
geom_segment(data = intersections,
aes(x = x, y = y, xend = x, yend= y), lty = "dotted") +
geom_point(data = intersections, size = 3) +
geom_text(data = plot_labels,
aes(x = x, y = y, label = label), parse = TRUE) +
scale_x_continuous(expand = c(0, 0), breaks = intersections$x,
labels = expression(Q[1], Q[2])) +
scale_y_continuous(expand = c(0, 0), breaks = intersections$y,
labels = expression(P[1], P[2]))+
labs(x = "Quantity", y = "Price") +
geom_area(data =intersections, fill="#9999FF", alpha=0.5) +
theme_classic() +
coord_equal()+
geom_rect(data=intersections[1,], aes(xmin=0, xmax=x, ymin=0, ymax=y),fill='green', alpha=0.5)
Edit based on comment:
geom_rect(data=intersections, aes(xmin=x[2], xmax=x[1], ymin=0, ymax=y[1]),fill='green', alpha=0.5)
Though the answer from J Con is in depth and does provide a solution, a cleaner approach in ggplot2 may be to use the annotate function, with geom and other arguments set appropriately. (See link for help page.)
This is because using something like geom_rect involves passing positions and so on as a data.frame, which is a bit more of a hack as, conceptually, from a grammar of graphics perspective, the data layer and the annotation layer are distinct: the act of mapping data variables to graphical aesthetics in a systematic and objective way, and of marking up features within the dataset in a piecemeal and subjective way, are separate activities, and using annotate explicitly for the latter purpose makes this divide clearer in terms of the code and concepts.
Edit
To be more specific, the annotate equivalent of the following:
geom_rect(data=intersections, aes(xmin=x[2], xmax=x[1], ymin=0, ymax=y[1]),fill='green', alpha=0.5)
Would likely be as follows
annotate(
geom = "rect",
xmin = intersections$x[2], x = intersections$x[1],
ymin = 0, ymax = intersections$y[1],
fill = 'green', alpha = 0.5
)
Functionally this is exactly the same, but conceptually it makes the separation between the data layer and the annotation layer much clearer in the code expressed.
Note: Annotate could also be used for the points and text.
Given a data frame, Data of the form
x y
1 250 1.00000000
2 345 0.03567766
3 290 0.16654457
4 260 0.58363858
5 270 0.38754579
6 280 0.24713065
7 290 0.17142857
8 300 0.11709402
9 310 0.09047619
10 320 0.06439560
11 330 0.05098901
I am able to derive and plot a fit for the data with
library(ggplot2)
Data$x2<-Data$x^2
quadratic.model <- lm(log(Data$y) ~ Data$x + Data$x2)
fun_quad <- function(x){return(exp(
quadratic.model$coef[[3]] * x ^ 2 +
quadratic.model$coef[[2]] * x +
quadratic.model$coef[[1]]
))}
chartObj <- ggplot() +
stat_function(
fun = fun_quad,
aes(color = factor(0)),
size = 1.3,
linetype = "dotdash"
)+
geom_point(data = Data,
aes(x = x, y = y, fill = factor(0)),
color = "black", shape = 22, stroke = 0.7, size = 2.2) +
coord_trans(y = 'log10',
limx = c(250,350), limy = c(.025,1))+
theme_bw() +
guides(fill=F,color=F,linetype=F)
chartObj
which renders
.
I also tried plotting the CI using confint and geom_ribbon.
ribbon.ymin <- function(x){return(exp(
confint(quadratic.model)[[3]]*x^2 +
confint(quadratic.model)[[2]]*x +
confint(quadratic.model)[[1]]
))}
ribbon.ymax <- function(x){return(exp(
confint(quadratic.model)[[6]]*x^2 +
confint(quadratic.model)[[5]]*x +
confint(quadratic.model)[[4]]
))}
ribbonData <- as.data.frame(cbind(x = seq(250,350,.01)))
attach(ribbonData)
ribbonData$ymin <- ribbon.ymin(x)
ribbonData$ymax <- ribbon.ymax(x)
ribbonData$y <- fun_quad(x)
detach(ribbonData)
head(ribbonData)
chartObj <- chartObj +
geom_ribbon( data = ribbonData,
aes(x = x, y = 0:0,
ymin = ymin, ymax = ymax,
color = factor(0),fill = factor(0)),
alpha = 0.3)
however, this renders as below, which again feels obviously incorrect.
So, how do I plot the confidence interval associated with the function described by quadratic.model?
Update
I think that I've found nearly what I am looking for with the use of the predict command, specifically, shown below, however this is still leaves a bit to be desired, particularly the unevenness of the edges of the produced ribbon.
Data$x2<-Data$x^2
quadratic.model <- lm(log(Data$y) ~ Data$x + Data$x2)
fun_quad <- function(x){return(exp(
quadratic.model$coef[[3]] * x ^ 2 +
quadratic.model$coef[[2]] * x +
quadratic.model$coef[[1]]
))}
ribbonData<-predict(quadratic.model,data.frame(x=Data$x),interval="predict",level=.95)
# "predict" used over "confidence" in this example to show the rough edges better.
ribbonData<-as.data.frame(cbind(x=Data$x,fit=ribbonData[,1],lower=ribbonData[,2],upper=ribbonData[,3]))
ribbonData[,2:4]<-exp(ribbonData[,2:4])
chartObj <- ggplot() +
geom_ribbon( data = ribbonData,
aes(x = x, y = fit,
ymin = lower, ymax = upper,
color = factor(0),fill = factor(0)),
alpha = 0.3) +
stat_function(
fun = fun_quad,
aes(color = factor(0)),
size = 1.3,
linetype = "dotdash"
)+
geom_point(data = Data,
aes(x = x, y = y, fill = factor(0)),
color = "black", shape = 22, stroke = 0.7, size = 2.2) +
coord_trans(y = 'log10',
limx = c(250,350), limy = c(.025,1))+
theme_bw() +
guides(fill=F,color=F,linetype=F)
Is there a better way to represent the information presented by the plot above? To smooth out the rough edges of the ribbon?
It might "feel obviously incorrect", but it plots what it's been asked. The whole interval cannot be seen because limx and limy have been set:
ribbon <- function(x, level = 0.95) {
data.frame(
x,
ymin = exp(
confint(quadratic.model, level = level)[[3]] * x ^ 2 +
confint(quadratic.model, level = level)[[2]] * x +
confint(quadratic.model, level = level)[[1]]
),
ymax = exp(
confint(quadratic.model, level = level)[[6]]*x^2 +
confint(quadratic.model, level = level)[[5]]*x +
confint(quadratic.model, level = level)[[4]]
)
)
}
chartObj +
coord_trans(y = 'log10') +
geom_ribbon(data = ribbon(seq(250, 350, .01), level = 0.95),
aes(x = x, ymin = ymin, ymax = ymax,
color = factor(0), fill = factor(0)),
alpha = 0.3)
(NB: My answer is strictly about programming with ggplot2 and says nothing about the statistical validity of exponentiating a confidence interval).
Edit in response to OP's updated question (smooth out edges of ribbon).
predict() over more points:
quadratic.model <- lm(log(y) ~ x + x2, data = Data)
ribbonData <- data.frame(x = seq(250, 350, 0.01), x2 = seq(250, 350, 0.01) ^ 2)
ribbonData <- cbind(
ribbonData,
predict(quadratic.model, ribbonData,
interval = "prediction", level = 0.95)
)
# "predict" used over "confidence" in this example to show the rough edges better.
ribbonData[, 3:5] <- exp(ribbonData[, 3:5])
ggplot() +
geom_ribbon( data = ribbonData,
aes(x = x, y = fit,
ymin = lwr, ymax = upr,
color = factor(0),fill = factor(0)),
alpha = 0.3) +
stat_function(
fun = fun_quad,
aes(color = factor(0)),
size = 1.3,
linetype = "dotdash"
) +
geom_point(data = Data,
aes(x = x, y = y, fill = factor(0)),
color = "black", shape = 22, stroke = 0.7, size = 2.2) +
coord_trans(y = 'log10',
limx = c(250, 350), limy = c(.025, 1)) +
theme_bw() +
guides(fill = F, color = F, linetype = F)
I have the following data structure:
y <- rep(1:10, 2)
group <- rep(c('a', 'b'), each = 10)
dens <- c(c(seq(from = 0, to = 0.8, by = 0.1), 0),
c(seq(from = -0, to = -0.8, by = -0.1), 0))
my_dat <- data.frame(group, dens, y, stringsAsFactors = FALSE )
These are calculated density disributions, in order to make a grouped violin plot, such as in
Split violin plot with ggplot2
# Plot 1:
require(ggplot2)
ggplot(my_dat, aes(x = dens, y = y, fill = group)) +
geom_polygon(color = 'black', show.legend = FALSE)
Now this is simplified, because my data contains hundreds of rows for a smooth outline. (However, there is the central vertical line in my case.) I would now like to remove exactly this vertical central line.
(I guess the problem is removing any specified part of the polygon.)
An idea in my example was to overplot this with a vertical line:
#Plot 2
ggplot(my_dat, aes(x = dens, y = y, fill = group)) +
geom_polygon(color = 'black', show.legend = FALSE) +
geom_segment(x = 0,
xend = 0,
y = min(y) + 0.2,
yend = max(y) - 0.2,
color = '#00BFC4')
But to get the end of the over plotting segment line correct is tricky. (I have purposefully left the line a bit too short for demonstration)
edit
the groups are not distributed in a symmetrical fashion, although my example strongly suggests so.
You can always just plot another polygon on top
x <- with(my_dat, chull(dens, y))
my_dat2 <- my_dat[c(x, x[1L]), ]
ggplot(my_dat, aes(x = dens, y = y, fill = group)) +
geom_polygon(show.legend = FALSE) +
geom_polygon(data = my_dat2, aes(group = 1), size = 1,
fill = 'transparent',
# fill = NA, ## or this
color = 'black')
I think the simpler solution is to first draw all the outlines and then all the filled areas. This should work for any arbitrary polygon shapes.
y <- rep(1:10, 2)
group <- rep(c('a', 'b'), each = 10)
dens <- c(c(seq(from = 0, to = 0.8, by = 0.1), 0),
c(seq(from = -0, to = -0.8, by = -0.1), 0))
my_dat <- data.frame(group, dens, y, stringsAsFactors = FALSE )
require(ggplot2)
ggplot(my_dat, aes(x = dens, y = y)) +
geom_polygon(color = 'black', fill = NA, size = 2) +
geom_polygon(aes(fill = group), color = NA)
I want to mark some part of a plot by filling the complete area from some x1 to x2 with (transparent) color in ggplot2.
With base R I would do something like:
plot(1:100)
polygon(x = c(0, 25, 25, 0), y = c(-1000, -1000, 1000, 1000), col = "#FF000050")
When doing the same with ggplot2 I'm stuck with the problem that the polygon either does not go to the upper and lower edge of the plot or isn't plotted at all if I limit the y-axis with ylim.
ggplot(data = data.frame(x = 1:100, y = 1:100), aes(x = x, y = y)) +
geom_point() +
#ylim(0, 100) +
geom_polygon(data = data.frame(x = c(0, 25, 25, 0), y = c(-1000, -1000, 1000, 1000)), aes(x = x, y = y), color = "red", fill = "red", alpha = 0.1)
I don't want to limit the solution to geom_polygon, maybe there is a better way to mark this part of the plot. In my real world data plot, I am using geom_bar for a stacked barplot, but I don't think the solution depends on that.
You can use -Inf and +Inf to define the limits of the polygon (or better in this case, a rect).
ggplot2 will ignore them for building the plot limits:
ggplot() +
geom_point(data = data.frame(x = 1:100, y = 1:100), aes(x = x, y = y)) +
geom_polygon(data = data.frame(x = c(0, 25, 25, 0), y = c(-Inf, -Inf, Inf, Inf)), aes(x = x, y = y), color = "red", fill = "red", alpha = 0.1) +
geom_rect(aes(xmin = 30, xmax = 35, ymin = -Inf, ymax = Inf), color = 'green', fill = "green", alpha = .1)
Note that I moved the data assignment from the ggplot call to the geom_point. The motive for this is better explained in this question.
Try this:
ggplot(data = data.frame(x = 1:100, y = 1:100), aes(x = x, y = y)) +
geom_rect(aes(xmin = 0, xmax = 25, ymin = 0, ymax = 100), fill = "red", alpha = 0.01)+
geom_point()+
scale_y_continuous(limits = c(0, 100), expand = c(0, 0))