Related
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)
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).
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")
I want to have an error bar for my geom_hline and thought that a geom_ribbon with opacity would look best. but i cant figure out how to make it reach the ends of the plot. I want the geom_ribbon to touch the sides of the plot as the geom_hline does. Here is the example code:
library('ggplot2')
x <- c(1,2,3,4,5,6,7,8,9,10)
y <- c(1,2,3,4,5,6,7,8,9,10)
data <- data.frame(x,y)
p1 <- ggplot(data,aes(x = x, y = y)) + geom_line() + geom_hline(yintercept=5)
p1 + geom_ribbon(aes(y = y[5],ymin = y[5]-0.5, ymax = y[5]+0.5, fill = 'red'), alpha = 0.4)
Use annotate with infinite x-values:
ggplot(data, aes(x, y)) +
geom_line() +
geom_hline(yintercept = 5) +
annotate('ribbon', x = c(-Inf, Inf), ymin = 5 - 0.5, ymax = 5 + 0.5,
alpha = 0.4, fill = 'red')
If you need a legend, use geom_ribbon directly, like so:
ggplot(data, aes(x, y)) +
geom_line() +
geom_hline(yintercept = 5) +
geom_ribbon(
aes(x, y = NULL, ymin = ymin, ymax = ymax, fill = 'my_label'),
data.frame(x = c(-Inf, Inf), ymin = 5 - 0.5, ymax = 5 + 0.5),
alpha = 0.4
)
There is no way in geom_hline() to set xlim, instead the horizontal line goes from end of the plot to the other. You can use geom_segment instead, which allows you to control the x-range and y-range of the line by specifying segment's start coordinate (x, y) and end coordinate (xend, yend)
This works:
library('ggplot2')
x <- c(1,2,3,4,5,6,7,8,9,10)
y <- c(1,2,3,4,5,6,7,8,9,10)
data <- data.frame(x,y)
p1 <- ggplot(data, aes(x = x, y = y)) + geom_line() + geom_segment(aes(x = x[1], xend = x[10], y = y[5], yend = y[5]))
p1 + geom_ribbon(aes(y = y[5],ymin = y[5]-0.5, ymax = y[5]+0.5, fill = 'red'), alpha = 0.4)
Couple options:
1) Use geom_hline instead of geom_ribbon like so (probably best option):
p1 + geom_hline(yintercept = y[5], color = 'red', size = 8, alpha = 0.4)
2) Remove area between plot area and axis by adding scale_x_continuous(expand=c(0,0)) like so (credit to https://stackoverflow.com/a/22945857/5727278):
p1 + geom_ribbon(aes(y = y[5],ymin = y[5]-0.5, ymax = y[5]+0.5, fill = 'red'), alpha = 0.4) +
scale_x_continuous(expand=c(0,0))
Is there a way to get the axes, with labels in the center of a ggplot2 plot, like a traditional graphing calculator? I've looked through the docs and there doesn't seem to be that functionality, but other plotting packages are not as graphically customizable as ggplot2. To clarify, I was looking to go from something like this:
To this:
The first plot is made with the following code:
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) + geom_point(size = 5)
p + xlim(-2,2) + ylim(-2,2)
The second plot is made with Mathematica. The main problem I am having is figuring out how to make the axis, with labels, go to the center (I can make the theme blank, etc., no problem). There seems to be no theme parameter you can edit to make this a quick fix.
I think this is what you are looking for:
I have constructed a function that does just that:
theme_geometry <- function(xvals, yvals, xgeo = 0, ygeo = 0,
color = "black", size = 1,
xlab = "x", ylab = "y",
ticks = 10,
textsize = 3,
xlimit = max(abs(xvals),abs(yvals)),
ylimit = max(abs(yvals),abs(xvals)),
epsilon = max(xlimit,ylimit)/50){
#INPUT:
#xvals .- Values of x that will be plotted
#yvals .- Values of y that will be plotted
#xgeo .- x intercept value for y axis
#ygeo .- y intercept value for x axis
#color .- Default color for axis
#size .- Line size for axis
#xlab .- Label for x axis
#ylab .- Label for y axis
#ticks .- Number of ticks to add to plot in each axis
#textsize .- Size of text for ticks
#xlimit .- Limit value for x axis
#ylimit .- Limit value for y axis
#epsilon .- Parameter for small space
#Create axis
xaxis <- data.frame(x_ax = c(-xlimit, xlimit), y_ax = rep(ygeo,2))
yaxis <- data.frame(x_ax = rep(xgeo, 2), y_ax = c(-ylimit, ylimit))
#Add axis
theme.list <-
list(
theme_void(), #Empty the current theme
geom_line(aes(x = x_ax, y = y_ax), color = color, size = size, data = xaxis),
geom_line(aes(x = x_ax, y = y_ax), color = color, size = size, data = yaxis),
annotate("text", x = xlimit + 2*epsilon, y = ygeo, label = xlab, size = 2*textsize),
annotate("text", x = xgeo, y = ylimit + 4*epsilon, label = ylab, size = 2*textsize),
xlim(-xlimit - 7*epsilon, xlimit + 7*epsilon), #Add limits to make it square
ylim(-ylimit - 7*epsilon, ylimit + 7*epsilon) #Add limits to make it square
)
#Add ticks programatically
ticks_x <- round(seq(-xlimit, xlimit, length.out = ticks),2)
ticks_y <- round(seq(-ylimit, ylimit, length.out = ticks),2)
#Add ticks of x axis
nlist <- length(theme.list)
for (k in 1:ticks){
#Create data frame for ticks in x axis
xtick <- data.frame(xt = rep(ticks_x[k], 2),
yt = c(xgeo + epsilon, xgeo - epsilon))
#Create data frame for ticks in y axis
ytick <- data.frame(xt = c(ygeo + epsilon, ygeo - epsilon),
yt = rep(ticks_y[k], 2))
#Add ticks to geom line for x axis
theme.list[[nlist + 4*k-3]] <- geom_line(aes(x = xt, y = yt),
data = xtick, size = size,
color = color)
#Add labels to the x-ticks
theme.list[[nlist + 4*k-2]] <- annotate("text",
x = ticks_x[k],
y = ygeo - 2.5*epsilon,
size = textsize,
label = paste(ticks_x[k]))
#Add ticks to geom line for y axis
theme.list[[nlist + 4*k-1]] <- geom_line(aes(x = xt, y = yt),
data = ytick, size = size,
color = color)
#Add labels to the y-ticks
theme.list[[nlist + 4*k]] <- annotate("text",
x = xgeo - 2.5*epsilon,
y = ticks_y[k],
size = textsize,
label = paste(ticks_y[k]))
}
#Add theme
#theme.list[[3]] <-
return(theme.list)
}
As an example you can run the following code to create an image similar to the one above:
simdata <- data.frame(x = rnorm(50), y = rnorm(50))
ggplot(simdata) +
theme_geometry(simdata$x, simdata$y) +
geom_point(aes(x = x, y = y), size = 3, color = "red") +
ggtitle("More geometric example")
ggsave("Example1.png", width = 10, height = 10)
There are some other useful answers, but the following comes closer to the target visual and avoids looping:
library(ggplot2)
library(magrittr)
# constants
axis_begin <- -2
axis_end <- 2
total_ticks <- 21
# DATA ----
# point to plot
my_point <- data.frame(x=1,y=1)
# chart junk data
tick_frame <-
data.frame(ticks = seq(axis_begin, axis_end, length.out = total_ticks),
zero=0) %>%
subset(ticks != 0)
lab_frame <- data.frame(lab = seq(axis_begin, axis_end),
zero = 0) %>%
subset(lab != 0)
tick_sz <- (tail(lab_frame$lab, 1) - lab_frame$lab[1]) / 128
# PLOT ----
ggplot(my_point, aes(x,y)) +
# CHART JUNK
# y axis line
geom_segment(x = 0, xend = 0,
y = lab_frame$lab[1], yend = tail(lab_frame$lab, 1),
size = 0.5) +
# x axis line
geom_segment(y = 0, yend = 0,
x = lab_frame$lab[1], xend = tail(lab_frame$lab, 1),
size = 0.5) +
# x ticks
geom_segment(data = tick_frame,
aes(x = ticks, xend = ticks,
y = zero, yend = zero + tick_sz)) +
# y ticks
geom_segment(data = tick_frame,
aes(x = zero, xend = zero + tick_sz,
y = ticks, yend = ticks)) +
# labels
geom_text(data=lab_frame, aes(x=lab, y=zero, label=lab),
family = 'Times', vjust=1.5) +
geom_text(data=lab_frame, aes(x=zero, y=lab, label=lab),
family = 'Times', hjust=1.5) +
# THE DATA POINT
geom_point(color='navy', size=5) +
theme_void()
A first approximation:
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) + theme_bw() +
geom_point(size = 5) +
geom_hline(yintercept = 0) +
geom_vline(xintercept = 0)
Adjust limits as per SlowLearner's answer.
I would just use xlim and ylim.
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) +
geom_point(size = 5) +
xlim(-2, 2) +
ylim(-2, 2)
p
Recently, I've added coord_axes_inside() to ggh4x that might be suitable for this problem.
library(ggplot2)
library(ggh4x)
df <- data.frame(x = c(-1, 1))
ggplot(df, aes(x, x)) +
geom_point() +
theme(axis.line = element_line()) +
coord_axes_inside(labels_inside = TRUE)
Aside from being syntactically pretty convenient, it really plots axes, so they respond to theme elements and guide declarations as you'd expect.
last_plot() +
guides(x = guide_axis(angle = 45)) +
theme(axis.ticks.length = unit(-5, "pt"))
Created on 2022-09-01 by the reprex package (v2.0.0)
For centering, I recommend setting the scale limits to centering functions:
last_plot() +
scale_x_continuous(limits = ~ c(-1, 1) * max(abs(.x))) +
scale_y_continuous(limits = ~ c(-1, 1) * max(abs(.x)))
(Disclaimer: I'm the author of ggh4x)