用特定值筛选出(1)所有行和(2)任意列大于该值的子集

17

我有一个数据框,它有一个id列和一些(可能很多)带有值的列,例如 'v1'、'v2':

df <- data.frame(id = c(1:5), v1 = c(0,15,9,12,7), v2 = c(9,32,6,17,11))
#   id v1 v2
# 1  1  0  9
# 2  2 15 32
# 3  3  9  6
# 4  4 12 17
# 5  5  7 11
  1. 如何提取所有值均大于某个特定值(比如10)的行?

    #   id v1 v2
    # 2  2 15 32
    # 4  4 12 17
    
  2. 如何提取至少有一个值大于10的行:

  3. #   id v1 v2
    # 2  2 15 32
    # 4  4 12 17
    # 5  5  7 11
    
4个回答

23
请查看函数all()any(),分别回答你问题的第一部分和第二部分。可以使用apply()函数在行或列上运行函数(MARGIN=1是行,MARGIN=2是列等等)。请注意,在进行比较时,我使用apply()df[, -1]上忽略了id变量。
> 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

第二部分:

> 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 则返回 TRUE,否则返回 FALSE。如果输入中有任何一个元素为 TRUE,则 any() 返回 TRUE,如果所有元素都是 FALSE,则返回 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 进行子集取值(如上所示)。


8
这可以通过使用带有margin 1的apply来实现,该函数将对每行应用一个函数。检查特定行的函数如下:
function(row) {all(row > 10)}

因此提取行本身的方法是:

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

2
等等,你想要执行 all(row[-1] > 10) 而不是考虑 id 列。或者在 df[-1] 上应用该函数。 - flodel

4

一种选项是逐行循环(例如使用apply),并使用anyall,就像其他两个答案中提出的那样。然而,这对于大型数据框来说可能效率低下。

矢量化方法是使用rowSums确定每行匹配您条件的值的数量,并基于此进行过滤。

(1)当过滤到所有值都至少为10的行时,这与过滤到行中小于或等于10的值的数量为0的情况相同:

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

(2) 同样的,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 

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接