R: Drawing Arbitrary Partitions on a Graph

Viewed 137

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")

enter image description here

I want to change the above code so that "x", "xend", "y", "yend" can come from a data frame:

enter image description here

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:

enter image description here

Furthermore, Graph 2 also does not work:

enter image description here

Can someone please show me how to fix this?

Thanks!

2 Answers

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')

enter image description here

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')

enter image description here

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

enter image description here

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

enter image description here

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

enter image description here

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).

Related