【问题标题】:R: Extract part of string with varying lengthR:提取具有不同长度的字符串的一部分
【发布时间】:2017-02-22 09:35:14
【问题描述】:

我有一个字符串列表(非常大,数百万行),我想从中提取特定部分。

我首先在分号处拆分字符串,然后提取到特定部分。它变得有点复杂,因为有时一行中有 3 段,有时 4 段。但我感兴趣的部分总是最后一个和倒数第二个部分。

示例代码:

dataStr = c("secAlways;  secExtr1; secExtr2",
            "secSometimes;  secAlways;  secExtr1; secExtr2",
            "secSometimes;  secAlways;  secExtr1; secExtr2",
            "secAlways;  secExtr1; secExtr2",
            "secAlways;  secExtr1; secExtr2",
            "secAlways;  secExtr1; secExtr2",
            "secSometimes;  secAlways;  secExtr1; secExtr2",
            "secAlways;  secExtr1; secExtr2",
            "secAlways;  secExtr1; secExtr2",
            "secAlways;  secExtr1; secExtr2")

splStr <- strsplit(dataStr, ";")
extr1 <- list()
extr2 <- list()

for (i in 1:length(splStr)) {
  extr1[i] <- head( tail(splStr[[i]], n=2), n=1)
  extr2[i] <- tail(splStr[[i]], n = 1)
}

它可以工作,但是太慢了。我将不胜感激有关如何使这更快的任何想法。我怀疑这可能是通过apply 完成的,但我无法理解它。


如果问题可能与this 问题重复,则会提出该问题。我认为这有点不同,因为我想提取最后两个元素并且部分的数量不同。另外,我还没有得到vapply 的解决方案来处理我的真实示例。

【问题讨论】:

标签: r string performance apply strsplit


【解决方案1】:

我认为你最好在这里使用正则表达式:

sub(".+; (.+?); (.+?)$", "\\2", dataStr)

这将抓取最后一个项目。

sub(".+; (.+?); (.+?)$", "\\1", dataStr)

这将在最后一个项目之前抓取项目。

【讨论】:

  • 在我的真实样本中将速度提高了 27 倍。
【解决方案2】:

word 来自 stringr 解决方案,

stringr::word(dataStr, -2, -1,  sep = ';')

然后您可以strsplit 将它们作为 2 个不同的单词,即

do.call(rbind, strsplit(trimws(word(dataStr, -2, -1,  sep = ';')), '; '))
#      [,1]       [,2]      
# [1,] "secExtr1" "secExtr2"
# [2,] "secExtr1" "secExtr2"
# [3,] "secExtr1" "secExtr2"
# [4,] "secExtr1" "secExtr2"
# [5,] "secExtr1" "secExtr2"
# [6,] "secExtr1" "secExtr2"
# [7,] "secExtr1" "secExtr2"
# [8,] "secExtr1" "secExtr2"
# [9,] "secExtr1" "secExtr2"
#[10,] "secExtr1" "secExtr2"

【讨论】:

  • 我认为这不起作用:两个部分仍然集中在同一个单元格中。
  • 已编辑。我还使用trimws 删除前导/尾随空格
  • 很好,现在可以使用 - 谢谢!不过,正则表达式解决方案要快得多。
  • 是的,我预计会这样,但我有点 regex-blind 所以我倾向于尽可能避免它们
【解决方案3】:

我们可以使用stringivapply 来加快速度

library(stringi)
vapply(stri_split(dataStr, regex=';\\s*'), function(x) tail(x, 2), character(2))

【讨论】:

  • 这适用于此处的示例,但在我的真实示例中失败:Error in vapply(splStr, function(x) tail(x, 2), character(2)) : values must be length 2, but FUN(X[[5487]]) result is length 1
  • @ulima2_不清楚当你的病例少于2个时,怎么办
【解决方案4】:

这样做可能更快:

str_list <- lapply(splStr, tail, 2)
do.call(rbind, str_list)

      [,1]         [,2]       
 [1,] "  secExtr1" " secExtr2"
 [2,] "  secExtr1" " secExtr2"
 [3,] "  secExtr1" " secExtr2"
 [4,] "  secExtr1" " secExtr2"
 [5,] "  secExtr1" " secExtr2"
 [6,] "  secExtr1" " secExtr2"
 [7,] "  secExtr1" " secExtr2"
 [8,] "  secExtr1" " secExtr2"
 [9,] "  secExtr1" " secExtr2"
[10,] "  secExtr1" " secExtr2"

【讨论】:

  • 是一样的。 splStr 是拆分后的 dataStr。
  • 正则表达式方法实际上更快(至少在这个示例集上)。
【解决方案5】:
> str_list <- lapply(dataStr, tail, 2)

> do.call(rbind, str_list)


      [,1]                                           
[1,] "secAlways;  secExtr1; secExtr2"               
[2,] "secSometimes;  secAlways;  secExtr1; secExtr2"
[3,] "secSometimes;  secAlways;  secExtr1; secExtr2"
[4,] "secAlways;  secExtr1; secExtr2"               
[5,] "secAlways;  secExtr1; secExtr2"               
[6,] "secAlways;  secExtr1; secExtr2"               
[7,] "secSometimes;  secAlways;  secExtr1; secExtr2"
[8,] "secAlways;  secExtr1; secExtr2"               
[9,] "secAlways;  secExtr1; secExtr2"               
[10,] "secAlways;  secExtr1; secExtr2"  

我不确定这是否有效?

【讨论】:

    【解决方案6】:

    假设最后一段和倒数第二段的字符数始终相同,这可以通过stringi 库通过以下方式实现。

    我还假设您想要两个列表作为输出。

    library(stringi)
    
    dataStr = c("secAlways;  secExtr1; secExtr2",
                "secSometimes;  secAlways;  secExtr1; secExtr2",
                "secSometimes;  secAlways;  secExtr1; secExtr2",
                "secAlways;  secExtr1; secExtr2",
                "secAlways;  secExtr1; secExtr2",
                "secAlways;  secExtr1; secExtr2",
                "secSometimes;  secAlways;  secExtr1; secExtr2",
                "secAlways;  secExtr1; secExtr2",
                "secAlways;  secExtr1; secExtr2",
                "secAlways;  secExtr1; secExtr2")
    
    extr1 <- as.list(stringi::stri_sub(dataStr, from=-18, to=-11))
    extr2 <- as.list(stringi::stri_sub(dataStr, from= -8))
    

    【讨论】:

    • 不幸的是,它们对我来说不是相同数量的案例,抱歉这里有误导性的例子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    • 2015-09-19
    • 2020-12-31
    相关资源
    最近更新 更多