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

时间:2019-04-22 14:19:21

标签: r dplyr data.table tidyverse

我有一个包含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的任何解决方案,但是任何其他可以提出的创意都请分享。

6 个答案:

答案 0 :(得分: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,]

答案 1 :(得分: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

编辑

根据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

答案 2 :(得分: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)

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

答案 3 :(得分: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列。)

答案 4 :(得分:0)

这是一个数据表解决方案:

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)]

答案 5 :(得分: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