【问题标题】:Index consecutive duplicates in vector索引向量中的连续重复项
【发布时间】:2019-06-24 13:07:36
【问题描述】:

获取重复 # 次的所有元素的索引的最佳方法是什么?我想识别重复超过 2 次的元素。 rle()rleid() 都提示我需要的值,但两种方法都没有直接给我索引。

我想出了这个代码:

t1 <- c(1, 10, 10, 10, 14, 37, 3, 14, 8, 8, 8, 8, 39, 12)

t2 <- lag(t1,1)
t2[is.na(t2)] <- 0
t3 <- ifelse(t1 - t2 == 0, 1, 0)
t4 <- rep(0, length(t3))
for (i in 2:length(t3)) t4[i] <- ifelse(t3[i] > 0, t3[i - 1] + t3[i], 0)

which(t4 > 1)

返回:

[1]  4 11 12 

这些是我需要的值。

有没有更合适的 R 函数?

【问题讨论】:

    标签: r vector


    【解决方案1】:

    data.table 的一个选项。当 n = 2 时,没有真正的理由使用它来代替 lag/shift,但对于较大的 n,这将避免您创建大量新的滞后向量。

    library(data.table)
    
    which(rowid(rleid(t1)) > 2)
    # [1]  4 11 12
    

    解释:

    rleid 将为每个相等值的“运行”生成一个唯一值,rowid 将标记每个元素有多少元素“进入”运行。您想要的是超过 2 个“进入”运行的元素。

    data.table(
      t1,
      rleid(t1),
      rowid(t1))
    
    #     t1 V2 V3
    #  1:  1  1  1
    #  2: 10  2  1
    #  3: 10  2  2
    #  4: 10  2  3
    #  5: 14  3  1
    #  6: 37  4  1
    #  7:  3  5  1
    #  8: 14  6  2
    #  9:  8  7  1
    # 10:  8  7  2
    # 11:  8  7  3
    # 12:  8  7  4
    # 13: 39  8  1
    # 14: 12  9  1
    

    编辑:如果,如this question提出的示例,没有两次运行(甚至长度为1“运行”)具有相同的值(或者如果您不关心重复项是否彼此相邻),你可以改用which(rowid(t1) &gt; 2)。 (这在 cmets 中被 Frank 指出)

    希望这个例子能阐明差异

    a <- c(1, 1, 1, 2, 2, 1)
    which(rowid(a) > 2)
    # [1] 3 6
    which(rowid(rleid(a)) > 2)
    # [1] 3
    

    【讨论】:

    • 至少在 OP 的示例中,不需要 rleid which(data.table::rowid(t1) &gt; 2) 也给出了正确的结果。 (...我不确定 OP 的“连续”是指重复多次还是立即重复)
    【解决方案2】:

    您可以使用dplyr::lagdata.table::shift(注意,shift 的默认值是滞后的,所以shift(t1, 1) 等于shift(t1, 1, type = "lag")

    which(t1 == lag(t1, 1) & lag(t1, 1) == lag(t1, 2))
    [1]  4 11 12
    # Or
    which(t1 == shift(t1, 1) & shift(t1, 1) == shift(t1, 2))
    [1]  4 11 12
    

    如果您需要它来扩展多个副本,您可以执行以下操作(感谢@IceCreamToucan 的提示):

    n <- 2
    df1 <- sapply(0:n, function(x) shift(t1, x))
    which(rowMeans(df1 == df1[,1]) == 1)
    [1]  4 11 12
    

    【讨论】:

    • 啊,很好的点。我会更新答案——谢谢@IceCreamToucan!
    【解决方案3】:

    这通常是rle有用的情况,即

    v1 <- rle(t1)
    i1 <- seq_along(t1)[t1 %in% v1$values[v1$lengths > 2]]
    i2 <- t1[t1 %in% v1$values[v1$lengths > 2]]
    tapply(i1, i2, function(i) tail(i, -2))
    #$`8`
    #[1] 11 12
    
    #$`10`
    #[1] 4
    

    您可以取消列出并将其作为向量获取,

    unlist(tapply(i1, i2, function(i) tail(i, -2)))
    #81 82 10 
    #11 12  4
    

    data.table 包中还有一个函数叫rleid,我们可以使用,

    unlist(lapply(Filter(function(i) length(i) > 2, split(seq_along(t1), data.table::rleid(t1))),
                                                                        function(i) tail(i, -2)))
     #2 71 72 
     #4 11 12 
    

    【讨论】:

      【解决方案4】:

      涉及rle() 的另一种可能性可能是:

      pseudo_rleid <- with(rle(t1), rep(seq_along(values), lengths))
      which(ave(t1, pseudo_rleid, FUN = function(x) seq_along(x) > 2) != 0)
      
      [1]  4 11 12
      

      【讨论】:

      • 好主意!我认为你应该重命名它。 rleid 可能会混淆,尤其是使用 data.table::rleid 函数的其他答案。
      • @Sotos 我将它重命名为“pseudo_rleid”以保留它的本质,但是,不会引起混淆:)
      • 啊……好多了。强迫症恢复正常水平:D
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-04
      • 1970-01-01
      • 1970-01-01
      • 2021-10-04
      • 2014-11-13
      相关资源
      最近更新 更多