【问题标题】:Detect sequences of ordered strings and group them using R检测有序字符串序列并使用 R 对它们进行分组
【发布时间】:2020-08-06 21:25:00
【问题描述】:

我有一个包含大约 500K 元素的字符串向量,我想为每个元素分配一个值以显示每个元素的组号。

分组标准如下:

  • 从列表顶部开始连续分配组号
  • 应为每个元素分配不同的组,除非至少有 3 个连续元素按字母升序排列,其中这些连续元素将属于一个组。

我如何在 R 中做到这一点?

例如和预期的输出:

> my_strings <- c("xx1", "1xxx", "abc.xyz", "a", "ad022", "ghj1", "kf1", "991r",
+                 "jdd", "12vd", "r34o", "z", "034mh")
> expected_output <- c(1, 2, 3, 4, 4, 4, 4, 5, 6, 7, 7, 7, 8)
> (df <- data.frame(input = my_strings, output = expected_output))
     input output
1      xx1      1
2     1xxx      2
3  abc.xyz      3
4        a      4
5    ad022      4
6     ghj1      4
7      kf1      4
8     991r      5
9      jdd      6
10    12vd      7
11    r34o      7
12       z      7
13   034mh      8

到目前为止,我尝试使用dplyr::lead 并根据两个连续元素分配顺序。我不知道如何从这里开始。

res <- as_tibble(my_strings) %>%
  mutate(after = lead(my_strings))
res$pre_group = apply(res, 1, function(x) order(c(x[1], x[2]))[2])

【问题讨论】:

    标签: r string sequence


    【解决方案1】:

    (该死,这是一个艰难的过程:-)

    tidyverse

    library(dplyr)
    df %>%
      mutate(r1 = cumsum(c(TRUE, diff(rank(input)) < 0)) + 0) %>%
      group_by(r1) %>%
      mutate(r2 = r1 + seq(0, 0.9*(n() < 3), len = n()) / n()) %>%
      ungroup() %>%
      mutate(r1 = with(list(rl = rle(r2)$lengths), rep(seq_along(rl), times = rl))) %>%
      select(-r2)
    # # A tibble: 13 x 3
    #    input   output    r1
    #    <chr>    <dbl> <int>
    #  1 xx1          1     1
    #  2 1xxx         2     2
    #  3 abc.xyz      3     3
    #  4 a            4     4
    #  5 ad022        4     4
    #  6 ghj1         4     4
    #  7 kf1          4     4
    #  8 991r         5     5
    #  9 jdd          6     6
    # 10 12vd         7     7
    # 11 r34o         7     7
    # 12 z            7     7
    # 13 034mh        8     8
    

    mutate 中冗长的with(...) 只是data.table::rleid 的内联版本。)

    data.table

    library(data.table)
    as.data.table(df)[
    , r1 := cumsum(c(TRUE, diff(rank(input)) < 0)) + 0 ][
    , r1 := r1 + seq(0, 0.9*(.N < 3), len = .N), by = .(r1) ][
    , r1 := rleid(r1) ]
    

    如果你想稍微模糊一下R-方言的线条,那么

    library(data.table)
    library(magrittr)
    as.data.table(df) %>%
      .[, r1 := cumsum(c(TRUE, diff(rank(input)) < 0)) + 0 ] %>%
      .[, r1 := r1 + seq(0, 0.9*(.N < 3), len = .N), by = .(r1) ] %>%
      .[, r1 := rleid(r1) ]
    

    注意事项:

    • ... + 0as.numeric(...) 的简写。这是因为data.table 在更新列时强制执行列的原始class;因为r1(没有+0)的第一个定义是integer,所以r1 的下一次重新分配返回numeric。但是,由于 data.table 保留原始类,因此数字将被强制 (truncated) 为整数,因此我的努力停止了。

    • 当一个组中有三个或更多时,seq(0, 0.9*(...)) 减少到 seq(0,0),这会导致该组无操作。 (这使用dplyrn()data.table.N 来表示组大小。)

    • 实现略有不同,因为dplyr 禁止修改分组变量; data.table 对此没有任何问题。 (我不确定哪个方向是正确的或更好的......)

    【讨论】:

    • 哇!这很聪明!谢谢你,r2evans。想知道为什么在第一个 mutate 行中需要 +0
    • 是的,这是一个小问题...data.table 强制上课,所以没有它r1 就是integer。在下一行中,因为我正在逐行 执行操作,所以返回值将是numeric,这与data.table 抱怨(并且失败)的integer 足够不同。这是安全的,我同意它的 (data.table) 逻辑和意图(ifelse 是违反此安全性的一个很好的 base-R 示例)。所以+0 是将整行包装在as.numeric(...) 中的另一个快捷方式。
    【解决方案2】:

    不如 r2evans',但似乎也给出了结果。

    x <- my_strings
    n <- length(x)
    c(FALSE,x[-1L] > x[-n]) &
    c(FALSE,FALSE,x[-1L][-1L] > x[-n][-(n-1)]) &
    c(FALSE,FALSE,FALSE,x[-1L][-1L][-1L] > x[-n][-(n-1)][-(n-2)])
    
    (lead(x, 1) > x & lead(x,2) > lead(x,1)) |
      (lag(x, 1) < x & lead(x,1) > x) |
      (lag(x, 1) < x & lag(x,2) < lag(x,1)) -> condition
    
    condition[is.na(condition)] <- FALSE # remove NAs
    
    #to visualize
    tibble(lag(x,2), lag(x,1), x, lead(x,1), lead(x,2), condition)
    
    # There may be a better way than a loop
    cur_class <- 0
    classes <- integer(n)
    for(i in 1:(n)){
      if(!condition[i]){ #not in a sequence
        cur_class <- cur_class + 1
        classes[i] <- cur_class
      } else if(!condition[i-1]){ #first of a sequence
        cur_class <- cur_class + 1
        classes[i] <- cur_class
      } else{ #mid-sequence
        classes[i] <- cur_class
      }
    }
    
    tibble(x, classes, condition*1L)
    
    # A tibble: 13 x 3
    #   x       classes `condition * 1L`
    #  <chr>     <dbl>            <int>
    # 1 xx1           1                0
    # 2 1xxx          2                0
    # 3 abc.xyz       3                0
    # 4 a             4                1
    # 5 ad022         4                1
    # 6 ghj1          4                1
    # 7 kf1           4                1
    # 8 991r          5                0
    # 9 jdd           6                0
    # 10 12vd          7                1
    # 11 r34o          7                1
    # 12 z             7                1
    # 13 034mh         8                0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-19
      • 2018-12-23
      • 2018-08-08
      • 2016-04-20
      • 2015-06-20
      • 2021-01-05
      • 2022-12-21
      • 1970-01-01
      相关资源
      最近更新 更多