Draw segment below density plot using relative y? - r

I want to draw a segment below a density plot and I would like to have the distance be a constant number of pixels. Is this possible? I Know how to hardcode the distance. For example:
set.seed(40816)
library(ggplot2)
df.plot <- data.frame(x = rnorm(100, 0, 1))
ggplot(df.plot, aes(x = x)) + geom_density() +
geom_segment(aes(x = -1, y = -0.05, xend = 1, yend = -0.05),
linetype = "longdash")
produces :
But
df.plot <- data.frame(x = rnorm(100, 0, 4))
ggplot(df.plot, aes(x = x)) + geom_density() +
geom_segment(aes(x = -1, y = -0.025, xend = 1, yend = -0.025),
linetype = "longdash")
produces a plot with the segment much further away from the density

You could use annotation_grob,
set.seed(40816)
library(ggplot2)
df.plot <- data.frame(x = rnorm(100, 0, 1))
strainerGrob <- function(pos=unit(2,"mm"), gp=gpar(lty=2, lwd=2))
segmentsGrob(0, unit(1,"npc") - pos, 1, unit(1,"npc") - pos, gp=gp)
ggplot(df.plot, aes(x = x)) + geom_density() +
annotation_custom(strainerGrob(), xmin = -1, xmax = 1, ymin=-Inf, ymax=0) +
expand_limits(y=-0.1)

Related

how to make vertical and horizonal line up to the axis boudary in ggplot

I want to use a vertical and horizonal line to mark a point in a facetd plot.
But the vertical and horizonal line can not touch the axis boudary.
I used a simple example to show the problem.
library(tidyverse)
library(ggh4x)
# data prepared
df = data.frame(
x = c(1, 2, 3, 4, 10, 20, 30 ,40),
y = c(1, 2, 3, -1, 10 ,20, 30, -10),
group = c(1, 1, 1, 1, 2, 2, 2, 2)
)
add.point = data.frame(
group = c(1, 2),
x = c(1, 10),
y = c(1.5, 15),
x_hline = c(0, 0),
y_hline = c(1.5, 15),
x_vline = c(1, 10),
y_vline = c(0, 0)
)
I tried several ways, such as expand_limits(), try scale_x(y)_continuous() and coord_cartesian(), but all failed.
By the way, I can live with the point y < 0 not showing up in the plot.
My question is that is there a way that make vertical and horizonal line touch the axis boudary in ggplot.
(p0 = df %>%
ggplot(aes(x = x, y = y)) +
geom_point() +
geom_smooth(se = F) +
facet_wrap(vars(group), scales = 'free')+
geom_point(data = add.point, aes(x = x, y = y), color = 'red')+
geom_segment(data = add.point,
aes(x = x_hline, y = y_hline,
xend = x, yend = y), linetype = 'dashed')+
geom_segment(data = add.point,
aes(x = x_vline, y = y_vline,
xend = x, yend = y), linetype = 'dashed'))
# try expand_limits()
(p1 = p0 + expand_limits(x = 0, y = 0))
# try scale_x(y)_continuous()
(p1 = p0 + scale_x_continuous(expand = c(0, 0)) + scale_y_continuous(expand = c(0, 0)))
# try coord_cartesian()
(p1 = p0 + coord_cartesian(ylim = 0))
You could use e.g. -Inf for the endpoints of your segments and use expand_limits to extend the limits to zero:
library(ggplot2)
ggplot(df, aes(x = x, y = y)) +
geom_point() +
geom_smooth(se = F) +
facet_wrap(vars(group), scales = 'free')+
geom_point(data = add.point, aes(x = x, y = y), color = 'red')+
geom_segment(data = add.point,
aes(x = -Inf, y = y_hline,
xend = x, yend = y), linetype = 'dashed')+
geom_segment(data = add.point,
aes(x = x_vline, y = -Inf,
xend = x, yend = y), linetype = 'dashed') +
expand_limits(x = 0, y = 0)

Adding horizontal lines in the plot of a step function

I attach graphic, image.jpg, in which I want to draw the line y=0 for x<3 and the line y=1 for x>=8, i.e. the result would be image2.jpg.
These are the instructions for image.jpg.
df <- data.frame(x=Asignaturas, y=solF)
df$xend <- c(df$x[2:nrow(df)],NA)
df$yend <- df$y
p <- (ggplot(df, aes(x=x, y=y, xend=xend, yend=yend)) +
geom_vline(aes(xintercept=x), linetype=2,color="grey") +
geom_point() + # Solid points to left
geom_point(aes(x=xend, y=y), shape=1) + # Open points to right
geom_segment() + # Horizontal line
geom_text(aes(label = paste0(solF,''),vjust = -0.5), color = "black") +
ylab("Función de distribucción") +
xlab("Asignaturas"))
p
Does anyone know how to do it?
Thanks
An option using base R.
plot(df$x, df$y, xlim=c(2.5, max(df$x) + .5), ylim=c(0, 1.075), pch=19)
points(df$x + 1, df$y, pch=1)
segments(df$x, df$y, df$x + 1)
text(df$x, df$y + .05, df$y)
sapply(0:1, \(x) lines(2:3 + x*6, rep(x, 2), col='red'))
Or just
plot(df$x, df$y, type='s', xlim=c(2.5, max(df$x) + .5), ylim=c(0, 1.075))
text(df$x, df$y + .05, df$y)
sapply(0:1, \(x) lines(2:3 + x*6, rep(x, 2), col='red'))
Data:
df <- structure(list(y = c(0, 0.14, 0.31, 0.48, 0.67, 0.82, 1), x = c(2,
3, 4, 5, 6, 7, 8)), class = "data.frame", row.names = c(NA, -7L
))
If you want to stick with ggplot2, the geom_segment() will give what you're looking for. Make a data.frame of the parameters and then add the geom_segment().
extraLines <- data.frame(x = c(-Inf, max(df$x)), xend = c(min(df$x), Inf), y = c(2, max(df$y)), yend = c(2, max(df$y)))
p +
geom_segment(data = extraLines, aes(x = x, xend = xend, y = y, yend = yend), colour = "red") +
geom_point(data = filter(extraLines, x > 0), aes(x = x, y=y), colour = "red") +
geom_point(data = filter(extraLines, x < max(df$x)), aes(x=xend, y=y), shape = 1, colour = "red")

How to draw a vector in Cartesian coordinate system by ggplot2?

I want to draw a vector with point P(2,4) and point Q(-4,-6). I can easily write this in base plot system.
arrows(0, 0, 2, 4, angle=20) # command to draw vector as arrows.
arrows(0, 0, -4, -6, angle=20) # with angle of the arrow
However, when I want to transform this to ggplot2, I don't know how to continue.
x1 <- c(0,2,-4)
y1 <- c(0,4,-6)
df <- data.frame(x1,y1)
ggplot(df) +
geom_point(aes(x = x1, y = y1))
Here is an attempt:
library(tidyverse)
x=c(-6:6)
y=c(-6:6)
df %>%
ggplot(aes(x,y))+geom_abline(aes(slope=0,intercept=0))+
annotate("text",x=2,y=4,label="P",size=9,colour="red")+
annotate("text",x=-4,y=-6,label="Q",colour="red",size=9)+
geom_vline(xintercept = 0)+
geom_point(aes(x=0,y=0))+
geom_segment(aes(x=0,y=0,xend=2,yend=4),
arrow=arrow(length = unit(0.5,"cm"),angle=20),lineend = "butt")+
geom_segment(aes(x=0,y=0,xend=-4,yend=-6),
arrow=arrow(length = unit(0.5,"cm"),angle=20),lineend = "butt",linejoin = "round")+
theme_minimal()
Plot:
You'll want to use geom_segment instead of geom_point:
library(ggplot2)
ggplot() +
geom_segment(aes(x = 0, y = 0, xend = 2, yend = 4)) +
geom_segment(aes(x = 0, y = 0, xend = -4, yend = -6))

Lines on top and bottom of ggplot plot area

I want to add a line on the top and bottom of my plots (bottom line below the x label and axis) created using ggplot2. So far I have added a rectangle around the plot, but I do not want the lines on the sides.
x <- 1:10
y <- rnorm(10,mean = x)
df <- data.frame(x,y)
library(ggplot2)
ggplot(data = df, mapping = aes(x,y)) + geom_point() +
theme(plot.background = element_rect(size = 1, color = 'blue'))
I hope you guys have a solution.
Will something similar to this work?
x <- 1:10
y <- rnorm(10,mean = x)
df <- data.frame(x,y)
ggplot(data = df, mapping = aes(x,y)) + geom_point() +
annotate(geom = 'segment',
y = Inf,
yend = Inf,
x = -Inf,
xend = Inf,
size = 2) +
theme(axis.line.x = element_line(size = 1))
Not a perfect, but working solution. You have to plot huge "-" (size = 1000) outside plot area. This solution is not perfect as you have to manually adjust position of "-" on the y-axis.
df <- data.frame(x = 1:10, y = 1:10)
library(ggplot2)
ggplot(df, aes(x, y)) +
geom_point() +
# Y position adjusted manually
geom_text(aes(5, 2.9, label = "-"), color = "blue", size = 1000) +
# Y position adjusted manually
geom_text(aes(5, 21.2, label = "-"), color = "blue", size = 1000) +
# Plot outside plot area
coord_cartesian(ylim = c(0, 10), clip = "off")
I am not completely happy with the solution as I don't fully grasp
how to change the size of the lines
why they are not perfectly aligned with top and bottom when using patchwork::wrap_plots()
why it does not show the top line using ggpubr::ggarrange() or cowplot::plot_grid()
but based on this code, I suggest the following solution:
library(ggplot2)
df <- data.frame(x = 1:5, y = 1:5)
p <- ggplot(data = df) + aes(x, y) + geom_point()
top_line <- grid::grobTree(grid::linesGrob(x = grid::unit(c(0, 1), "npc"), y = grid::unit(1, "npc")))
bot_line <- grid::grobTree(grid::linesGrob(x = grid::unit(c(0, 1), "npc"), y = grid::unit(0, "npc")))
patchwork::wrap_plots(top_line, p, bot_line,
ncol = 1, nrow = 3,
heights = c(0, 1, 0))
ggpubr::ggarrange(top_line, p, bot_line,
ncol = 1, nrow = 3,
heights = c(0, 1, 0))
cowplot::plot_grid(top_line, p, bot_line,
ncol = 1, nrow = 3,
rel_heights = c(0, 1, 0))
Created on 2022-08-25 with reprex v2.0.2

I'd like to paint an area but i don't know how to

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.

Resources