【问题标题】:Accessing list element using grep使用 grep 访问列表元素
【发布时间】:2017-09-02 14:21:54
【问题描述】:

如何使用 grep 输出直接访问列表项? 到目前为止,我发现了一种间接的方法,它涉及取消列出列表和 grep 输出:

list1 <- list(c("Group1", "Group2", "Group3"))
list2 <- list(c("GroupA", "GroupB", "GroupC"))
list.all <- c(list1,list2)               

以下代码有效,但我正在寻找 unlist() 的替代方法

idx <- unlist(lapply(list.all, function(x) grepl("Group1", x)))
unlist(list.all)[idx]

按预期返回“Group1”。

我正在寻找的语法方面 - 但不起作用 - 是通过以下方式直接访问列表元素:

list.all[[id.index]]

但这显然返回 > invalid subscript type 'list'

任何想法都将不胜感激!

【问题讨论】:

    标签: r list indexing lapply


    【解决方案1】:

    好的,所以我对此进行了一些思考。不幸的是,我没有什么简单的东西(就像你要求的那样),但我已经把它分成了几种不同的方式,并认为它会很好分享。当然,如果你想要简单的话,你可以将其中任何一个变成一个函数。

    另外,为了它的价值,我这样做是为了自学如何使用列表中的匹配文本,这就是为什么它倾向于成为顶部的小人物(也许)。

    数据

    list1 <- list(c("Group1", "Group2", "Group3"))
    list2 <- list(c("GroupA", "GroupB", "GroupC"))
    
    list3 <- list(rep(paste0("Group", 1:1e5), 2))
    list4 <- list(rep(paste0("Group", LETTERS), ceiling(2e5 / 26)))
    
    list.12 <- c(list1,list2)
    list.34 <- c(list3,list4)
    

    返回向量的选项

    选项 1(您的选择):

    idx <- unlist(lapply(list.12, function(x) grepl("Group1", x, fixed = T)))
    unlist(list.12)[idx]
    [1] "Group1"
    

    选项 2

    idx.list <- lapply(list.12, grepl, pattern = "Group1", fixed = TRUE)
    match.list <- Map(`[`, list.12, idx.list)
    unlist(match.list)
    [1] "Group1"
    

    选项 3

    unlist(list.12)[(grep("Group1", unlist(list.12), fixed = TRUE))]
    [1] "Group1"
    

    选项 4

    unlist(list.12)[(stri_detect_fixed(unlist(list.12), "Group1"))]
    [1] "Group1"
    

    以及小名单的基准:

    Unit: microseconds
      expr    min     lq     mean  median     uq      max neval
     opt_1  7.001  8.305 153.6345  8.7790  9.668 1454.531    10
     opt_2 12.234 12.433  14.6120 13.0425 14.481   26.645    10
     opt_3  3.744  3.861   5.7395  4.0010  5.509   18.314    10
     opt_4  4.511  4.763   7.1403  5.1635  6.047   18.205    10
    

    输出list.34 以上所有选项

    [1] "Group1"    "Group10"   "Group11"   "Group12"   "Group13"   "Group14"  
    [7] "Group15"   "Group16"   "Group17"   "Group18"   "Group19"   "Group100" 
    [13] "Group101"  "Group102"  "Group103"  "Group104"  "Group105"  "Group106" 
    [19] "Group107"  "Group108"  "Group109"  "Group110"  "Group111"  "Group112"
    ...........
    [997] "Group1885" "Group1886" "Group1887" "Group1888"
    [ reached getOption("max.print") -- omitted 21224 entries ]
    

    和基准(它们的价值):

    Unit: milliseconds
      expr      min       lq     mean   median       uq      max neval
     opt_1 20.28270 21.59880 24.63574 22.97677 25.07883 34.28110    10
     opt_2 15.65542 17.01358 19.29739 17.47097 18.95579 28.15225    10
     opt_3 21.87411 23.17457 25.59646 24.06683 25.01356 40.57871    10
     opt_4 31.78084 34.60584 38.84164 38.00180 42.79115 50.28889    10
    

    所以看起来选项 2 使用更大的数据进行了最佳缩放。但是输出说明了为什么在使用grepgrepl 或其他使用正则表达式的函数时必须小心。

    返回向量的“更安全”选项

    因此,我认为包含一些“更安全”的选项可能会有所帮助。而且,更安全的是,我的意思是它们只会匹配文本“Group1”。它们也不会匹配“Group11...”

    安全选项 1

    unlist(list.12)[match(unlist(list.12), "Group1", nomatch = F)]
    [1] "Group1"
    

    安全选项 2

    unlist(list.12)[unlist(list.12) %in% "Group1"]
    [1] "Group1"
    

    安全选项 3

    idx.list <- lapply(list.12, grepl, pattern = "\\bGroup1\\b")
    match.list <- Map(`[`, list.12, idx.list)
    unlist(match.list)
    [1] "Group1"
    

    以及“更安全”或精确单词匹配选项的基准:

    Unit: microseconds
       expr    min     lq    mean  median     uq    max neval
     safe_1  3.063  3.124  3.9562  3.7135  4.172  7.157    10
     safe_2  2.897  3.089  5.8281  3.3700  4.436 26.729    10
     safe_3 27.978 28.564 34.1458 28.9110 30.869 75.457    10
    

    输出 list.34 用于输出向量的所有更安全的选项

    [1] "Group1" "Group1"
    

    这个输出是预期的,因为"Group1" 中只有两个list.34

    list.34 的基准,以获得更安全的选项。

    Unit: milliseconds
       expr       min        lq      mean    median        uq       max neval
     safe_1  13.79823  14.45908  17.40700  15.07796  23.18696  23.95939    10
     safe_2  15.61516  17.61696  21.76075  21.15972  25.17552  28.91110    10
     safe_3 120.27222 125.82341 129.01684 129.11074 131.15969 141.02159    10
    

    返回列表的选项

    最后,我认为将匹配项保持为列表形式可能也很有用。所以,我用了几种方法,并且也对它们进行了基准测试。请注意,选项 1 和 4 返回相同的结果,选项 2 和 3 返回大约。同样的结果。

    列出选项 1

    idx.list <- lapply(list.12, grepl, pattern = "\\bGroup1\\b", fixed = F)
    match.list <- Map(`[`, list.12, idx.list)
    match.list[lapply(match.list, length) > 0]
    [[1]]
    [1] "Group1"
    

    只返回匹配的元素

    列出选项 2

    Filter(function(x) "Group1" %in% x, list.12)
    [[1]]
    [1] "Group1" "Group2" "Group3"
    

    返回包含匹配元素的向量

    列出选项 3

    list.12[which(unlist(list.12) %in% "Group1")]
    [[1]]
    [1] "Group1" "Group2" "Group3"
    

    还返回包含匹配元素的向量

    列出选项 4

    list4_ind <- Map(`%in%`, list.12[which(unlist(list.12) %in% "Group1")], "Group1")
    Map(`[`, list.12[which(unlist(list.12) %in% "Group1")], list4_ind)
    [[1]]
    [1] "Group1"
    

    与选项 1 一样,它只返回匹配的元素

    列表选项的基准:

    Unit: microseconds
           expr    min     lq     mean  median     uq      max neval
     list_opt_1 37.837 38.943  48.4543 46.3615 48.831   85.498    10
     list_opt_2  8.474  9.749 243.2209 11.2840 12.538 2330.869    10
     list_opt_3  3.384  3.876   4.9735  4.2705  5.051   11.233    10
     list_opt_4 22.056 22.209  27.1948 25.8670 29.827   36.355    10
    

    输出列表的所有选项的 list.34 的输出:

    选项 1 和 4

    [[1]]
    [1] "Group1" "Group1"
    

    选项 2 & 3(实际上,选项 2 不会向列表返回空的第二个元素,仅返回 list1):

    [[1]]
       [1] "Group1"    "Group2"    "Group3"    "Group4"    "Group5"    "Group6"   
       [7] "Group7"    "Group8"    "Group9"    "Group10"   "Group11"   "Group12"  
      [13] "Group13"   "Group14"   "Group15"   "Group16"   "Group17"   "Group18"
      ..........
      [985] "Group985"  "Group986"  "Group987"  "Group988"  "Group989"  "Group990" 
      [991] "Group991"  "Group992"  "Group993"  "Group994"  "Group995"  "Group996" 
      [997] "Group997"  "Group998"  "Group999"  "Group1000"
      [ reached getOption("max.print") -- omitted 199000 entries ]
    
    [[2]]
    NULL
    

    再次返回包含匹配项的整个向量。

    和基准:

    Unit: milliseconds
           expr        min         lq       mean     median         uq        max neval
     list_opt_1 120.899425 121.931426 128.245115 130.018640 131.247377 136.823413    10
     list_opt_2   1.365003   2.398965   3.554334   2.499366   2.560078   9.524567    10
     list_opt_3  11.169013  11.987195  16.755645  15.267181  21.299495  24.538944    10
     list_opt_4  27.388212  32.125164  40.104322  40.680335  47.248335  48.679709    10
    

    总结:

    • 如果您想在不使用正则表达式的情况下返回精确(安全)模式的向量,那么安全选项 1 的缩放效果似乎最好。

    • 如果您想使用正则表达式返回一个向量,安全选项 3 的缩放比例最佳以获取更多数据。与常规选项 2 相同。

    • 如果您想要一个仅返回匹配项的列表,请使用列表选项 4。

    • 如果您想要使用正则表达式仅包含匹配项的列表,请使用列表选项 1。

    • 如果要返回包含匹配in 列表的向量,列表选项2 中的Filter 非常快。

    【讨论】:

    • 这是一个非常有价值的答案,因为它突出了我没有见过的错误匹配的风险。非常感谢@Andrew!列表选项 3 似乎有一个小缺陷 - 如果我在 list.all list.all[which(unlist(list.all) %in% "Group1")] 上运行它,它只会返回第一个向量:[[1]] [1] "Group1" "Group2" "Group3" [[2]] NULL [[3]] NULL 我不确定如何使用未列出的索引对列表进行子集化。有什么想法吗?
    • 一个很好的观点——我需要更新我的答案 b/c 不能像宣传的那样工作。不知道是怎么漏掉的!我的意思是更新它以包括rapply。无论如何,@AgileBean,您要寻找的输出是什么?您想要包含"Group1"list.all 元素还是只需要匹配项?
    • 亲爱的@Andrew,最初我只想要匹配项,但不知道如何转换 unlist(list.all) 以便它可以找到列表条目。但是在看到您使用 list.all 的有趣示例后,我还想找到一种方法来检索 which(unlist(list.all)) 的索引信息,仅翻译为列表中的索引。
    【解决方案2】:

    受到@Andrew 详尽回答的启发,我发现了将他对安全方法的考虑考虑在内的最简单的方法,同时还能保持最大的可读性恕我直言。不过,主要功劳归@Andrew。我的回答只适用于那些不想阅读他有趣分析的人。这是我的建议:

    idx <- unlist(list.all) %in% "Group1"
    unlist(list.all)[idx]
    
    "Group1" "Group1" "Group1"
    

    或者使用 dpyr 更短更优雅:

    unlist(list.all) %in% "Group1" %>% unlist(list.all)[.]
    

    我知道这仍然没有摆脱 unlist() - 我最初的目标 - 但避免了通过 grepls 的部分匹配获得错误结果的风险。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-18
      • 1970-01-01
      • 2020-05-24
      • 2012-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多