通过因子水平和列识别R数据中的异常值

时间:2019-05-13 21:07:55

标签: r loops dplyr outliers

我有一个定期更新的生物学数据库,该数据库目前包含250个物种中的13,500个人的记录(物种=第一栏中的因子水平)。每个人都有一个唯一的ID(第二列)。对于每个人,记录了7个不同的测量值(第3-9列)。手动将这么多值输入数据库后,我确定会出现一些错别字并造成离群值,例如,应该将读数读为15.2的度量值输入为1.52或152或25.2。我想找出那些离群值,以便我可以解决它们,而不是将它们扔掉,但是有太多的物种需要逐案处理。如何针对每个物种的每个测量子集自动化并组织异常值搜索的输出?最后一部分很关键,因为每种物种的大小可能都不同,并且测量值也大不相同。我正在尝试尽可能地简化程序,因为每次将新一批数据添加到数据库时(或直到有人获得文件制造商许可时),都可能会这样做。

我正在R中进行分析。我认为为平均值之外的2或3个标准偏差之外的所有值嵌套嵌套的for循环将解决问题,和/或使用dplyr和分位数功能进行group_by。但是我无法弄清楚如何在返回实际异常值时立即运行所有列。还有很多其他问题需要解决,但我找不到将所有问题组合在一起的问题。

示例数据:

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)
)

谢谢!

1 个答案:

答案 0 :(得分:0)

从提供的数据开始...

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()

它从this answer借用以查找均值和标准差(不包括当前记录),然后如果该行距均值大于2 SD,则将该行标记为离群值。

如果您排除当前记录并且记录不是不是异常值,它会变得很奇怪,它会明显改变均值和标准差。但是,如果记录 是一个异常值,则您绝对想这样做。 :)