我有这个数据集:
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)
在处理异常值时,我希望删除包含第1个和最新百分比的行,只考虑百分比计算的X
变量和所有X
变量作为一组。因此,百分位数将X1
至X5
视为一组。为此,我想到了以下步骤:
- 将
X1
到X5
的值替换为1到100(每个百分位数为1(。记住,我不是在寻找每个X
的百分位数,而是寻找所有X作为一个整体 - 删除变量
X1
到X5
包含1或100的行
我的尝试:(基于如何找到百分位数,用第5个和第95个百分位数替换异常值,删除数据帧中大于第95个百分点的数据(
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()
的情况下完成了这项工作,所以它不是创建一行,而是根据需要创建整个数据的副本- 此后将推导出该数据的两个分位数。(将是标量(
- 现在剩下的工作就是将这些值和数据中的其他值进行核对。所以我直接用了
across
- 使用cross,我构建了一个以
twiddle
开头的lambda公式,其参数仅为.
。这个旋转式的公式~ .
相当于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行输出作为所需
您可以尝试以下操作-
library(dplyr)
vec <- DF %>% select(starts_with('X')) %>% as.matrix() %>% quantile(c(0.01, 0.99))
DF %>% filter(if_all(starts_with('X'), ~. > vec[1] & . < vec[2]))