1

使用 ggplot 绘制椭圆时,是否可以将椭圆限制为实际可能的值?

例如,以下可重现的代码和数据绘制了两个物种的 Ele 与 Var。Var 为正变量,不能为负。尽管如此,结果椭圆中仍包含负值。是否可以在 x 轴上将椭圆限制为 0(使用 ggplot)?

更具体地说,我正在描绘一个平坦的边缘,其中椭圆体在 x 轴上的 0 处被截断。

library(ggplot2)
set.seed(123)
df <- data.frame(Species = rep(c("BHS", "MTG"), each = 100),
                 Ele = c(sample(1500:3000, 100), sample(2500:3500, 100)),
                 Var = abs(rnorm(200)))

ggplot(df, aes(Var, Ele, color = Species)) +
  geom_point() +
  stat_ellipse(aes(fill = Species), geom="polygon",level=0.95,alpha=0.2) 

在此处输入图像描述

4

2 回答 2

6

您可以编辑默认统计数据以将点剪辑为特定值。这里我们将基本统计信息更改为将小于 0 的 x 值修剪为 0

StatClipEllipse <- ggproto("StatClipEllipse", Stat,
    required_aes = c("x", "y"),
    compute_group = function(data, scales, type = "t", level = 0.95,
       segments = 51, na.rm = FALSE) {
           xx <- ggplot2:::calculate_ellipse(data = data, vars = c("x", "y"), type = type,
               level = level, segments = segments)
           xx %>% mutate(x=pmax(x, 0))
      }
)

然后我们必须将它包装在一个 ggplot stat 中,stat_ellipe除了它使用我们的自定义 Stat 对象之外

stat_clip_ellipse <- function(mapping = NULL, data = NULL,
                         geom = "path", position = "identity",
                         ...,
                         type = "t",
                         level = 0.95,
                         segments = 51,
                         na.rm = FALSE,
                         show.legend = NA,
                         inherit.aes = TRUE) {
  layer(
    data = data,
    mapping = mapping,
    stat = StatClipEllipse,
    geom = geom,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params = list(
      type = type,
      level = level,
      segments = segments,
      na.rm = na.rm,
      ...
    )
  )
}

然后你可以用它来制作你的情节

ggplot(df, aes(Var, Ele, color = Species)) +
  geom_point() +
  stat_clip_ellipse(aes(fill = Species), geom="polygon",level=0.95,alpha=0.2) 

在此处输入图像描述

这是受到stat_ellipse 源代码的启发

于 2017-08-18T17:24:14.850 回答
3

根据我上面的评论,我为可视化创建了一个较少误导的选项。这忽略了均匀分布的问题,因为与严重偏斜的变量y相比,这是一个不太严重的问题。x

这两个选项都使用ggforcepackage,它是 的扩展ggplot2,但为了以防万一,我还包含了我使用的特定函数的源代码。

library(ggforce)
library(scales)


# power_trans <- function (n) 
# {
#     scales::trans_new(name = paste0("power of ", fractions(n)), transform = function(x) {
#         x^n
#     }, inverse = function(x) {
#         x^(1/n)
#     }, breaks = scales::extended_breaks(), format = scales::format_format(), 
#         domain = c(0, Inf))
# }

选项1:

ggplot(df, aes(Var, Ele, color = Species)) +
  geom_point() + 
  stat_ellipse(aes(fill = Species), geom="polygon",level=0.95,alpha=0.2) +
  scale_x_sqrt(limits = c(-0.1,3.5), 
               breaks = c(0.0001,1:4), 
               labels = 0:4,
               expand = c(0.00,0))

在此处输入图像描述

此选项沿平方根变换拉伸 x 轴,分散聚集在零附近的点。然后它在这个新空间上计算一个椭圆。

  • 优点:看起来仍然像一个椭圆。
  • 缺点:为了让它玩得很好并标记Var=0x 轴上的点,你必须使用expand = c(0,0),它精确地剪辑限制,因此需要更多地摆弄手动限制/中断/标签,包括选择一个非常小的值 (0.0001) 表示为 0。
  • 缺点:x 值不是沿轴线性分布的,这在阅读图形时需要更多的认知负荷。

选项 2:

ggplot(df, aes(sqrt(Var), Ele, color = Species)) +
  geom_point() + 
  stat_ellipse() +
  coord_trans(x = ggforce::power_trans(2)) + 
  scale_x_continuous(breaks = sqrt(0:4), labels = 0:4,
                     name = "Var")

在此处输入图像描述

此选项绘制预转换的sqrt(Var)(注意aes(...))。然后它根据这个新的近似正常值计算椭圆。然后它拉伸 x 轴,使 的值Var再次呈线性间隔,这会在同一变换中扭曲椭圆。

  • 优点:看起来很酷。
  • 优点:Var 的值很容易在 x 轴上解释。
  • 优点:您可以很容易地看到 Var=0 附近的密度点和“蛋”的宽平端。
  • 优点:尖端显示了在这些值下密度有多低。
  • 缺点:看起来不熟悉,需要解释和额外的认知负荷来解释。
于 2017-08-18T21:38:13.097 回答