所有/任何列都大于特定值的子集行

时间:2012-03-24 23:33:21

标签: r

使用

df <- data.frame(id=c(1:5), v1=c(0,15,9,12,7), v2=c(9,32,6,17,11))

如何在大于10的所有列上提取值,并返回:

  id v1 v2
2  2 15 32
4  4 12 17

如果在任何大于10的列上,该怎么办?

  id v1 v2
2  2 15 32
4  4 12 17
5  5  7 11

4 个答案:

答案 0 :(得分:19)

分别针对问题的第一部分和第二部分查看函数all()any()apply()函数可用于在行或列上运行函数。 (MARGIN = 1是行,MARGIN = 2是列等)。注意我在apply()上使用df[, -1]在进行比较时忽略id变量。

第1部分:

> df <- data.frame(id=c(1:5), v1=c(0,15,9,12,7), v2=c(9,32,6,17,11))
> df[apply(df[, -1], MARGIN = 1, function(x) all(x > 10)), ]
  id v1 v2
2  2 15 32
4  4 12 17

第2部分:

> df[apply(df[, -1], MARGIN = 1, function(x) any(x > 10)), ]
  id v1 v2
2  2 15 32
4  4 12 17
5  5  7 11

要查看正在进行的操作,x > 10会为每一行返回一个逻辑向量(通过apply()指示每个元素是否大于10. all()返回TRUE如果输入向量的所有元素都是TRUEFALSE否则。any()如果任何元素,则返回TRUE输入中的TRUEFALSE如果全部为FALSE

然后我使用apply()调用

产生的逻辑向量
> apply(df[, -1], MARGIN = 1, function(x) all(x > 10))
[1] FALSE  TRUE FALSE  TRUE FALSE
> apply(df[, -1], MARGIN = 1, function(x) any(x > 10))
[1] FALSE  TRUE FALSE  TRUE  TRUE

到子集df(如上所示)。

答案 1 :(得分:6)

这可以使用带有边距1的apply来完成,这会将函数应用于每一行。检查给定行的函数是

function(row) {all(row > 10)}

所以提取行本身的方法是

df[apply(df, 1, function(row) {all(row > 10)}),]

答案 2 :(得分:2)

一个选项是逐行循环(例如使用apply)并使用anyall,如其他两个答案中所建议的那样。但是,对于大型数据帧,这可能效率低下。

矢量化方法是使用rowSums来确定符合条件的每一行中的值的数量,并根据该值进行过滤。

当过滤到一切都至少为10的行时,这与过滤到不超过10的值的数量为0的情况相同:

df[rowSums(df[,-1] <= 10) == 0,]
#   id v1 v2
# 2  2 15 32
# 4  4 12 17

同样,rowSums可以很容易地用于计算任何超过10的行:

df[rowSums(df[,-1] > 10) > 0,]
#   id v1 v2
# 2  2 15 32
# 4  4 12 17
# 5  5  7 11

输入较大时加速很明显:

set.seed(144)
df <- matrix(sample(c(1, 10, 20), 3e6, replace=TRUE), ncol=3)
system.time(df[apply(df[, -1], MARGIN = 1, function(x) all(x > 10)), ])
#    user  system elapsed 
#   1.754   0.156   2.102 
system.time(df[rowSums(df[,-1] <= 10) == 0,])
#    user  system elapsed 
#    0.04    0.01    0.05 

答案 3 :(得分:1)

dplyr 等价物如下

library(dplyr)

#ANY
df %>% rowwise() %>%
  filter(any(across(starts_with("v"), ~ sum((. > 10)))))
# A tibble: 3 x 3
# Rowwise: 
     id    v1    v2
  <int> <dbl> <dbl>
1     2    15    32
2     4    12    17
3     5     7    11


#ALL
df %>% rowwise() %>%
  filter(all(across(starts_with("v"), ~ sum((. > 10)))))

# A tibble: 2 x 3
# Rowwise: 
     id    v1    v2
  <int> <dbl> <dbl>
1     2    15    32
2     4    12    17