【发布时间】:2018-10-05 10:13:54
【问题描述】:
我有一个大约 600 列的数据框。我想根据变量对我的数据框进行分组,并根据每个列和组的特定阈值对这些“感兴趣的列”(通常是总列数的一小部分)的n 进行过滤。
我已经开始使用 dplyr 执行此操作。我将使用iris 数据集(因为我不是原创)来演示:
library(tidyverse)
iris %>%
group_by(Species) %>%
mutate_at(vars(starts_with("Petal")),
funs(threshold = quantile(., 0.5) - IQR(.)))
这会计算我的阈值(每组和每列)并将它们放入名为 Petal.Length_threshold 和 Petal.Width_threshold 的新列中。
# A tibble: 150 x 7
# Groups: Species [3]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species Petal.Length_th…
<dbl> <dbl> <dbl> <dbl> <fct> <dbl>
1 5.1 3.5 1.4 0.2 setosa 1.32
2 4.9 3 1.4 0.2 setosa 1.32
3 4.7 3.2 1.3 0.2 setosa 1.32
4 4.6 3.1 1.5 0.2 setosa 1.32
5 5 3.6 1.4 0.2 setosa 1.32
6 5.4 3.9 1.7 0.4 setosa 1.32
7 4.6 3.4 1.4 0.3 setosa 1.32
8 5 3.4 1.5 0.2 setosa 1.32
9 4.4 2.9 1.4 0.2 setosa 1.32
10 4.9 3.1 1.5 0.1 setosa 1.32
我现在要做的是检查每一行的所有感兴趣的列是否大于它们各自的(组和列)阈值。我是这样做的:
columns <- colnames(
iris %>%
select(starts_with("Petal"))
)
threshold_cols <- paste(columns, "threshold", sep = "_")
filtered_iris <- iris %>%
group_by(Species) %>%
mutate_at(vars(starts_with("Petal")),
funs(threshold = quantile(., 0.5) - IQR(.))) %>%
filter(UQ(as.name(columns[1])) > UQ(as.name(threshold_cols[1])) &
UQ(as.name(columns[2])) > UQ(as.name(threshold_cols[2])))
(注意UQ(as.name()) 是由于烦人的 dplyr 非标准评估导致难以将列名作为变量输入到 dplyr 函数中)。
问题是我想概括这一点(因为我想编写一个可重用的函数),以便它能够将任意数量的“感兴趣的列”与其各自的(组和列)阈值进行比较。我可以使用starts_with() 找出在每种情况下我有多少感兴趣的列,这将是上面代码中columns 的长度。
此外,写出UQ(as.name(columns[1])) > UQ(as.name(threshold_cols[1])) 既丑陋又耗时,因此对于如何改进这一点的任何建议也将不胜感激。
我通过编写自己的函数来添加到 dplyr 管道的末尾来尝试此操作。该功能变得非常hacky且难以阅读,但它是:
columns <- colnames(
iris %>%
select(starts_with("Petal"))
)
threshold_fun <- function(x){
# obtain only columns of interest
reduced_x <- x[,columns]
# create empty threshold vector
threshold <- vector(mode = "numeric",
length = length(columns))
# fill vector with the threshold
# result should be a vector of 2 (in this case) with the
# Petal.Length threshold then the Petal.Width threshold
for (i in 1:length(columns)){
print(i)
threshold[i] <- quantile(reduced_x[,i], 0.5) + IQR(reduced_x[,i])
}
# for each row check that all elements are greater than
# threshold. Result should be vector of TRUEs and FALSEs
filter_rows <- apply(reduced_x, 1, function(a)
sum(a > threshold) == length(columns))
# subset using vector above
filtered_x <- x[filter_rows,]
return(filtered_x)
}
my_filter <- iris %>%
group_by(Species) %>%
threshold_fun()
这给了我一个错误Error: Can't use matrix or array for column indexing。我尝试添加print() 语句来试图找出问题出现在这个函数的哪个位置,并且它似乎在for循环中。仅此一项就会出现上述错误:quantile(reduced_x[,i], 0.5)。
我的问题是,如何概括第一个 dplyr 代码或修复我的函数?
编辑
Calum You 的回答很好,但如果它对将来想知道这个问题的人有用,我已经设法使我的功能正常工作:
columns <- colnames(
iris %>%
select(starts_with("Petal"))
)
threshold_fun <- function(x){
# obtain only columns of interest
reduced_x <- x[,columns]
# create empty threshold vector
threshold <- vector(mode = "numeric",
length = length(columns))
for (i in 1:length(columns)){
threshold[i] <- quantile(reduced_x[,i][[1]], 0.5) - IQR(reduced_x[,i][[1]])
}
# for each row check that all elements are greater than threshold.
# Result should be vector of TRUEs and FALSEs
filter_rows <- apply(reduced_x, 1, function(a){
sum(a > threshold) == length(columns)}
)
# subset using vector above
filtered_x <- x[filter_rows,]
#
return(filtered_x)
}
myiris <- iris %>%
group_by(Species) %>%
do(threshold_fun(.))
-
reduced_x[,i]返回一个数据帧,而reduced_x[,i][[1]]返回一个向量。mean和quantile等函数需要向量 - 添加
do()(do(threshold_fun(.))) 可确保尊重组,并在数据帧组而不是整个数据帧上执行功能(如直接通过管道传输到threshold_fun()时)。更多信息here
(是的,现在这是一个荒谬的长问题)
【问题讨论】:
-
您到底希望能够传递给这个函数什么?列名列表,或者因为您只是在查找带有
starts_with的列,您可以将其作为参数传递吗? -
抱歉,我应该指定 - 我希望分组的变量和放入
starts_with()以查找感兴趣的列的字符串每次都是相同的。所以这个函数应该只需要将df作为参数。 -
@JPL:您能解释一下
quantile(., 0.5) - IQR(.))衡量的阈值,统计数据吗?