【问题标题】:Clustering rows by group based on column value根据列值按组对行进行聚类
【发布时间】:2018-06-21 08:56:16
【问题描述】:

我有以下几点:

df <- data.frame(ID = c(1,1,1,1,1,1,1,1,1,1,2,2,2),
             Obs = c(0,1, 1, 0, 1,0,0, 1, 1, 1, 0,0,1))

我想要这个:

df <- data.frame(ID = c(1,1,1,1,1,1,1,1,1,1,2,2,2),
             Obs = c(0,1, 1, 0, 1,0,0, 1, 1, 1, 0,0,1),
             Cluster = c(0,1,1,1,2,2,2,3,3,3,0,0,1))

如何使用 dplyr 获得 'Cluster' 列,在该列中我必须对 1 的数量进行排序,直到第一个 0 出现?

连续的 0 必须保持该值,直到出现新的值。

编辑

如果有很多列,我该怎么做?

假设我有 99 个 obs 列,我想创建 99 个集群,每列一个。像这样:

df <- data.frame(ID = c(1,1,1,1,1,1,1,1,1,1,2,2,2),
Obs1 = c(0,1, 1, 0, 1,0,0, 1, 1, 1, 0,0,1),
Obs2 = c(0,0, 0, 1, 1,1,0, 1, 0, 1, 0,0,1),
ClusterObs1 = c(0,1,1,1,2,2,2,3,3,3,0,0,1),
ClusterObs2 = c(0,0,0,1,1,1,1,2,2,3,0,0,1))

【问题讨论】:

    标签: r dplyr seq


    【解决方案1】:

    这是一个使用rle的选项:

    df %>% 
      group_by(ID) %>% 
      mutate(clust = with(rle(Obs), rep(cumsum(values == 1), lengths)))
    # # A tibble: 13 x 4
    # # Groups:   ID [2]
    # ID   Obs Cluster clust
    # <dbl> <dbl>   <dbl> <int>
    # 1    1.    0.      0.     0
    # 2    1.    1.      1.     1
    # 3    1.    1.      1.     1
    # 4    1.    0.      1.     1
    # 5    1.    1.      2.     2
    # 6    1.    0.      2.     2
    # 7    1.    0.      2.     2
    # 8    1.    1.      3.     3
    # 9    1.    1.      3.     3
    # 10    1.    1.      3.     3
    # 11    2.    0.      0.     0
    # 12    2.    0.      0.     0
    # 13    2.    1.      1.     1
    

    这是它的主要部分:

    rle(df$Obs)
    #Run Length Encoding
    #  lengths: int [1:8] 1 2 1 1 2 3 2 1
    #  values : num [1:8] 0 1 0 1 0 1 0 1
    

    这告诉您每个 1 或 0 在 Obs 列中的长度(我暂时忽略 ID 分组)。

    我们现在需要的是累计计算有多少次 1 的延伸,然后我们简单地对值为 1 的地方进行 cumsum:

    with(rle(df$Obs), cumsum(values == 1))
    #[1] 0 1 1 2 2 3 3 4
    

    到目前为止一切顺利,现在我们需要重复这些值的次数与这些延伸的长度一样多,因此我们使用来自 rle 的 replengths 信息:

    with(rle(df$Obs), rep(cumsum(values == 1), lengths))
    # [1] 0 1 1 1 2 2 2 3 3 3 3 3 4
    

    最后,我们通过 ID 组来做这件事。


    如果您需要为不同的obs-columns创建多个cluster-columns,您可以轻松地按照以下方式进行:

    df %>% 
      group_by(ID) %>% 
      mutate_at(vars(starts_with("Obs")), 
                funs(cluster= with(rle(.), rep(cumsum(values == 1), lengths))))
    
    # # A tibble: 13 x 7
    # # Groups:   ID [2]
    # ID  Obs1  Obs2 ClusterObs1 ClusterObs2 Obs1_cluster Obs2_cluster
    # <dbl> <dbl> <dbl>       <dbl>       <dbl>        <int>        <int>
    # 1    1.    0.    0.          0.          0.            0            0
    # 2    1.    1.    0.          1.          0.            1            0
    # 3    1.    1.    0.          1.          0.            1            0
    # 4    1.    0.    1.          1.          1.            1            1
    # 5    1.    1.    1.          2.          1.            2            1
    # 6    1.    0.    1.          2.          1.            2            1
    # 7    1.    0.    0.          2.          1.            2            1
    # 8    1.    1.    1.          3.          2.            3            2
    # 9    1.    1.    0.          3.          2.            3            2
    # 10    1.    1.    1.          3.          3.            3            3
    # 11    2.    0.    0.          0.          0.            0            0
    # 12    2.    0.    0.          0.          0.            0            0
    # 13    2.    1.    1.          1.          1.            1            1
    

    df 在哪里:

    df <- data.frame(ID = c(1,1,1,1,1,1,1,1,1,1,2,2,2), Obs1 = c(0,1, 1, 0, 1,0,0, 1, 1, 1, 0,0,1), Obs2 = c(0,0, 0, 1, 1,1,0, 1, 0, 1, 0,0,1), ClusterObs1 = c(0,1,1,1,2,2,2,3,3,3,0,0,1), ClusterObs2 = c(0,0,0,1,1,1,1,2,2,3,0,0,1))
    

    【讨论】:

    • 嗯,我尝试使用rle 获得解决方案,但我似乎从未成功过。您能否简要介绍一下with(rle(Obs), rep(cumsum(values == 1), lengths)) 中发生的情况?
    • 那很好,我之前也尝试过使用 rle 但没有成功。另外,如何适应在多列下运行?
    • @LAP,我添加了一些解释,希望现在更清楚
    • 我们不能只做group_by(ID) %&gt;% mutate(Cluster = cumsum(Obs==1 &amp; lag(Obs)==0)) 吗?
    • @mtoto,也许吧。但是如果第一个 obs 是 1 呢?
    【解决方案2】:

    这是一个非常有趣的问题,所以这里有一个 data.table 解决方案:

    # Packages used
    library(data.table)
    library(magrittr)
    
    # Setup
    setDT(df)
    df[, Obs := as.integer(Obs)]
    
    # Calculations
    df[, Cluster := cumsum(!Obs), by = ID] %>%
      .[, Cluster := Cluster - rowid(Obs) * !Obs, by = rleid(Obs)] %>%
      .[, Cluster := frank(Cluster, ties.method = "dense") - 1L, by = ID]
    
    df
        ID Obs Cluster
     1:  1   0       0
     2:  1   1       1
     3:  1   1       1
     4:  1   0       1
     5:  1   1       2
     6:  1   0       2
     7:  1   0       2
     8:  1   1       3
     9:  1   1       3
    10:  1   1       3
    11:  2   0       0
    12:  2   0       0
    13:  2   1       1
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-05
      • 2022-01-23
      • 2019-02-12
      • 2019-02-25
      • 2014-09-03
      • 2017-07-05
      • 1970-01-01
      相关资源
      最近更新 更多