首页 > 解决方案 > 按因子水平和列识别 R 数据中的异常值

问题描述

我有一个定期更新的生物数据库,目前包含 250 个物种的 13,500 个人的记录(物种 = 第一列中的因子水平)。每个人都有一个唯一的 ID(第 2 列)。对于每个人,记录了 7 个不同的测量值(第 3-9 列)。由于手动将这么多值输入数据库,我确信会出现一些拼写错误并产生异常值,例如,应该读取 15.2 的测量值可能已输入为 1.52 或 152 或 25.2。我想识别那些异常值,这样我就可以修复它们而不是丢弃它们,但是有太多的物种无法根据具体情况进行处理。如何自动化和组织每个物种的每个测量子集的异常值搜索的输出?最后一部分很关键,因为每个物种的大小可能不同,测量结果也大不相同。我'

我在 R 中进行分析。我认为对于平均值的 2 或 3 个标准差之外的所有值的嵌套 for 循环可以解决问题,和/或 group_by 与 dplyr 和 quantile 函数。但是我无法弄清楚如何在返回实际异常值的同时一次运行所有列。还有许多其他问题可以解决这个问题,但我找不到任何可以将它们放在一起的问题。

示例数据:

df = data.frame(
  species = c("a","b","a","b","a","b","a","b","a","b"),
  uniqueID = c("x01","x02","x03","x04","x05","x06","x07","x08","x09","x10"),
  metric1 = c(1,2,3,1,2,3,1,2,3,11),
  metric2 = c(4,5,6,4,5,6,55,4,5,6),
  metric3 = c(0.7,7,8,9,7,8,9,77,8,9)
)

就预期结果而言,我正在设想一个 data.frame 或矩阵报告物种、unique_ID、带有异常值的测量/列以及异常值本身。但是如何格式化并不重要,例如:

outliers = data.frame(
  species = c("a","a","b","b"),
  uniqueID = c("x01","x07","x08","x10"),
  var = c("metric3","metric2","metric3","metric1"),
  value = c(0.7,55,77,11)
)

提前致谢!

标签: rloopsdplyroutliers

解决方案


从您提供的数据开始...

df = data.frame(
  species = c("a","b","a","b","a","b","a","b","a","b"),
  uniqueID = c("x01","x02","x03","x04","x05","x06","x07","x08","x09","x10"),
  metric1 = c(1,2,3,1,2,3,1,2,3,11),
  metric2 = c(4,5,6,4,5,6,55,4,5,6),
  metric3 = c(0.7,7,8,9,7,8,9,77,8,9)
)

我将在tidyverse这里自由地使用...

library(tidyverse)

然后将行增加三倍,这样标准差计算就不会在我们身上消失,并添加另一个异常值行......

df2 <- df %>% 
  bind_rows(df) %>% 
  bind_rows(df) %>% 
  add_row(
    species = "a",
    uniqueID = "x01",
    metric1 = 1,
    metric2 = 4,
    metric3 = 1e12
  )

如果你尝试这样的事情怎么办?

df2 %>% 
  gather(key = "metric", value = "value", -species, -uniqueID) %>% 
  group_by(species, uniqueID, metric) %>% 
  arrange(species, uniqueID, metric) %>% # just to make the results easy to scan
  mutate(
    mean_obs = mapply(function(x) mean(value[-x]), 1:n()),
    stdev    = mapply(function(x)   sd(value[-x]), 1:n()),
    minimum  = mean_obs - stdev * 2,
    maximum  = mean_obs + stdev * 2,
    outlier  = value < minimum | value > maximum
  ) %>% 
  filter(outlier) %>% 
  glimpse()

它借用这个答案来查找不包括当前记录的均值和标准差,然后如果该行与均值相差超过 2 个 SD,则将该行标记为异常值。

如果您排除当前记录并且该记录不是异常值并且它会明显改变平均值和标准偏差,它会变得很奇怪。但是,如果记录异常值,您肯定想要这样做。:)


推荐阅读