我有一个定期更新的生物学数据库,该数据库目前包含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)
)
谢谢!
答案 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,则将该行标记为离群值。
如果您排除当前记录并且记录不是不是异常值,它会变得很奇怪,它会明显改变均值和标准差。但是,如果记录 是一个异常值,则您绝对想这样做。 :)