Если у меня есть этот фрейм данных:
df <- data.frame(time = seq(as.Date('2000-01-01'), length.out = 200, by = 'days'),
a = rnorm(200,8.4, 22), b=rnorm(200,8.4, 22), d= rnorm(200,8.4, 22),
e=rnorm(200,8.4, 22))
Каким будет самый простой способ подмножить это df
, чтобы значения из каждого столбца были выше 10% процентиля, но ниже 90%?
Я мог бы сделать это с помощью цикла, т.е.:
for (i in names(df[,2:5])){
print(i)
column <- df[,c('time', i)]
q <- unname(quantile(column[,2], probs = c(0.1, 0.9))) # just for one column
column <- column[column[,2] > q[1] &column[,2] < q[2],]
df <- merge(df, column, by = 'time', all.x = T)
}
Но есть более простые и элегантные способы сделать это с помощью таких функций или пакетов, как dplyr
. Спасибо!
Используйте sapply
над столбцами и отфильтруйте значения, которые находятся в диапазоне.
sapply(df[-1], function(x) x[x > quantile(x, 0.1) & x < quantile(x, 0.9)])
Вернуть столбец time
может быть непросто, поскольку мы отфильтровали значения, которые каждая строка потенциально может представлять разные time
.
Лучшим вариантом, предложенным @Sotos, является преобразование этих значений в NA
вместо фильтрации.
cbind(df[1], sapply(df[-1], function(i)
replace(i, i < quantile(i, 0.1) | i > quantile(i, 0.9), NA)))
или просто конвертировать в NA, sapply(df[-1], function(i) replace(i, i < quantile(i, 0.1) | i > quantile(i, 0.9), NA))
Привет, спасибо за совет. К сожалению, мне нужен столбец времени, так как мне нужно выполнить некоторые агрегации после этой фильтрации.
Вот dplyr
подход:
library(dplyr)
df %>%
mutate_at(vars(a:e), function(x) if_else(between(percent_rank(x), .1, .9), x, NA_real_))
Привет, я знаю. В этом случае можно вставить NA