我有这个数据集:
A <- paste0("event_", c(1:100))
some_number <- sample.int(1000,size=100)
X1 <- c(1:100)
X2 <- c(101:200)
X3 <- c(201:300)
X4 <- c(301:400)
X5 <- c(401:500)
DF <- data.frame(A, some_number, X1, X2, X3, X4, X5)
当我处理异常值时,我希望删除包含第一个和最新百分位的行,仅考虑用于百分位计算的 X
变量和所有 X
变量作为一组。因此,百分位数会将 X1
到 X5
视为一个组。为此,我想到了以下步骤:
- 将
X1
到X5
的值替换为 1 到 100(每个百分位 1)。请记住,我不是在查找每个X
的百分位数,而是查找所有 X 的整体。 - 删除变量
X1
到X5
包含1或100的行
我的尝试:(基于 how to find percentiles 、 replace outliers with the 5th and 95th percentile 、 remove data greater than 95th percentile in data frame )
as.data.frame(sapply(select(DF, X1:X5), function (x) {
qx <- quantile(x, probs = c(1:100)/100)
cut(x, qx, labels = c(1:100))
}))
但是..我的尝试引发了错误,即中断数量与标签数量不同,我正在努力分配新的数据帧而不丢失 A
和 some_number
变量(在我的实际问题中,它们不是两列,而是近 50 个)
有什么建议吗?
最佳答案
在dplyr
中同时使用across
和c_across
,你也可以这样做-
步骤说明 -
c_across
通常与row_wise
一起使用,因为它创建通过其内部参数子集化的数据的完整副本。但我是在没有rowwise()
的情况下完成的,因此它不是创建一行,而是根据需要创建整个数据的副本。- 此后将推导出该数据的两个分位数。 (这将是标量)
- 现在剩下的唯一工作就是检查这些值与数据中的所有其他值。所以我在这里直接使用了
cross
。 - 使用 across 我构建了一个 lambda 公式,该公式以
twiddle
开头,其参数仅为.
。这个旋转风格的公式~ .
相当于function(x) x
,其余的很清楚。
DF %>% mutate(across(starts_with('X'), ~ifelse(. > quantile(c_across(starts_with('X')), 0.99) |
. < quantile(c_across(starts_with('X')), 0.01),
NA, .)
)) %>% na.omit()
#> A some_number X1 X2 X3 X4 X5
#> 6 event_6 69 6 106 206 306 406
#> 7 event_7 871 7 107 207 307 407
#> 8 event_8 356 8 108 208 308 408
.
.
.
#> 93 event_93 432 93 193 293 393 493
#> 94 event_94 967 94 194 294 394 494
#> 95 event_95 516 95 195 295 395 495
由于 starts_with
仅适用于 across
或 c_across
并避免此处较慢的 rowwise
,我们也可以这样做直接这个
DF %>% filter(rowSums(cur_data()[str_detect(names(DF), 'X')] > quantile(c_across(starts_with('X')), 0.99)) == 0 &
rowSums(cur_data()[str_detect(names(DF), 'X')] < quantile(c_across(starts_with('X')), 0.01)) == 0)
这还将根据需要提供 90 行输出
关于一次替换和删除数据帧或多列中的第一个和最后一个百分位数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67896918/