在ggplot中,如何绘制一个circle/disk,用一条线根据给定的比例和内部的彩色点划分它的区域?

In ggplot, how to draw a circle/disk with a line that divides its area according to a given ratio and colored points inside?

我想用圆圈内的点可视化比例。例如,假设我有 100 个点要分散(有点随机抖动)成一个圆圈。

接下来,我想用这个图表来表示每个州在 2020 年美国总统选举中投票 Biden/Harris 的人数比例。

示例 #1 -- 密歇根州
拜登获得了密歇根州50.62%的选票。我要绘制一个水平直径,将圆分成两半,然后用蓝色(民主党的颜色)为直径下方的点着色。


示例 #2 -- 怀俄明州
与密歇根州不同,拜登在怀俄明州仅获得26.55%的选票,大约是四分之一的选票。在这种情况下,我会画一条水平弦来划分圆,这样 disk 在弦下的面积是整​​个圆盘面积的 25%。然后我将该区域中的各个点涂成蓝色。因为我一共100分,所以25分代表怀俄明州25%的人给拜登投了票。


我的问题: 我如何用 ggplot 做到这一点?我研究了这个问题,这里有很多几何问题。首先,我所说的那种区域称为“circular segment". Second, there are many formulas to calculate its area, if we know some other parameters about the shape (such as the radius length, etc.). See this 不错的演示。

但是,我的目标不是解决几何问题,而只是以非常具体的方式表示比例:

  1. 画一个圆
  2. 在里面撒 X 个点
  3. 绘制一条(真实的或不可见的)水平线,根据给定的比例划分 circle/disk 区域
  4. 确保这些点是根据拆分排列的。也就是说,如果我们要表示 30%-70% 的分割,那么在分割圆盘的线下有 30% 的点。
  5. 在线下的点上色。

我知道这有点奇异的可视化效果,但我将感谢您对此提供的任何帮助。


编辑


我找到了对 JavaScript package 的引用,它做的事情与我的要求非常相似。

为了好玩,我试了一下。还有很多事情可以做。我同意这不是可视化比例的好方法,但如果它能吸引您的观众...

确定适当高度的公式取自Wikipedia。特别是我们需要公式

a/A = (theta - sin(theta))/(2*pi)
h = 1-cos(theta/2)

其中a是线段的面积; A是圆的整个面积; theta是定义线段的圆弧所描述的角度(图片见维基百科); h 是段的高度。

测高机。

afun <- function(x) (x-sin(x))/(2*pi)
## curve(afun, from=0, to = 2*pi)
find_a <- function(a) {
    uniroot(
        function(x) afun(x) -a,
        interval=c(0, 2*pi))$root
}
find_h <- function(a) {
    1- cos(find_a(a)/2)
}
vfind_h <- Vectorize(find_h)
## find_a(0.5)
## find_h(0.5)
## curve(vfind_h(x), from = 0, to= 1)

建立一个圈子

dd <- data.frame(x=0,y=0,r=1)
library(ggforce)
library(ggplot2); theme_set(theme_void())
gg0 <- ggplot(dd) + geom_circle(aes(x0=x,y0=y,r=r)) + coord_fixed()

完成

props <- c(0.2,0.5,0.3)  ## proportions
n <- 100                 ## number of points to scatter
cprop <- cumsum(props)[-length(props)]
h <- vfind_h(cprop)
set.seed(101)
r <- runif(n)
th <- runif(n, 0, 2 * pi)
  
dd <- 
 data.frame(x = sqrt(r) * cos(th), 
            y = sqrt(r) * sin(th))

dd2 <- data.frame(x=r*cos(2*pi*th), y = r*sin(2*pi*th))
dd2$g <- cut(dd2$y, c(1, 1-h, -1))
gg0 + geom_point(data=dd2, aes(x, y, colour = g), size=3)

有很多调整可以使它变得更好(有意义的类别名称;反转轴顺序以匹配图;可能添加分隔部分的线段,或(更多工作)多边形以便您可以遮蔽部分。

你应该一定要检查一下是否有错误——例如有些地方我可能使用了一组值,而我应该使用它们的一阶差分,反之亦然(值与累积总和)。但这应该可以帮助您入门。