【问题标题】:Count transitions between different states in several time steps (columns)计算几个时间步长(列)中不同状态之间的转换
【发布时间】:2017-09-25 10:40:11
【问题描述】:

我有以下数据集:

data <- data.frame(id = 1:7,
                   t1 = c("AV1", "AV1", "AZ", "AV1", "AV1","AV1","AV2"),
                   t2 = c("AV2", NA, "AV3", "AV2", "AV2",NA, "AV3"),
                   t3 = c("AZ", "AV2", "AV4", "AZ", "AZ","AV4","AV4"))

每一行代表一个单独的“id”,具有几个不同时间步长的状态(值)(列“t1”-“t3”):

id t1  t2   t3
1 AV1  AV2  AZ
2 AV1  NA   AV2
3 AZ   AV3  AV4
4 AV1  AV2  AZ
5 AV1  AV2  AZ
6 AV1  NA   AV4
7 AV2  AV3  AV4

我想计算不同的转换,“从”一个时间步中的值,“到”后续时间步中的一个值,汇总整个数据集:

 from            to                 count 
  AV1            AV2                 4             
  AV2            AZ                  3              
  AZ             AV3                 1             
  AV3            AV4                 2           
  AV1            AV4                 1
  AV2            AV3                 1

因此,“计数”表示特定转换发生的次数。例如,AV1 到 AV2 出现 4 次,AV2 到 AZ 出现 3 次。 NAs 被排除在外。

非常感谢!

【问题讨论】:

    标签: r


    【解决方案1】:

    为避免对列进行硬编码,您可以将数据重新整形为长格式 (melt)。使用headtail,以及每个“id” (by = id),以对齐连续时间步长中的值。计算每个唯一转换 (by = .(from, to)) 的行数 (.N)

    library(data.table)
    setDT(data)
    d <- melt(data ,id.vars = "id", na.rm=TRUE)
    d[ , .(from = head(value, -1), to = tail(value, -1)), by = id][ , .N, by = .(from, to)]
    #   from  to N
    # 1: AV1 AV2 4
    # 2: AV2  AZ 3
    # 3:  AZ AV3 1
    # 4: AV3 AV4 2
    # 5: AV1 AV4 1
    # 6: AV2 AV3 1
    

    一个类似的base替代方案,尽管转换连接:

    d <- na.omit(reshape(data, varying = list(2:4), direction = "long"))
    as.data.frame(table(unlist(by(d, d$id, function(dat) paste(head(dat$t1, -1), tail(dat$t1, -1), sep = " - ")))))
    
    #        Var1 Freq
    # 1 AV1 - AV2    4
    # 2 AV1 - AV4    1
    # 3 AV2 - AV3    1
    # 4  AV2 - AZ    3
    # 5 AV3 - AV4    2
    # 6  AZ - AV3    1
    

    【讨论】:

      【解决方案2】:

      这是一个适用于任意数量列的通用方法。我们找到列的所有对组合(按索引)。我们使用它们来索引原始 df 中的列,并将它们放入列表中。粘贴元素,进行一些清理 (trimws(gsub('NA', '', do.call(paste, a[i1[,x]]))),然后使用 table 函数,我们会得到您预期的结果。将其包装在 as.data.frame 中会给出您预期的输出结构。

      i1 <- combn(seq_along(a[-1])+1, 2)
      
      final_d <- as.data.frame(table(unlist(lapply(seq(ncol(i1)), function(x) {
                     v1 <- trimws(gsub('NA', '', do.call(paste, a[i1[,x]]))); 
                     grep('\\s', v1, value = TRUE)
                     }))))
      

      给出,

           Var1 Freq
      1 AV1 AV2    4
      2 AV1 AV4    1
      3  AV1 AZ    3
      4 AV2 AV3    1
      5 AV2 AV4    1
      6  AV2 AZ    3
      7 AV3 AV4    2
      8  AZ AV3    1
      9  AZ AV4    1
      

      或者说完全一样,

      setNames(data.frame(do.call('rbind', strsplit(as.character(final_d$Var1),' ',fixed=TRUE)), 
                          final_d$Freq), 
                          c('from', 'to', 'freq.'))
      
        from  to freq.
      1  AV1 AV2     4
      2  AV1 AV4     1
      3  AV1  AZ     3
      4  AV2 AV3     1
      5  AV2 AV4     1
      6  AV2  AZ     3
      7  AV3 AV4     2
      8   AZ AV3     1
      9   AZ AV4     1
      

      【讨论】:

        【解决方案3】:

        其中一种方法可能是

        library(dplyr)
        
        d1 <- data %>% group_by(t1, t2) %>% filter(!is.na(t1) & !is.na(t2)) %>% summarise(n()) %>% `colnames<-`(c("from", "to", "weight")) %>% as.data.frame()
        d2 <- data %>% group_by(t2, t3) %>% filter(!is.na(t2) & !is.na(t3)) %>% summarise(n()) %>% `colnames<-`(c("from", "to", "weight")) %>% as.data.frame()
        d3 <- data %>% group_by(t1, t3) %>% filter(!is.na(t1) & !is.na(t3)) %>% summarise(n()) %>% `colnames<-`(c("from", "to", "weight")) %>% as.data.frame()
        #final data
        df <- rbind(d1, d2, d3) %>% group_by(from, to) %>% summarise(weight=sum(weight)) %>% as.data.frame()
        

        【讨论】:

        • 嗨@Prem,感谢您的回复。但是,我注意到结果中有更多的关系,例如 AV1>AZ 和 AV2>AV4 不是直接存在而是间接存在。
        • @MohammadZahrawy df 拥有您所期望的所有关系。 BTW Sotos 的答案很棒,可以在任意数量的列上复制。
        【解决方案4】:

        编辑
        避免硬编码列的tidyverse 方法可以遵循与@Henrik 出色的公认答案类似的方法。在这种情况下,我使用lead 函数来组合相邻的值,然后再获取结果的count

        library(tidyverse)
        data %>% 
          gather(key, value, -id) %>% filter(!is.na(value)) %>% group_by(id) %>% 
          transmute(from = value, to = lead(value)) %>% filter(!is.na(to)) %>% ungroup() %>%
          count(from, to)
        
        #> # A tibble: 6 x 3
        #>    from    to     n
        #>   <chr> <chr> <int>
        #> 1   AV1   AV2     4
        #> 2   AV1   AV4     1
        #> 3   AV2   AV3     1
        #> 4   AV2    AZ     3
        #> 5   AV3   AV4     2
        #> 6    AZ   AV3     1
        

        原始解决方案
        这样的事情怎么样?它不是很优雅,但我认为它会完成工作。

        library(dplyr)
        data <- tibble(id = 1:7,
                       t1 = c("AV1", "AV1", "AZ", "AV1", "AV1", "AV1", "AV2"),
                       t2 = c("AV2", NA, "AV3", "AV2", "AV2", NA, "AV3"),
                       t3 = c("AZ", "AV2", "AV4", "AZ", "AZ", "AV4", "AV4"))
        
        
        cnt1 <- data %>% filter(!is.na(t2)) %>% count(t1, t2) %>% rename(from = t1, to = t2)
        cnt2 <- data %>% filter(!is.na(t2)) %>% count(t2, t3) %>% rename(from = t2, to = t3)
        cnt3 <- data %>% filter(is.na(t2)) %>% count(t1, t3) %>% rename(from = t1, to = t3)
        
        cnt1 %>%
          bind_rows(cnt2) %>%
          bind_rows(cnt3) %>%
          group_by(from, to) %>%
          summarise(weight = sum(n))
        #> # A tibble: 6 x 3
        #> # Groups:   from [?]
        #>    from    to weight
        #>   <chr> <chr>  <int>
        #> 1   AV1   AV2      4
        #> 2   AV1   AV4      1
        #> 3   AV2   AV3      1
        #> 4   AV2    AZ      3
        #> 5   AV3   AV4      2
        #> 6    AZ   AV3      1
        

        【讨论】:

        • 谢谢。它有效,但这是一个示例,我有 53 个序列(53 列)哈哈。有没有办法让你的代码做到这一点?
        • 我认为这可能是@MohammadZahrawy 的情况!必须有更好的方法来做到这一点。希望其他人可以提供更好的解决方案!
        • @MohammadZahrawy,我添加了一个编辑以包含一个 tidyverse 选项,该选项应该适用于任意数量的列。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-02
        • 1970-01-01
        • 2017-01-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多