两个列表中向量元素的部分交集

5

我有一个类似于这样的列表:

mylist <- list(PP = c("PP 1", "OMITTED"),
           IN01 = c("DID NOT PARTICIPATE", "PARTICIPATED", "OMITTED"),                     
           RD1 = c("YES", "NO", "NOT REACHED", "INVALID", "OMITTED"),
           RD2 = c("YES", "NO", "NOT REACHED", "NOT AN OPTION", "OMITTED"),
           LOS = c("LESS THAN 3", "3 TO 100", "100 TO 500", "MORE THAN 500", "LOGICALLY NOT APPLICABLE", "OMITTED"),
           COM = c("BAN", "SBAN", "RAL"), 
           VR1 = c("WITHIN 30", "WITHIN 200", "NOT AVAILABLE", "OMITTED"),                         
           INF = c("A LOT", "SOME", "LITTLE OR NO", "NOT APPLICABLE", "OMITTED"),               
           IST = c("FULL-TIME", "PART-TIME", "FULL STAFFED", "NOT STAFFED", "LOGICALLY NOT APPLICABLE", "OMITTED"),
           CMP = c("ALL", "MOST", "SOME", "NONE", "LOGICALLY NOT APPLICABLE", "OMITTED"))

我有另一个类似的列表:

如下所示:

matchlist <- list("INVALID", c("INVALID", "OMITTED OR INVALID"),
c("INVALID", "OMITTED"), "OMITTED", c("NOT REACHED", "INVALID", "OMITTED"),
c("LOGICALLY NOT APPLICABLE", "INVALID", "OMITTED"),
c("LOGICALLY NOT APPLICABLE", "INVALID", "OMITTED OR INVALID"),
c("Not applicable", "Not stated"), c("Not reached", "Not administered/missing by design", "Presented but not answered/invalid"),
c("Not administered/missing by design", "Presented but not answered/invalid"),
"OMITTED OR INVALID",
c("LOGICALLY NOT APPLICABLE", "OMITTED OR INVALID"),
c("NOT REACHED", "OMITTED"),
c("NOT APPLICABLE", "OMITTED"), 
c("LOGICALLY NOT APPLICABLE", "OMITTED"),
c("LOGICALLY NOT APPLICABLE", "NOT REACHED", "OMITTED"),
"NOT EXCLUDED", c("Default", "Not applicable", "Not stated"), c("Valid Skip", "Not Reached", "Not Applicable", "Invalid", "No Response"),
c("Not administered", "Omitted"),
c("NOT REACHED", "INVALID RESPONSE", "OMITTED"),
c("INVALID RESPONSE", "OMITTED"))

正如您所看到的,matchlist 中的一些向量与 mylist 中的向量部分匹配。在某些情况下,matchlist 中的向量与 mylist 中的向量部分有完全匹配。例如,在 mylist 中,RD1 的最后一个值与 matchlist 的第五个组件中的向量匹配,但是 RD2 不匹配它,尽管存在公共值。在 mylist 中的 RD2 值(“NOT REACHED”、“NOT AN OPTION”、“OMITTED”)连在一起并按照此顺序不匹配 matchlist 中任何向量中的值。对于 mylist 中的 COM 值也是如此。

我想实现的目标是将 mylist 中每个向量中的元素与 matchlist 中的每个向量进行比较,提取共同和匹配 matchlist 中值顺序相同的值,并将它们存储在另一个列表中。期望的结果应该如下所示:

$PP
[1] "OMITTED"

$IN01
[1] "OMITTED"

$RD1
[1] "NOT REACHED" "INVALID" "OMITTED"

$RD2
character(0)

$LOS
[1] "LOGICALLY NOT APPLICABLE" "OMITTED"

$COM
character(0)

$VR1
[1] "OMITTED"

$INF
[1] "NOT APPLICABLE" "OMITTED"

$IST
[1] "LOGICALLY NOT APPLICABLE" "OMITTED"

$CMP
[1] "LOGICALLY NOT APPLICABLE" "OMITTED"

我尝试过的方法:

使用intersect

lapply(mylist, function(i) {
  intersect(i, lapply(matchlist, function(i) {i}))
})

它仅返回matchlist每个向量中的最后一个值(“OMITTED”)。

通过%in%使用match

lapply(mylist, function(i) {
  i[which(i %in% matchlist)]
})

只针对 RD1 ("INVALID", "OMITTED") 返回所需结果,对于其余情况仅返回最后一个值("OMITTED"),除了COM是正确的。
使用 mapplyintersect:
mapply(intersect, mylist, matchlist)

返回一个混合了几乎所有内容的长列表,包括不应该存在的组合,以及长度不相等的警告。请问有人可以帮忙吗?

2
RD1 为例,当您有多个匹配项时,您的期望是什么?最长的一个(按矢量长度)?在这里,mapply 不是您想要的,它执行 intersect(mylist[[1]], matchlist[[1]]),然后执行 intersect(mylist[[2]], matchlist[[2]]),以此类推。 - r2evans
@r2evans - 不太确定我是否理解了,但是mylist中的字符串应该与matchlist中的整个向量匹配。也就是说,RD1中的值应该仅与mylist中的第五个向量(c("NOT REACHED", "INVALID", "OMITTED"))匹配,而不是其他任何东西。 - panman
2
RD1 匹配 matchlist 的第 1、2、4、7、14、15 和 22 个索引中的一个单词;它匹配第 3、6、13、16 和 21 个索引中的两个单词;以及第 5 个索引中的三个单词。很明显,您想要其中最长的那个,是吗? - r2evans
4个回答

4

这里有一个使用unlistmatchlist的简单解决方案:

lapply(mylist, function(x) x[x %in% unlist(matchlist)])

输出(新列表):

$PP
[1] "OMITTED"

$IN01
[1] "OMITTED"

$RD1
[1] "NOT REACHED" "INVALID"     "OMITTED"    

$LOS
[1] "LOGICALLY NOT APPLICABLE" "OMITTED"                 

$COM
character(0)

$VR1
[1] "OMITTED"

$INF
[1] "NOT APPLICABLE" "OMITTED"       

$IST
[1] "LOGICALLY NOT APPLICABLE" "OMITTED"                 

$CMP
[1] "LOGICALLY NOT APPLICABLE" "OMITTED"                 

太好了!非常感谢!您能否解释一下为什么在这种情况下取消列表matchlist是有效的,我不确定是否理解它,它会从matchlist生成单个向量。 - panman
使用?unlist应该会有所帮助!给定一个列表,它将返回一个包含列表中所有单独组件的单个向量。然后,您可以应用%in%来返回一个逻辑向量,指示第一个向量的哪些组件在第二个(即未列出的列表)中。 - Carles Mitjans
谢谢您的解释。 - panman

3

简单明了的写作

lapply(mylist, intersect, unlist(matchlist))

同样适用。


哇,这真的将OP的想法转化为了一个可行且简短的解决方案。 - Andre Elrico
非常感谢!正如我已经向Carles Mitjans提出的问题,您能否请解释一下为什么在这种情况下取消列出matchlist是有效的?我不确定我理解它,它会从matchlist生成一个单一的向量。 - panman
正如Charles告诉你的那样:在R控制台中键入“?unlist”。阅读它。然后执行Info末尾的示例。阅读和理解手册是成为R专业人员的方法。 - Andre Elrico
另外,@panman,我认为,鉴于期望的输出,您目前描述的问题略微令人困惑,可以以不同的方式陈述,清楚地说明为什么“unlist”是正确的选择。您只需要关心mylist[[i]]中的哪些元素在matchlist中存在,这意味着没有必要计算出现次数(因此使用intersect)或跟踪它们在matchlist中的位置(因此使用unlist)。intersect还保留了顺序。 - Julius Vainora

2
lapply(mylist, function(i) {
  unlist(sapply(i,function(x){if(any(grepl(paste0("^",x,"$"),matchlist))){x}}))
})

我在字符串前后添加了"\b",因为它可以导致找到"NOT"。使用grepl肯定不是最好的方法,正如其他答案所示 :)


非常感谢!这是一个有趣的解决方案。 - panman
仅添加 \\b 是不够的。你应该在两侧添加 ^$。(除了这个解决方案令人印象深刻但比其他解决方案差。) - Andre Elrico
@AndreElrico 谢谢,确实我应该加上这两个。是的,显然不如其他的,但我认为这仍然是值得的。 - denis
这绝对是值得的!\\b 应该分别被替换为 ^$。否则,"\\bNO\\b" 将匹配 "RAND NO RAND" - Andre Elrico

1
有一些非常简单/好的答案,但它们似乎都依赖于unlist。我假设您需要保留matchlist中的分组,因此对它们进行取消列表操作没有意义。这里是一种解决方案,它可以在不使用取消列表的情况下工作,使用双重lapply循环,就像您开始做的那样:
out <- lapply(mylist, function(this) {
  mtch <- lapply(matchlist, intersect, this)
  wh <- which.max(lengths(mtch))
  if (length(wh)) mtch[[wh]] else character(0)
})
str(out)
# List of 9
#  $ PP  : chr "OMITTED"
#  $ IN01: chr "OMITTED"
#  $ RD1 : chr [1:3] "NOT REACHED" "INVALID" "OMITTED"
#  $ LOS : chr [1:2] "LOGICALLY NOT APPLICABLE" "OMITTED"
#  $ COM : chr(0) 
#  $ VR1 : chr "OMITTED"
#  $ INF : chr [1:2] "NOT APPLICABLE" "OMITTED"
#  $ IST : chr [1:2] "LOGICALLY NOT APPLICABLE" "OMITTED"
#  $ CMP : chr [1:2] "LOGICALLY NOT APPLICABLE" "OMITTED"

它始终返回具有最多匹配项的向量,但如果有(某种方式)多个匹配项,则会保留自然顺序并返回所述长匹配项中的第一个。(问题是:“which.max是否保留自然顺序?”我认为它会,但尚未验证。)
更新:添加了约束条件,不仅需要匹配列表向量的存在和顺序,还需要没有插入的单词。例如,如果如评论中建议的那样,mylist $ RD1具有"BLAH",则不再与matchlist [[5]]匹配。
检查一个向量的完美有序子集是否与另一个向量匹配有些棘手(因此不是代码高尔夫冠军),并且通常由于我们没有易于确定子集,而导致规模扩展性差。在此情况下,此实现执行一些嵌套的*apply函数...
(注:在评论中建议$RD1应返回character(0),但它确实有"INVALID"matchlist的单个长度组件之一相匹配,因此它应该匹配,只是不匹配更长的一个。)
out <- lapply(mylist, function(this) {
  ind <- lapply(matchlist, function(a) which(this == a[1]))
  perfectmatches <- mapply(function(ml, allis, this) {
    length(ml) * any(sapply(allis, function(i) all(ml == this[ i + seq_along(ml) - 1 ])))
  }, matchlist, ind, MoreArgs = list(this=this))
  if (any(perfectmatches) > 0) {
    wh <- which.max(perfectmatches)
    return(matchlist[[wh]])
  } else return(character(0))
})
str(out)
# List of 9
#  $ PP  : chr "OMITTED"
#  $ IN01: chr "OMITTED"
#  $ RD1 : chr "INVALID"
#  $ LOS : chr [1:2] "LOGICALLY NOT APPLICABLE" "OMITTED"
#  $ COM : chr(0) 
#  $ VR1 : chr "OMITTED"
#  $ INF : chr [1:2] "NOT APPLICABLE" "OMITTED"
#  $ IST : chr [1:2] "LOGICALLY NOT APPLICABLE" "OMITTED"
#  $ CMP : chr [1:2] "LOGICALLY NOT APPLICABLE" "OMITTED"

尝试运行 mylist <- list(A = 1:3); matchlist <- list(1, 2:3); 然后 out 只会输出 2 3。我怀疑这不是预期的输出结果。 - Julius Vainora
1
这是我对问题的理解与你不同的一种方式,这是有意为之的差异。最终我也不知道,或许 panman 会回来澄清一些事情。 - r2evans
抱歉,我不在办公室。实际上,我应该向大家道歉,这个问题可能没有被很好地表达。尝试将mylist中的RD1更改为c(“YES”,“NO”,“NOT REACHED”,“BLAH”,“INVALID”,“OMITTED”)。然后结果应该返回character(0),因为它在任何一个matchlist向量中相同顺序的值都没有匹配,正如问题所述。对于造成的困惑,我深感歉意。 - panman
1
是的,你需要编辑你的问题并添加这个限制条件,因为我觉得很容易假设干扰者(“BLAH”)的存在不会有问题。 - r2evans
谢谢r2evans,我已经编辑了问题。希望现在更清楚了。 - panman
非常感谢您的时间和努力,这真的帮了很多忙! - panman

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