r - 使用 ggplot 展开密度图

标签 r plot ggplot2

我从 50 岁看到了这个很棒的情节,不同学院的密度情节略有重叠。查看 this link at fivethirtyeight.com

你将如何用 ggplot2 复制这个图?

具体来说,您将如何获得 轻微重叠 , facet_wrap 是行不通的。

TestFrame <-  
  data.frame(
    Score =
      c(rnorm(100, 0, 1)
        ,rnorm(100, 0, 2)
        ,rnorm(100, 0, 3)
        ,rnorm(100, 0, 4)
        ,rnorm(100, 0, 5))
    ,Group =
      c(rep('Ones', 100)
        ,rep('Twos', 100)
        ,rep('Threes', 100)
        ,rep('Fours', 100)
        ,rep('Fives', 100))
  )

ggplot(TestFrame, aes(x = Score, group = Group)) +
  geom_density(alpha = .75, fill = 'black')

Partially overlaid density

最佳答案

与 ggplot 一样,关键是以正确的格式获取数据,然后绘图非常简单。我确信会有另一种方法来做到这一点,但我的方法是使用 density() 进行密度估计。然后制作一种手册geom_density()geom_ribbon() ,它需要一个 yminymax ,将形状从 x 轴上移开所必需的。

剩下的挑战是让打印顺序正确,因为 ggplot 似乎会首先打印最宽的色带。最后,需要最庞大代码的部分是四分位数的产生。

我还制作了一些与原始数据更一致的数据。

library(ggplot2)
library(dplyr)
library(broom)
rawdata <- data.frame(Score = rnorm(1000, seq(1, 0, length.out = 10), sd = 1),
                  Group = rep(LETTERS[1:10], 10000))

df <- rawdata %>% 
  mutate(GroupNum = rev(as.numeric(Group))) %>% #rev() means the ordering will be from top to bottom
  group_by(Group, GroupNum) %>% 
  do(tidy(density(.$Score, bw = diff(range(.$Score))/20))) %>% #The original has quite a large bandwidth
  group_by() %>% 
  mutate(ymin = GroupNum * (max(y) / 1.5), #This constant controls how much overlap between groups there is
         ymax = y + ymin,
         ylabel = ymin + min(ymin)/2,
         xlabel = min(x) - mean(range(x))/2) #This constant controls how far to the left the labels are

#Get quartiles
labels <- rawdata %>% 
  mutate(GroupNum = rev(as.numeric(Group))) %>% 
  group_by(Group, GroupNum) %>% 
  mutate(q1 = quantile(Score)[2],
         median = quantile(Score)[3],
         q3 = quantile(Score)[4]) %>%
  filter(row_number() == 1) %>% 
  select(-Score) %>% 
  left_join(df) %>% 
  mutate(xmed = x[which.min(abs(x - median))],
         yminmed = ymin[which.min(abs(x - median))],
         ymaxmed = ymax[which.min(abs(x - median))]) %>% 
  filter(row_number() == 1)

p <- ggplot(df, aes(x, ymin = ymin, ymax = ymax)) + geom_text(data = labels, aes(xlabel, ylabel, label = Group)) +


geom_vline(xintercept = 0, size = 1.5, alpha = 0.5, colour = "#626262") + 
  geom_vline(xintercept = c(-2.5, -1.25, 1.25, 2.5), size = 0.75, alpha = 0.25, colour = "#626262") + 
  theme(panel.grid = element_blank(),
        panel.background = element_rect(fill = "#F0F0F0"),
        axis.text.y = element_blank(),
        axis.ticks = element_blank(),
        axis.title = element_blank())
for (i in unique(df$GroupNum)) {
  p <- p + geom_ribbon(data = df[df$GroupNum == i,], aes(group = GroupNum), colour = "#F0F0F0", fill = "black") +
    geom_segment(data = labels[labels$GroupNum == i,], aes(x = xmed, xend = xmed, y = yminmed, yend = ymaxmed), colour = "#F0F0F0", linetype = "dashed") +
    geom_segment(data = labels[labels$GroupNum == i,], x = min(df$x), xend = max(df$x), aes(y = ymin, yend = ymin), size = 1.5, lineend = "round") 
}
p <- p + geom_text(data = labels[labels$Group == "A",], aes(xmed - xlabel/50, ylabel), 
                   label = "Median", colour = "#F0F0F0", hjust = 0, fontface = "italic", size = 4)  

编辑
我注意到原版实际上通过用水平线拉伸(stretch)每个分布来做了一些捏造(如果你仔细观察,你可以看到一个连接......)。我添加了与第二个 geom_segment() 类似的内容在循环。

enter image description here

关于r - 使用 ggplot 展开密度图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33619980/

相关文章:

r - 将包含分隔字符串的数据框列拆分为多个列,并保留拆分字符串的特定部分

python - rpy2 ggplot2错误: Invalid input: date_trans works with objects of class Date only

r - 用于绘制时间序列数据的 x 轴刻度的日期格式

python matplotlib 以小时格式设置 xticks

linux - Linux 与 mswindows 中的 Matplotlib 绘图

python - 在 matplotlib 图上绘制裁剪后的背景图像

r - 将ggplot2与名称中带有空格的列一起使用

r - 如何定义线条颜色并在一个图中显示两个图例 block

r - 使用 docker 文件安装 R 包

r - 如果观察在特定时间之前停止,则结转特定值