How to randomly scatter points inside a circle with ggplot, without clustering around the center?
Asked Answered
E

2

6

I want to use ggplot to draw a circle, then scatter points inside it. I have code (adopted from this answer) that gets me pretty close to what I want. However, I want the points to scatter inside the circle randomly, but right now I get an undesired cluster around the center.

I saw a similar SO question and answer, but it's in c# and I don't understand how to adapt it to R code.

My code so far

The following code defines a custom visualization function vis_points_inside_circle(), and then calls it 4 times to give 4 examples of visualizing using my current method.

library(ggplot2)
library(ggforce)

## set up function
vis_points_inside_circle <- function(n) {
  
  # part 1 -- set up empty circle
  df_empty_circle <-
    data.frame(x = 0,
               y = 0, 
               r = 1)
  
  p_empty_circle <-
    ggplot(df_empty_circle) +
    geom_circle(aes(x0 = x, y0 = y, r = r)) + 
    coord_fixed() +
    theme_void()
  
  # part 2 -- set up points scatter
  r <- runif(n)
  th <- runif(n)
  
  df_circular_points_scatter <- 
    data.frame(x = r*cos(2*pi*th),  ## from @Ben's answer: https://mcmap.net/q/1773533/-in-ggplot-how-to-draw-a-circle-disk-with-a-line-that-divides-its-area-according-to-a-given-ratio-and-colored-points-inside
               y = r*sin(2*pi*th))
  
  
  # part 3 -- combine circle and points
  p_empty_circle +
    geom_point(data = df_circular_points_scatter, 
               aes(x = x, y = y))
}

## visualize
library(gridExtra)

set.seed(2021)
p1000_1 <- vis_points_inside_circle(n = 1000)
p1000_2 <- vis_points_inside_circle(n = 1000)
p2000_1 <- vis_points_inside_circle(n = 2000)
p2000_2 <- vis_points_inside_circle(n = 2000)


gridExtra::grid.arrange(p1000_1, p1000_2, p2000_1, p2000_2, nrow = 2)

Created on 2021-08-02 by the reprex package (v2.0.0)


It should be easy to notice the cluster around the center of each circle. How can we randomly arrange the points within the circle, to avoid the cluster at the center?

My attempt with sample()

I guess the solution involves the modification of df_circular_points_scatter data. However, I don't know how. I've tried to wrap each of df_circular_points_scatter's columns with sample(), but then got points that exceed the circumference, and overall a "cross" arrangement.

That is, if we wrap with sample() like this:

r  <- runif(n)
th <- runif(n)

df_circular_points_scatter <- 
    data.frame(x = sample(r*cos(2*pi*th)),  
               y = sample(r*sin(2*pi*th)))

Then the result is:

points_exceeding


Any idea how to avoid clustering? I don't necessarily want a perfectly even scatter. Just random within the circle.


Desired output

Adopted from this answer, the desired output should look something like: Sample output

Esta answered 2/8, 2021 at 9:16 Comment(0)
T
7

You are almost there. The sampling needs to be done as follows:

r <- runif(n)
th <- runif(n, 0, 2 * pi)
  
df_circular_points_scatter <- 
 data.frame(x = sqrt(r) * cos(th), 
            y = sqrt(r) * sin(th)

)

(see this question on crossvalidated)

Result:

enter image description here

Ten answered 2/8, 2021 at 9:32 Comment(0)
A
2

You have the points eventually distributed in r and theta, but you want them to be evenly distributed in area.

As the area element in the circle is $r dr dtheta$ (not $dr dtheta$ as your code implies), so you should transform r <- sqrt(r)

Appendicectomy answered 2/8, 2021 at 9:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.