仅当行与特定字符串匹配时才选择行

问题描述 投票:2回答:6

我有一个包含5个数字列的数据集,比如{A,B,C,D,E},其中任何列的值可以在1到100之间变化。即,

1 <= A / B / C / D / E中的所有值<= 100

我们的数据集如下所示:

A  B  C  D  E  
1  5  7  19 2    
90 12 8  45 30  
30 10 20 50 40 #need this row  
33 11 22 55 44  
50 40 10 20 30 #and this row  
40 40 10 20 30 #not this one

我想只过滤那些包含以下5个值中的每一个的行,例如:{10,20,30,40,50}。顺序无关紧要,但5列应包含所有5个值。

所以输出应该是这样的:

A  B  C  D  E    
30 10 20 50 40  
50 40 10 20 30

我尝试使用大量的ifelse来过滤所有5列条件,但事实是我需要将这个概念应用于更复杂的问题,它们可能不是定义的否。列或甚至定义的“查找”数据集。所以任何使用dplyr,data.table,tidyverse的解决方案都非常受欢迎,但任何其他任何人都可以提出的任何创意请分享。

r dplyr data.table tidyverse
6个回答
2
投票
tb <- data.frame(A = c(1, 90, 30 ,33,50,40),
                 B = c(5,12,10,11,40,40),
                 C = c(7,8,20,22,10,10),
                 D = c(19,45,50,55,20,20),
                 E = c(2,30,40,44,30,30))

cols <- paste0(c(10,20,30,40,50), collapse = "_")

index <- apply(tb, 1, function(x) paste0(sort(x), collapse = "_") == cols)

tb[index,]

2
投票

使用来自apply基地的sum%in%R

my_vals = c(10, 20, 30, 40, 50)
df[apply(df, 1, function(row) all(my_vals %in% row)), ]

   A  B  C  D  E
3 30 10 20 50 40
5 50 40 10 20 30

这可以扩展到任意数量的列,您所要做的就是更新my_vals

Edit

基于OP关于当my_vals可能有重复元素时获取正确行的情况的注释,上面的代码可以修改有点像这样

my_vals = sort(c(10, 20, 30, 40, 40))
df[apply(df, 1, function(row) all(my_vals == sort(row))), ]

   A  B  C  D  E
6 40 40 10 20 30

1
投票

也许是这样的?

library(dplyr)

dat %>%
  rowwise() %>%
  filter(paste(sort(c(A, B, C, D, E)), collapse = ".") == "10.20.30.40.50") %>%
  ungroup()

# A tibble: 2 x 5
      A     B     C     D     E
  <int> <int> <int> <int> <int>
1    30    10    20    50    40
2    50    40    10    20    30

数据:

dat <- read.table(text = "A  B  C  D  E  
1  5  7  19 2    
90 12 8  45 30  
30 10 20 50 40
33 11 22 55 44  
50 40 10 20 30
40 40 10 20 30", header = TRUE)

注意:我不确定这是否是一个很好的方法来扩展到“更复杂的问题,它们可能不是定义的列数,甚至是定义的'查找'数据集”,因为它有点含糊不清。如果您遇到更复杂的问题,我强烈建议您构建问题以反映它。


0
投票

这是一种重新整形为长格式,过滤和重新整形的方法:

my_vals = c(10, 20, 30, 40, 50)

library(tidyr)
library(dplyr)
df %>% mutate(id = row_number()) %>%
  gather("col", "val", -id) %>%
  group_by(id) %>%
  filter(all(my_vals %in% val)) %>%
  spread(col, val)

# A tibble: 2 x 6
# Groups:   id [2]
     id     A     B     C     D     E
  <int> <int> <int> <int> <int> <int>
1     3    30    10    20    50    40
2     5    50    40    10    20    30

(当然,如果你不想要它,你可以放弃id专栏。)


0
投票

这是一个data.table解决方案:

library(data.table)

dt <- setDT(read.table(text = "A  B  C  D  E  
  1  5  7  19 2    
  90 12 8  45 30  
  30 10 20 50 40
  33 11 22 55 44  
  50 40 10 20 30
  40 40 10 20 30", header = TRUE))

dt = dt[, .SD[all(seq(10, 50, 10) %in% .SD)], by = 1:nrow(dt)]

0
投票

这是另一个选项,没有排序每一行。

我们的想法是逐列连接数据集的每一列和查找值。对于例如对于A列,使用全部5个值来过滤原始数据集。

然后,对于列B,使用上一步中数据集的每个子集使用A列中未使用的任何内容进行连接。

然后,对于C列,使用上一步中数据集的每个子集使用A和B列中未使用的任何内容进行连接。

然后,对于列D,使用上一步骤中数据集的每个子集使用A,B和C列中未使用的任何内容进行连接。

等等等等。

以下是data.table中上述想法的实现:

v <- c(10, 20, 30, 40, 40)
nm <- names(dat)

dat <- dat[.(A=unique(v)), on=.(A), nomatch=0L]

for (k in seq_along(nm)[-1L]) {
    dat <- dat[, .SD[.(unique(v[-match(.BY, v)])), 
                     on=eval(nm[k]), 
                     nomatch=0L], 
        by=eval(nm[seq_len(k)[-k]])]
}
dat

v <- c(10, 20, 30, 40, 40)的输出:

    A  B  C  D  E
1: 10 40 40 20 30
2: 40 40 10 20 30
3: 40 40 10 20 30

v <- c(10, 20, 30, 40, 50)的输出:

    A  B  C  D  E
1: 30 10 20 50 40
2: 50 40 10 20 30

数据:

library(data.table)
dat <- fread("A  B  C  D  E  
1  5  7  19 2    
90 12 8  45 30
30 10 20 50 40
33 11 22 55 44
50 40 10 20 30
40 40 10 20 30  
40 40 10 20 30    
10 40 40 20 30")     #2 dupe rows to demonstrate edge case 
© www.soinside.com 2019 - 2024. All rights reserved.