在包含字符串列表的数据框中检查字符串

时间:2019-07-11 19:38:49

标签: r

我正试图找到一种方法来在数据框中查找多个值并返回一个值。简化示例:

df1 <- read.table(text="chk1    chk2    chk3    value
xx  aa;bb;cc    jj  1
xx;yy   dd;ee;ff    kk  2
zz  gg;hh;ii    ll;nn   3", header=T)

df2 <- read.table(text="val1    val2    val3
xx  bb  jj
xx  dd  kk
yy  ee  kk
zz  hh  jj
", header=T)

val1的{​​{1}}中查找值val2val3df2,并从df1返回值。

所需结果:

df1

尝试df2 <- read.table(text=" val1 val2 val3 value xx bb jj 1 xx dd kk 2 yy ee kk 2 zz hh jj NA ") 并在各行中循环,无法使其正常工作。

3 个答案:

答案 0 :(得分:0)

这里是一种可能性:

library(tidyverse)
df3 <- df2 %>% rowwise %>% 
       mutate(rowmatch=which(grepl(val1, df1$chk1) & 
                             grepl(val2, df1$chk2) & 
                             grepl(val3, df1$chk3))[1], 
              value=df1$value[rowmatch])

结果:

# A tibble: 4 x 5
  val1  val2  val3  rowmatch value
  <chr> <chr> <chr>    <int> <int>
1 xx    bb    jj           1     1
2 xx    dd    kk           2     2
3 yy    ee    kk           2     2
4 zz    hh    jj          NA    NA

注意:

  • [1]是为了确保仅使用匹配的第一行。
  • 请注意,尽管在此示例中rowmatchvalue是相同的,但这仅是因为df1$value等于行号。
  • tibble的行为类似于data.frame,但是如果您真的更喜欢数据框,请添加%>% as.data.frame

对于基数R也可以这样做并应用:

df2$rowmatch <- with(df1, apply(df2, 1, function(x) 
    which(grepl(x["val1"], chk1) & 
          grepl(x["val2"], chk2) & 
          grepl(x["val3"], chk3))[1]))

df2$value <- df1$value[df2$rowmatch]

答案 1 :(得分:0)

另一个选择是先分割值:

df1 <- df1 %>% 
    splitstackshape::cSplit("chk1", ";", fixed = TRUE, direction = "long", drop = FALSE, type.convert = FALSE) %>% 
    splitstackshape::cSplit("chk2", ";", fixed = TRUE, direction = "long", drop = FALSE, type.convert = FALSE) %>% 
    splitstackshape::cSplit("chk3", ";", fixed = TRUE, direction = "long", drop = FALSE, type.convert = FALSE) 

然后使用加入

答案 2 :(得分:0)

您还可以使用两个嵌套的for循环来完成此操作。逻辑是采用df2的第一行,然后开始遍历df1的行以查看df2$val1是否匹配df1$chkdf2$val2是否匹配{{1} }和df1$chk2匹配df2$val3。如果每列至少有一个匹配项,那么我认为所有值都是匹配项。需要注意的是,如果df1$chk3没有唯一的行,则df2的最后一个匹配行将被写入df1。但这可以通过找到匹配项后立即跳出循环来更改。

df2

输出:

for (i in 1:nrow(df2)) {
  for (j in 1:nrow(df1)) {
    # Take i-th row and split by ;. Result is a vector of strings against
    # which we'll use match.
    i.split <- strsplit(as.character(unlist(df1[j, , drop = TRUE][-4])), ";")
    # Pairwise check columns from df1 and df2.
    all.ok <- all(mapply(FUN = function(x, y) {
      any(x %in% y)
    }, x = i.split, y = as.list(df2[i, 1:3])
    ))

    if (all.ok) {
      # If a match is found, write the value to df2.
      df2[i, "value"] <- df1[j, "value"]
    }
  }
}