根据密度子集数据框

时间:2018-06-07 19:17:49

标签: r ggplot2 dplyr

我正在寻找一种能够根据双变量观测密度对数据帧进行子集化的函数。例如:

ggplot(iris, aes(x = Petal.Length, y = Sepal.Width, color = Species)) + 
  stat_density2d(geom = 'polygon', aes(fill = ..level..), n = 8) + 
  geom_point()

在这里,我只想根据物种中的点密度显示异常点(即只显示来自setosa的3个点和来自位于轮廓之外的virginica的4个点)。

2 个答案:

答案 0 :(得分:1)

这是一个相当黑客的解决方案,但您可以编写一个函数来提取轮廓图之外的点并返回仅包含这些点的数据框:

plot_outliers_only <- function (original_plot) {
  require(ggplot2)
  require(sp)
  pb <- ggplot_build(original_plot)
  group_labels <- grep("001", levels(pb$data[[1]]$group), value=TRUE)
  outlier_points <- lapply(group_labels, function (gl) {
    contour_data <- filter(pb$data[[1]], as.character(group)==gl)
    original_data <- 
    group_id <- as.numeric(strsplit(gl, "-")[[1]][1])
    outlier_id <- pb$data[[2]] %>%
      filter(group==group_id) %>%
      select(c(x, y)) %>%
      apply(1, function (point) {
        point.in.polygon(point[1], point[2], contour_data$x, contour_data$y)==0
      }) %>%
      which()
    if (length(outlier_id)==0) return (outlier_id)
    grouping_name <- as.character(original_plot$mapping$colour)
    as.numeric(original_plot$data[, grouping_name]) %>%
      `==`(group_id) %>%
      which() %>%
      slice(original_plot$data, .) %>%
      `[`(., outlier_id, )
  })
  do.call(what=rbind, outlier_points)
}

P <- ggplot(iris, aes(x = Petal.Length, y = Sepal.Width, color = Species)) + 
    stat_density2d(geom = 'polygon', aes(fill = ..level..), n = 8) + 
    geom_point()

plot_outliers_only(P)

答案 1 :(得分:1)

我的方法有点令人费解,所以请耐心等待,我将在下面解释:

library(data.table)
dt <- as.data.table(iris)[, .(Petal.Length, Sepal.Width, Species)]
dt[, sample := .I]
dt <- melt(dt, id.vars = c("Species", "sample"))
dt[, c("meanval", "sdval") := .(mean(value), sd(value)), .(Species, variable)]
dt[abs({value - meanval} / sdval) > 2, outlier := TRUE]
dt[, anyOutliers := sum(outlier, na.rm = T), sample]
dt[anyOutliers != 0, outlier := TRUE]
dt <- dcast(
  dt[, .(Species, variable, value, outlier, sample)],
  sample + outlier + Species ~ variable,
  value.var = "value"
)

首先,我们将dt指定为数据集,并仅保留我们计划绘制的列。接下来,我们分配一个虚拟列,这对于此特定数据集很重要,以便稍后区分行。然后我们melt()数据集以获得权宜之计。然后,对于每个物种,我们计算每个值的平均值和标准差。这允许我们在下面的行中定义异常值(您可以在此处更改> 2以影响要使用的SD数量。)

然后,对于每一朵花,我们发现它是否是我们所选择的任何指标的异常值(在本例中为petal.length和sepal.width)。如果是的话,整个花被标记为异常值。然后,我们将表格重新制作成原始表格,现在只有一个离群列显示花朵是否是任何指标中的异常值。

我不会去绘制这些,因为你可以自己弄清楚你想怎么做,但这应该给出一个方向的一般要点。希望有所帮助。