R: Erasing Lines on ggplot2 - drawing half rectangles - r
I am working with the R programming language.
I generated some random data and plotted the following graph:
library(ggplot2)
var_1 = rnorm(1000,20,20)
var_2 = rnorm(1000,20,20)
my_data = data.frame(var_1, var_2)
ggplot(data=my_data, aes(x=var_1, y=var_2)) + geom_point()
I now want to make the following pattern on this graph:
I tried to do this with the following code, but this is the closest I could get:
a1 = ggplot(data=my_data, aes(x=var_1, y=var_2)) +
geom_point()+
geom_hline(yintercept=0, color = "red") +
geom_vline(xintercept=0, color = "red")
a2 = a1 + geom_point() +
geom_hline(yintercept=50, color = "red") +
geom_vline(xintercept=50, color = "red")
My Question: Could someone please show me a "direct" way to make this pattern on this graph? Is there some way to "erase" these extra lines?
Suppose I had a data frame ("my_points") that looked like this:
my_points = data.frame(var_1_point_1 = 0, var_1_point_2 = 50, var_2_point_1 = 0, var_2_point_2 = 50)
Could the graph be made using "my_data", and the lines be made by "referencing" the points in "my_points"? For example, something like this:
a1 = ggplot(data=my_data, aes(x=var_1, y=var_2)) +
geom_point()+
geom_hline(yintercept my_points[1,2], color = "red") +
geom_vline(xintercept=my_points[1,1], color = "red")
Thanks!
You'll need to use geom_segment instead of geom_hline or geom_vline since you'll need to specify xend and yend for each segment.
library(tidyverse)
var_1 = rnorm(1000,20,20)
var_2 = rnorm(1000,20,20)
my_data = data.frame(var_1, var_2)
ggplot(data=my_data, aes(x=var_1, y=var_2)) +
geom_point() +
geom_segment(aes(x = -50, xend = 0, y = 0, yend = 0), color = "red") +
geom_segment(aes(x = 0, xend = 0, y = 0, yend = -50), color = "red") +
geom_segment(aes(x = -50, xend = 50, y = 50, yend = 50), color = "red") +
geom_segment(aes(x = 50, xend = 50, y = 50, yend = -50), color = "red")
Created on 2022-03-21 by the reprex package (v2.0.1)
Update
Since OP wish to use an extra dataframe, here is the updated code:
library(ggplot2)
var_1 = rnorm(1000,20,20)
var_2 = rnorm(1000,20,20)
my_data = data.frame(var_1, var_2)
mypoint <- data.frame(var_1_point_1 = c(-50, 0),
var_1_point_2 = c(0, -50),
var_2_point_1 = c(-50, 50),
var_2_point_2 = c(50, -50))
ggplot(data=my_data, aes(x=var_1, y=var_2)) +
geom_point() +
geom_step(data = mypoint, aes(var_1_point_1, var_1_point_2), color = "red") +
geom_step(data = mypoint, aes(var_2_point_1, var_2_point_2), color = "red")
Created on 2022-03-22 by the reprex package (v2.0.1)
People tend to forget about geom_linerange - in vanilla ggplot sadly only available for vertical lines. But there is also ggstance::geom_linerangeh. Those would allow you to pass a data frame with your x/y coordinates and you will "only" need two geom layers for vertical and for horizontal lines (if your lines always start at the edge of the plot). You could apply the same idea with geom_segment of course, then without need of another package.
library(ggplot2)
library(ggstance)
## changed data frame my points:
x <- y <- c(0, 50)
my_points <- data.frame(x, y)
ggplot() +
geom_point(aes(x = var_1, y = var_2), data = my_data) +
geom_linerange(aes(x = x, ymin = -Inf, ymax = y), data = my_points, color = "red", size = 2) +
geom_linerangeh(aes(xmin = -Inf, xmax = x, y = y), data = my_points, color = "red", size = 2)
Another option would be to make your own stat and use this with geom_segment:
library(ggplot2)
var_1 <- rnorm(1000, 20, 20)
var_2 <- rnorm(1000, 20, 20)
my_data <- data.frame(var_1, var_2)
## changed data frame my points:
x <- y <- c(0, 50)
my_points <- data.frame(x, y)
StatEdge <- ggproto("StatEdge", Stat, compute_group = function(data, scales) {
x <- c(rep(-Inf, nrow(data)), data$x)
y <- c(data$y, rep(-Inf, nrow(data)))
xend <- rep(data$x, nrow(data))
yend <- rep(data$y, nrow(data))
data.frame(x, xend, y, yend)
})
## now you can use the new stat for your geom
ggplot() +
geom_point(aes(x = var_1, y = var_2), data = my_data) +
geom_segment(data = my_points, aes(x, y), stat = "edge", size = 2, color = "red")
Created on 2022-03-21 by the reprex package (v2.0.1)
Related
Add label to geom_vline within a ggplot2 figure
so far I can manage to build the following geom_density figure using ggpot2: cuts1 <- data.frame(Ref="p", vals=c(140)) cuts2 <- data.frame(Ref="s", vals=c(300)) cuts3 <- data.frame(Ref="m", vals=c(250)) cuts <- rbind(cuts1, cuts2, cuts3) ggplot(mtcars, aes(x=disp)) + geom_density(color = "black", fill = 4, alpha = 1) + geom_vline(data = cuts , aes(xintercept=vals, color= Ref) ) And I wondered if someone knew a way to plot the geom_vline much more like that : Where the lines do not reach the top and bottom of the figure and where the labels are all displayed with a rotation.
Here is one potential solution: library(ggplot2) cuts1 <- data.frame(Ref="p", vals=c(140)) cuts2 <- data.frame(Ref="s", vals=c(300)) cuts3 <- data.frame(Ref="m", vals=c(250)) cuts <- rbind(cuts1, cuts2, cuts3) ggplot(mtcars, aes(x=disp)) + geom_density(color = "black", fill = 4, alpha = 1) + geom_segment(data = cuts, aes(x=vals, xend = vals, y = 0, yend = max(density(mtcars$disp)[[2]]), color= Ref), key_glyph = "vpath") + geom_text(data = cuts, aes(x = vals, y = max(density(mtcars$disp)[[2]]) * 1.02, label = Ref), nudge_x = 5, angle = 45) Created on 2022-08-29 by the reprex package (v2.0.1)
Take a look at geom_segment, you can set the yend parameter to where you want your lines to end.
R: Drawing Arbitrary Partitions on a Graph
I am working with the R programming language. In a previous question (R: Erasing Lines on ggplot2 - drawing half rectangles), I learned how to make the following graph: library(tidyverse) var_1 = rnorm(1000,20,20) var_2 = rnorm(1000,20,20) my_data = data.frame(var_1, var_2) ggplot(data=my_data, aes(x=var_1, y=var_2)) + geom_point() + geom_segment(aes(x = -50, xend = 0, y = 0, yend = 0), color = "red") + geom_segment(aes(x = 0, xend = 0, y = 0, yend = -50), color = "red") + geom_segment(aes(x = -50, xend = 50, y = 50, yend = 50), color = "red") + geom_segment(aes(x = 50, xend = 50, y = 50, yend = -50), color = "red") I want to change the above code so that "x", "xend", "y", "yend" can come from a data frame: For instance, suppose I have the following data frame: my_points = data.frame(x1 = c(-40, -41) , x2 = c(20, 21) , y1 = c(-30, -31), y2 = c(50, 51) ) > my_points x1 x2 y1 y2 1 -40 20 -30 50 2 -41 21 -31 51 From here, I would like to make two graphs: ggplot(data=my_data, aes(x=var_1, y=var_2)) + geom_point() + geom_segment(aes(x = my_points[1,1], xend = my_points[1,2], y = my_points[1,3], yend = my_points[1,4]), color = "red") + geom_segment(aes(x = my_points[1,2], xend = my_points[1,1], y = my_points[1,4], yend = my_points[1,3]), color = "red") + geom_segment(aes(x = my_points[1,1], xend = my_points[1,2], y = my_points[1,4], yend = my_points[1,3]), color = "red") + geom_segment(aes(x = my_points[1,1], xend = my_points[1,2], y = my_points[1,4], yend = my_points[1,3]), color = "red") + ggtitle("graph 1") ggplot(data=my_data, aes(x=var_1, y=var_2)) + geom_point() + geom_segment(aes(x = my_points[2,1], xend = my_points[2,2], y = my_points[2,3], yend = my_points[2,4]), color = "red") + geom_segment(aes(x = my_points[2,2], xend = my_points[2,1], y = my_points[2,4], yend = my_points[2,3]), color = "red") + geom_segment(aes(x = my_points[2,1], xend = my_points[2,2], y = my_points[2,4], yend = my_points[2,3]), color = "red") + geom_segment(aes(x = my_points[2,1], xend = my_points[2,2], y = my_points[2,4], yend = my_points[2,3]), color = "red") + ggtitle("graph 2") But this is not producing the same result as before: Furthermore, Graph 2 also does not work: Can someone please show me how to fix this? Thanks!
If you will use a data.frame, you cand do everything in a single call of geom_segment. library(tidyverse) var_1 = rnorm(1000,20,20) var_2 = rnorm(1000,20,20) my_data = data.frame(var_1, var_2) my_points = tibble::tribble( ~rowid, ~x1, ~x2, ~y1, ~y2, "h1", -50, 50, 50, 50, "v1", 50, 50, -50, 50, "h2", -50, 0, 0, 0, "v2", 0, 0, -50, 0 ) ggplot(data = my_data, aes(x = var_1, y = var_2)) + geom_point() + geom_segment(data = my_points, aes(x = x1, xend = x2, y = y1, yend = y2), color = 'red') You can use this structure as well, but now you need two geom_segment() calls my_points2 = tibble::tribble( ~rowid, ~x1, ~y1, ~x2, ~y2, "Segment 1", -50, 50, 50, -50, "Segment 2", -50, 0, 0, -50, ) ggplot(data = my_data, aes(x = var_1, y = var_2)) + geom_point() + # Horizontal lines geom_segment(data = my_points2, aes(x = x1, y = y1, xend = x2, yend = y1), color = 'red') + # Vertical lines geom_segment(data = my_points2, aes(x = x2, y = y2, xend = x2, yend = y1), color = 'red')
OP requested to expand on a previous question related to drawing "half-rectangles" on a plot. The previous question dealt with actually drawing the rectangles, and in this question, OP would like to have the half rectangles drawn based on a separate dataframe called my_points. Additionally, the dataframe my_points contains data in columns x1, x2, y1, and y2, which can be used to draw a pair of half rectangles on the plot. One key feature requested is that each row in the data frame my_points should draw a separate pair of half rectangles. As with the previous answer to the original question, I'll demonstrate using separate calls to geom_segment(). One half rectangle requires two lines, so we'll need 4 calls to geom_segment() to draw a pair of half rectangles. The key here is to understand that each line has one terminus that extends beyond the plot area and will therefore be -Inf at either x (for the horizontal lines) or y (for the vertical lines). The Plot p <- ggplot(data=my_data, aes(x=var_1, y=var_2)) + geom_point(size=0.6) + # vertical lines geom_segment(data=my_points, aes(x=x1, xend=x1, y=-Inf, yend=y1), color='red') + geom_segment(data=my_points, aes(x=x2, xend=x2, y=-Inf, yend=y2), color='red') + # horizontal lines geom_segment(data=my_points, aes(x=-Inf, xend=x1, y=y1, yend=y1), color='red') + geom_segment(data=my_points, aes(x=-Inf, xend=x2, y=y2, yend=y2), color='red') + theme_bw() p Separating out the Rows If OP wants to separate each pair of half rectangles, you can use faceting or apply an aesthetic to associate with rownames(my_points). For convenience, it might make sense to first create a column in my_points for this purpose, but it is not strictly required. my_points$graph <- rownames(my_points) p <- ggplot(data=my_data, aes(x=var_1, y=var_2)) + geom_point(size=0.6) + # vertical lines geom_segment(data=my_points, aes(x=x1, xend=x1, y=-Inf, yend=y1, color=graph)) + geom_segment(data=my_points, aes(x=x2, xend=x2, y=-Inf, yend=y2, color=graph)) + # horizontal lines geom_segment(data=my_points, aes(x=-Inf, xend=x1, y=y1, yend=y1, color=graph)) + geom_segment(data=my_points, aes(x=-Inf, xend=x2, y=y2, yend=y2, color=graph)) + theme_bw() p Extending to more Half Rectangles We can extend this answer to accommodate a larger dataframe of my_points by adding some additional rows to my_points and using faceting as an example to demonstrate the principles above even further. # moar points!!!1!1!!111!1! my_points <- rbind( my_points, c(-20, 40, -20, 40), c(-10, 60, -10, 30) ) # adding the column... or just reference color=rownames(my_points) instead. my_points$graph <- rownames(my_points) p <- ggplot(data=my_data, aes(x=var_1, y=var_2)) + geom_point(size=0.6, alpha=0.3) + # vertical lines geom_segment(data=my_points, aes(x=x1, xend=x1, y=-Inf, yend=y1, color=graph)) + geom_segment(data=my_points, aes(x=x2, xend=x2, y=-Inf, yend=y2, color=graph)) + # horizontal lines geom_segment(data=my_points, aes(x=-Inf, xend=x1, y=y1, yend=y1, color=graph)) + geom_segment(data=my_points, aes(x=-Inf, xend=x2, y=y2, yend=y2, color=graph)) + facet_wrap(~graph) + theme_bw() p The key to remember when working this question is to keep track of how to reference x, xend, y and yend in the aesthetics for geom_segment(). OP was referencing those incorrectly in their attempts posted (resulting in the "X" instead of the half-rectangle).
Plot customised vertical lines up to the curve
I want the vertical lines to not go beyond the curve line after they intersect Example data: x <- 1:50 dat <- data.frame(x = x, y = 1 - exp(-x/43)^4) ggplot(dat, aes(x = x, y = y)) + geom_line() + geom_vline(xintercept = c(10, 20, 30), lty = "dashed")
Use geom_segment instead: ggplot(dat, aes(x = x, y = y)) + geom_line() + geom_segment(aes(x = x, xend = x, y = min(dat$y), yend = y), data = dat[ dat$x %in% c(10, 20, 30), ], lty = "dashed")
How to customize a boxplot legend indicating mean, outliers, median, etc?
I have a boxplot and by my supervisor's advice I have to indicate the mean, outliers and median in the legend, like this image: How can I do this using ggplot2? library(ggplot2) A <- 1:20 DF <- data.frame(A) ggplot(data = DF) + geom_boxplot(aes(x = "", y = A))
There is no straightforward way. But you could make a custom legend using another plot: p <- ggplot(mtcars) + geom_boxplot(aes(x = factor(cyl), y = mpg)) d1 <- data.frame(x = 1, y = c(1:1000, 1502)) d2 <- data.frame( y = c(boxplot.stats(d1$y)$stats, 1502), x = 1, label = c('min', '1st quartile', 'median', '3rd quartile', 'max', 'outlier') ) leg <- ggplot(d1, aes(x, y)) + geom_boxplot(width = 0.2) + geom_text(aes(x = 1.15, label = label), d2, hjust = 0) + xlim(0.9, 1.5) + theme_void() + theme(panel.background = element_rect(fill = 'white', color = 1)) p + annotation_custom(ggplotGrob(leg), xmin = 3, xmax = 3.5, ymin = 25, ymax = 35)
R: plotting pdf in a certain interval
I want to plot a probability function in a certain interval. The x-axes has to be longer than the interval because there are more pdf's in one plot. But with the growing of the x-axes, the pdf generating data too, although the code implied the certain interval. Code: p1 <- ggplot() + stat_function(data=data.frame(x=c(2,30)),aes(x),fun = dnorm, n = 101, args= list(mean=5,sd=1),color="black")+ xlim(-5,80)+ scale_y_continuous(breaks = NULL) the pdf in p1 generate data until x=80. But x values in the code are in a vector until x=30. How could I prevent that the pdf produces values until 80 or how have to be the code that the distribution stops at x=30?
We can construct a dataframe with the only the x-values you want plotted on the fly using dplyr::data_frame. I added another p.d.f. to demonstrate that the way you want it presented will work. library(dplyr) # to use data_frame ggplot() + geom_line(data=data_frame(x=seq(2,30, 0.25), y = dnorm(x, mean = 5, sd = 1)), aes(x, y), color = "black") + geom_line(data=data_frame(x=seq(40,70, 0.25), y = dnorm(x, mean = 60, sd = 5)), aes(x, y), color = "red") + xlim(-5,80)+ scale_y_continuous(breaks = NULL) Update You can also label them like so, by moving the color= inside the aes(...): ggplot() + geom_line(data=data_frame(x=seq(2,30, 0.25), y = dnorm(x, mean = 5, sd = 1)), aes(x, y, color = "mean:5, sd:1")) + geom_line(data=data_frame(x=seq(40,70, 0.25), y = dnorm(x, mean = 60, sd = 5)), aes(x, y, color = "mean:60, sd:5")) + xlim(-5,80)+ scale_y_continuous(breaks = NULL) + scale_color_manual(values = c("mean:5, sd:1" = "black", "mean:60, sd:5" = "red")) Update2 Your density function works for me. dTDF<-function(x,g,a,b,k){ exp(-exp(-(x/a)+((a*k)/(g-x))-b))*(exp(-(x/a)+((a*k)/(g-x))-b))*((1/a)-((a*k)/((g-x)^2))) } df1 <- data_frame(x=seq(2500,11300, 100), y = dTDF(x,g=11263,a=1185, b=-4, k=-0.5)) df2 <- data_frame(x=seq(7000,14300, 100), y = dTDF(x,g=15263,a=1105, b=-10, k=-0.5)) ggplot() + geom_line(data = df1, aes(x, y))+ geom_line(data = df2, aes(x, y)) + xlim(1000, 15000)