如何在没有coord_polar的情况下制作堆叠圆图

时间:2018-10-29 07:23:44

标签: r ggplot2 visualization

我有一个与此类似的数据集:

x <- 100 - abs(rnorm(1e6, 0, 5))
y <- 50 + rnorm(1e6, 0, 3)
dist <- sqrt((x - 100)^2 + (y - 50)^2)
z <- exp(-(dist / 8)^2)

可以如下显示:

data.frame(x, y, z) %>%  
  ggplot() + geom_point(aes(x, y, color = z))

enter image description here

我想做的是一个堆叠的半圆图,在后续层中具有z的平均值。我认为可以将geom_colcoord_polar()组合使用,尽管我能得到的最远距离是

data.frame(x, y, z, dist) %>% 
  mutate(dist_fct = cut(dist, seq(0, max(dist), by = 5))) %>% 
  ggplot() + geom_bar(aes(x = 1, y = 1, fill = dist_fct), stat = 'identity', position = 'fill') +
  coord_polar()

enter image description here

这显然与预期相差甚远(各层的大小应相等,图的右半部分应剪裁)。

问题是由于进一步使用coord_polar(),我不能真正使用annotate_custom()。所以我的问题是:

  • 可以像这样不用完成coord_polar()吗?
  • 如果没有,怎么用coord_polar()完成?

除了绘制由点构成的图层外,结果应类似于下图,我只想绘制整个图层,其颜色定义为图层内部z的平均值。 enter image description here

3 个答案:

答案 0 :(得分:3)

如果您想要简单的半径带,也许像您在问题中所描绘的那样,可以进行如下操作:

# your original sample data
x <- 100 - abs(rnorm(1e6, 0, 5))
y <- 50 + rnorm(1e6, 0, 3)
dist <- sqrt((x - 100)^2 + (y - 50)^2)

nbr_bands <- 6  # set nbr of bands to plot 

# calculate width of bands
band_width <- max(dist)/(nbr_bands-1)

# dist div band_width yields an integer 0 to nbr bands
# as.factor makes it categorical, which is what you want for the plot
band = as.factor(dist %/% (band_width))

library(dplyr)
library(ggplot2)
data.frame(x, y, band) %>%  
  ggplot() + geom_point(aes(x, y, color = band)) + coord_fixed() +
  theme_dark()  # dark theme

enter image description here

编辑以详细说明:

第一次尝试时,最好使用方便的cut()函数来计算半径颜色类别。

一种获得分类(离散)颜色(而不是连续着色)的方法是对绘图颜色组进行设置,将aes color=设置为一个因子列。

要直接从cut()获取因子,可以使用选项ordered_result=TRUE

band <- cut(dist, nbr_bands, ordered_result=TRUE, labels=1:nbr_bands)  # also use `labels=` to specify your own labels

data.frame(x, y, band) %>%
  ggplot() + geom_point(aes(x, y, color = band)) + coord_fixed() 

enter image description here

或更简单地说,您可以使用不带选项的cut()并使用as.factor()转换为因数:

band <- as.factor( cut(dist, nbr_bands, labels=FALSE) )

data.frame(x, y, band) %>%
  ggplot() + geom_point(aes(x, y, color = band)) + coord_fixed() 

enter image description here

答案 1 :(得分:3)

ggforce包中的圆形和圆弧绘图功能很有用:

# data
set.seed(1234)
df <- data.frame(x = 100 - abs(rnorm(1e6, 0, 5)),
                 y = 50 + rnorm(1e6, 0, 3)) %>%  
  mutate(dist = sqrt((x - 100)^2 + (y - 50)^2)) %>%
  mutate(z = exp(-(dist / 8)^2))

# define cut-off values
cutoff.values <- seq(0, ceiling(max(df$dist)), by = 5)

df %>%
  # calculate the mean z for each distance band
  mutate(dist_fct = cut(dist, cutoff.values)) %>%
  group_by(dist_fct) %>%
  summarise(z = mean(z)) %>%
  ungroup() %>%

  # add the cutoff values to the dataframe for inner & outer radius
  arrange(dist_fct) %>%
  mutate(r0 = cutoff.values[-length(cutoff.values)],
         r = cutoff.values[-1]) %>%

  # add coordinates for circle centre
  mutate(x = 100, y = 50) %>%

  # plot
  ggplot(aes(x0 = x, y0 = y, 
             r0 = r0, r = r, 
             fill = z)) +
  geom_arc_bar(aes(start = 0, end = 2 * pi), 
               color = NA) + # hide outline

  # force equal aspect ratio in order to get true circle
  coord_equal(xlim = c(70, 100), expand = FALSE)

在我的计算机上,图生成花费了不到1秒的时间。您的可能会有所不同。

plot

答案 2 :(得分:2)

我不确定这是否满足所有条件,但这应该是一个开始。为了减少打印时间,我将数据汇总到一个网格中,使您可以使用geom_raster。我并不完全了解这些中断以及您正在使用的所有内容,因此您可能需要调整一些如何划分数据以形成不同频段的方法。我用cut_intervalcut_width尝试了几种方法,这是插入不同选项(例如乐队的数量或宽度)的好地方。

由于您提到要获得每个频段的平均z,所以我将按照网格xy以及切线dist进行分组,然后使用{ {1}}用于设置频段。我迈出了一步,制作如示例中的标签-您可能想要反转它们或调整它们的位置-但这是通过获取每个波段的因子水平的数目来实现的。

z

要制作标签,请对数据进行汇总,使其每条带具有一行-我这样做是通过取网格library(tidyverse) set.seed(555) n <- 1e6 df <- data_frame( x = 100 - abs(rnorm(n, 0, 5)), y = 50 + rnorm(n, 0, 3), dist = sqrt((x - 100)^2 + (y - 50)^2), z = exp(-(dist / 8)^2) ) %>% mutate(brk = cut(dist, seq(0, max(dist), by = 5), include.lowest = T)) summarized <- df %>% filter(!is.na(brk)) %>% mutate(x_grid = floor(x), y_grid = floor(y)) %>% group_by(x_grid, y_grid, brk) %>% summarise(avg_z = mean(z)) %>% ungroup() %>% # mutate(z_brk = cut_width(avg_z, width = 0.15)) %>% mutate(z_brk = cut_interval(avg_z, n = 9)) %>% mutate(brk_num = as.numeric(z_brk)) head(summarized) #> # A tibble: 6 x 6 #> x_grid y_grid brk avg_z z_brk brk_num #> <dbl> <dbl> <fct> <dbl> <fct> <dbl> #> 1 75 46 (20,25] 0.0000697 [6.97e-05,0.11] 1 #> 2 75 47 (20,25] 0.000101 [6.97e-05,0.11] 1 #> 3 75 49 (20,25] 0.0000926 [6.97e-05,0.11] 1 #> 4 75 50 (20,25] 0.0000858 [6.97e-05,0.11] 1 #> 5 75 52 (20,25] 0.0000800 [6.97e-05,0.11] 1 #> 6 76 51 (20,25] 0.000209 [6.97e-05,0.11] 1 的最小值,然后使用x的平均值,以便出现在情节的中间。

y

labels <- summarized %>% group_by(brk_num) %>% summarise(min_x = min(x_grid)) %>% ungroup() %>% mutate(y_grid = mean(summarized$y_grid)) head(labels) #> # A tibble: 6 x 3 #> brk_num min_x y_grid #> <dbl> <dbl> <dbl> #> 1 1 75 49.7 #> 2 2 88 49.7 #> 3 3 90 49.7 #> 4 4 92 49.7 #> 5 5 93 49.7 #> 6 6 94 49.7 非常适合这些情况,即您将数据存储在均匀间隔的网格中,每个位置只需要均匀的图块。此时,汇总数据有595行,而不是原始的100万行,因此绘制时间不应该成为问题。

geom_raster

reprex package(v0.2.1)于2018-11-04创建