【问题标题】:R: Count all combinations in a list of strings (Specific Order)R:计算字符串列表中的所有组合(特定顺序)
【发布时间】:2018-05-21 05:06:18
【问题描述】:

我正在尝试计算由“>”分隔的大量字符列表中的所有序列,但只计算彼此直接相邻的组合。

例如给定字符向量:

[1]Social>PaidSearch>PaidSearch>PaidSearch>PaidSearch>PaidSearch>PaidSearch>PaidSearch>PaidSearch>PaidSearch>PaidSearch>OrganicSearch>OrganicSearch>OrganicSearch
[2]Referral>Referral>Referral

我可以运行以下行来检索包含 2 个字符的所有组合:

split_fn <- sapply(p , strsplit , split = ">", perl=TRUE)

split_fn <- sapply(split_fn, function(x) paste(head(x,-1) , tail(x,-1) , sep = ">") )

返回:

[[1]]

 [1] "Social>PaidSearch"           "PaidSearch>PaidSearch"       "PaidSearch>PaidSearch"       "PaidSearch>PaidSearch"       "PaidSearch>PaidSearch"      
 [6] "PaidSearch>PaidSearch"       "PaidSearch>PaidSearch"       "PaidSearch>PaidSearch"       "PaidSearch>PaidSearch"       "PaidSearch>PaidSearch"      
[11] "PaidSearch>OrganicSearch"    "OrganicSearch>OrganicSearch" "OrganicSearch>OrganicSearch"

[[2]]

[1] "Referral>Referral" "Referral>Referral"

我的数据中所有可能的 2 个字符序列(按顺序拆分)

我知道想要 3 个字符的所有可能结果。

例如

"Social>PaidSearch>PaidSearch" "PaidSearch>PaidSearch>PaidSearch"..."Referral>Referral>Referral"

尝试使用

unlist(lapply(strsplit(p, split = ">"), function(i) combn(sort(i), 3, paste, collapse='>')))

但它会返回所有组合,包括那些不直接跟随的组合。

我也不希望它返回第一行中的最后一个值与第二行中的第一个值等的组合。

【问题讨论】:

    标签: r count combinations sapply strsplit


    【解决方案1】:

    让我们从创建一些数据开始:

    set.seed(1)
    
    data <- lapply(1:3, function(i) sample(LETTERS[1:3], rpois(1, 6), re = T))
    data <- sapply(data, paste, collapse = ">")
    
    data
    #> [1] "B>B>C>A"           "C>B>B>A>A>A>C>B>C" "C>C>B>C>C>A"
    

    鉴于问题,将这些数据视为一个列表是有意义的 我们用分隔符&gt;分割元素后得到的向量:

    strsplit(data, ">")
    #> [[1]]
    #> [1] "B" "B" "C" "A"
    #> 
    #> [[2]]
    #> [1] "C" "B" "B" "A" "A" "A" "C" "B" "C"
    #> 
    #> [[3]]
    #> [1] "C" "C" "B" "C" "C" "A"
    

    现在,问题的核心是找到给定的所有连续序列 单个向量的长度。一旦我们能做到这一点,申请就很简单了 我们拥有的数据列表;转换回分隔格式将 也很简单。

    考虑到这个目标,我们可以创建一个函数来提取 序列;在这里,我们只是遍历每个元素并提取 给定长度的所有序列到一个列表中:

    seqs <- function(x, length = 2) {
      if (length(x) < length)
        return(NULL)
      k <- length - 1
      lapply(seq_len(length(x) - k), function(i) x[i:(i + k)])
    }
    

    我们现在可以在之后的数据中应用该函数 将分隔字符拆分为向量以获得结果。我们还需要一个额外的 sapplypaste 来将数据转换回我们开始使用的分隔格式:

    lapply(strsplit(data, ">"), function(x) {
      sapply(seqs(x, 3), paste, collapse = ">")
    })
    #> [[1]]
    #> [1] "B>B>C" "B>C>A"
    #> 
    #> [[2]]
    #> [1] "C>B>B" "B>B>A" "B>A>A" "A>A>A" "A>A>C" "A>C>B" "C>B>C"
    #> 
    #> [[3]]
    #> [1] "C>C>B" "C>B>C" "B>C>C" "C>C>A"
    

    进一步,为了同时得到多个长度的序列,我们可以再增加一层迭代:

    lapply(strsplit(data, ">"), function(x) {
      unlist(sapply(c(2, 3), function(n) {
        sapply(seqs(x, n), paste, collapse = ">")
      }))
    })
    #> [[1]]
    #> [1] "B>B"   "B>C"   "C>A"   "B>B>C" "B>C>A"
    #> 
    #> [[2]]
    #>  [1] "C>B"   "B>B"   "B>A"   "A>A"   "A>A"   "A>C"   "C>B"   "B>C"  
    #>  [9] "C>B>B" "B>B>A" "B>A>A" "A>A>A" "A>A>C" "A>C>B" "C>B>C"
    #> 
    #> [[3]]
    #> [1] "C>C"   "C>B"   "B>C"   "C>C"   "C>A"   "C>C>B" "C>B>C" "B>C>C" "C>C>A"
    

    reprex package (v0.2.0) 于 2018 年 5 月 21 日创建。

    【讨论】:

    • 效果很好。我还有一个问题,只是为了让我的生活更轻松一些,这样我以后就不必进行任何合并了。是否对这段代码进行了简单的修改,可以让我获得 2 个字符和 3 个字符的序列。即“B>B”、“B>C”、“B>B>C”等
    • 嗯.. 当然:最后只需要一个额外的sapply 层。我会相应地进行编辑。
    • 非常感谢您的帮助。非常感谢。
    • @JamieAllan 没问题,欢迎来到 SO!当您的问题解决后,您也可以accept an answer.
    • 我刚刚尝试增加我的列表大小,因为它不会让我超过 10。事实上,我有 24,000,000 行我最终需要应用代码。我原始帖子中的代码能够循环遍历所有 24M 行并返回一个统计所有序列的表格。根据我使用的模型的顺序,有一个确定的序列数量。在第一种情况下(2 个字符),有 81 种可能的组合。在第二种情况下(2-3 个字符),有 810 种可能的组合。随着高阶指数增加。我认为这段代码可以工作,但我想不会大规模
    【解决方案2】:

    使用stringr 包(或一般的正则表达式)。

    library(stringr)
    str_extract_all(p, "(\\w+)>(\\w+)>(\\w+)")
    

    有重叠,但代码可以简化。

    str_extract_all_overlap <- function (x) {
      extractions <- character()
      x_curr <- x
      extr <- str_extract(x_curr, "(\\w+)>(\\w+)>(\\w+)")
      i = 1
      while (!is.na(extr)) {
        extractions[i] <- extr 
        x_curr <- str_replace(x_curr, "\\w+", replacement = "")
        extr <- str_extract(x_curr, "(\\w+)>(\\w+)>(\\w+)")
        i = i + 1
      }
      return(extractions)
    }
    
    lapply(p, str_extract_all_overlap)
    

    【讨论】:

    • 我认为在考虑重叠时应该有 12 个提取的块。这只会得到没有重叠的 4 个离散部分。
    • 这是一个好的开始。但是,thelatemail 是正确的,它只返回 5 个离散部分,第一个字符 4 个,第二个字符 1 个。我也需要出现重叠。
    • 已在编辑中解决,但可能有更简单的解决方案,代码行数更少。
    • 感谢您的意见
    【解决方案3】:

    您还可以将第二个 sapply 中的 paste-command 调整为:

    paste(head(x,-2), head(tail(x,-1),-1), tail(x,-2) , sep = ">")
    

    您的完整代码现在应该如下所示:

    split_fn <- sapply(p , strsplit , split = ">", USE.NAMES = FALSE)
    
    split_fn <- sapply(split_fn, function(x) paste(head(x,-2), head(tail(x,-1),-1), tail(x,-2), sep = ">") )
    

    结果:

    > split_fn
    [[1]]
     [1] "Social>PaidSearch>PaidSearch"              "PaidSearch>PaidSearch>PaidSearch"          "PaidSearch>PaidSearch>PaidSearch"         
     [4] "PaidSearch>PaidSearch>PaidSearch"          "PaidSearch>PaidSearch>PaidSearch"          "PaidSearch>PaidSearch>PaidSearch"         
     [7] "PaidSearch>PaidSearch>PaidSearch"          "PaidSearch>PaidSearch>PaidSearch"          "PaidSearch>PaidSearch>PaidSearch"         
    [10] "PaidSearch>PaidSearch>OrganicSearch"       "PaidSearch>OrganicSearch>OrganicSearch"    "OrganicSearch>OrganicSearch>OrganicSearch"
    
    [[2]]
    [1] "Referral>Referral>Referral"
    

    【讨论】:

    • 不错。 head(tail(x,-1),-1) 行是我最初寻找的。我试图分别修改头部和尾部,但没有想到这一点。但是,上面的解决方案是一种更简洁的方法,并且可以通过将其与我之前的结果合并来获得额外的信息。感谢您的输入
    猜你喜欢
    • 1970-01-01
    • 2023-01-18
    • 2017-07-07
    • 2012-10-30
    • 2021-04-02
    • 2020-08-03
    • 1970-01-01
    • 1970-01-01
    • 2016-03-22
    相关资源
    最近更新 更多