【问题标题】:Complex long to wide reshape algorithm复杂的长到宽重塑算法
【发布时间】:2021-06-25 09:02:50
【问题描述】:

我有一个问题,我需要根据 ID1 和 ID2 将长格式数据表重塑为具有不重叠条目的宽格式。逻辑相当复杂,依赖于 3 列(“Seq”、“ID1”和“ID2”)。

如果属于 ID1 的 Value_1 与 ID2“重叠”,则应将其相加,反之亦然,但仅适用于不同的 ID。

请参阅下面的输入示例和输出,希望能够澄清它。

输入:

df <- structure(list(Seq = c(9143L, 916L, 9293L, 9301L, 9302L, 9304L, 
9305L, 9306L, 9307L, 931L, 9311L), ID1 = c("ID1_1", "ID1_1", 
NA, "ID1_2", "ID1_2", NA, "ID1_3", "ID1_3", "ID1_3", "ID1_4", 
"ID1_4"), value_1 = c(30L, 30L, NA, 30L, 30L, NA, 30L, 30L, 30L, 
50L, 50L), ID2 = c(NA, NA, "ID2_1", "ID2_2", "ID2_3", "ID2_4", 
"ID2_4", "ID2_4", "ID2_4", "ID2_4", "ID2_5"), value_2 = c(NA, 
NA, 33L, 200L, 46L, 58L, 58L, 58L, 58L, 58L, 46L)), class = "data.frame", row.names = c(NA, 
-11L))

输出:

(例如最后一行,value_1 = 80,因为 30+50 来自 ID1_3 和 ID1_4 的值的总和)

【问题讨论】:

  • 我看不出问题的关键在哪里。在我看来,你有一个逻辑```` df1 % mutate(value_2 = if(ID_1 == ID_2, value_1 + value_2, value_2))````。你能更好地阐述逻辑吗?
  • 公平地说,他从来没有提到过枢轴,虽然他使用的术语很宽很长,但可能不知道枢轴x函数。
  • 我在表格中澄清了一点,希望对您有所帮助
  • 您写道“逻辑非常复杂”,但您提供的唯一描述是“......如果它'重叠'则求和......”其中没有定义'重叠'...... ;) 请更彻底地描述逻辑,并带我们通过一个具有代表性的案例,而不是将解密留给我们。干杯

标签: r dataframe dplyr reshape tidyr


【解决方案1】:

不像上面那么简洁,但还是一个Base R解决方案:

# Function to calculate the aggregate value: .agg_func => function() 
.agg_func <- function(df, id_col, value_col){
  sbst <- subset(
    df, 
    !(is.na(df[,id_col])) & !(duplicated(df[,id_col])),
    select = c(id_col, value_col)
  )
  return(sum(sbst[,value_col], na.rm = TRUE))
}

# Function to group data by ids: .grouping_func => function() 
.grouping_func <- function(df, id_col){
  r_l_e <- rle(df[,id_col])
  rle_id <- rep(seq_along(r_l_e$values), times = r_l_e$lengths)
  return(c(0, diff(rle_id)) != 0)
}

# Group the data: grpd_df => data.frame 
grpd_df <- transform(
  df, 
  grp = cumsum(
    apply(
      vapply(
        names(df)[startsWith(names(df), "ID")],
        function(x).grouping_func(df, x),
        logical(nrow(df))
        ), 
      1,
      all
    )
  )
)  

# Split-apply-combine the aggregate function to the grouped data: 
data.frame(do.call(rbind, lapply(with(grpd_df, split(grpd_df, grp)), function(s){
        data.frame(
          Seq = toString(s$Seq), 
          value_1 = .agg_func(s, "ID1", "value_1"), 
          value_2 = .agg_func(s, "ID2", "value_2")
        )
      }
    )
  ), row.names = NULL, stringsAsFactors = FALSE
)

【讨论】:

    【解决方案2】:

    首先,我非常喜欢 AnilGoyal 的解决方案。可以看到需要开始使用data.table包了。

    话虽如此,我正在研究一种没有 data.table 的 dplyr 方法,这显然更加冗长。此外,我花了一段时间才弄清楚如何处理重复值。乘以 changei 列(0 或 1)删除了重复项。以下是我的方法:

    df %>% 
      mutate_if(is.numeric, replace_na, 0) %>% 
      mutate_if(is.character, replace_na, "NA") %>% 
      mutate(
        change1 = ID1 != lag(ID1, default = "Start"),
        value_1 = value_1 * change1,
    
        change2 = ID2 != lag(ID2, default = "Start"),
        value_2 = value_2 * change2,
    
        change = cumsum(change1 & change2)
      ) %>% 
      group_by(change) %>% 
      summarise(
        Seq = toString(Seq),
        value_1 = sum(value_1, na.rm = T),
        value_2 = sum(value_2, na.rm = T)
      ) %>% 
      ungroup()
    

    结果是:

    df
    #   change Seq                               value_1 value_2
    #    <int> <chr>                               <dbl>   <dbl>
    # 1      1 9143, 916                              30       0
    # 2      2 9293                                    0      33
    # 3      3 9301, 9302                             30     246
    # 4      4 9304, 9305, 9306, 9307, 931, 9311      80     104
    

    【讨论】:

      【解决方案3】:

      我使用了 data.table 包中的rleid() 函数,这是一个计算运行长度编码的迷人函数。这样做

      library(data.table)
      library(dplyr)
      df %>% 
        mutate(d = cumsum( c(0, diff(rleid(ID1))) != 0 & c(0, diff(rleid(ID2))) != 0),
               value_1 = value_1 * c(1, diff(rleid(ID1))),
               value_2 = value_2 * c(1, diff(rleid(ID2)))) %>% group_by(d) %>%
        summarise(Seq = toString(Seq),
                  value_1 = sum(value_1, na.rm = T),
                  value_2 = sum(value_2, na.rm = T)) %>%
        ungroup() %>% select(-d)
      
      # A tibble: 4 x 3
        Seq                               value_1 value_2
        <chr>                               <int>   <int>
      1 9143, 916                              30       0
      2 9293                                    0      33
      3 9301, 9302                             30     246
      4 9304, 9305, 9306, 9307, 931, 9311      80     104
      

      旧答案

      df %>% group_by(d = cumsum( c(0, diff(rleid(ID1))) != 0 & c(0, diff(rleid(ID2))) != 0)) %>%
        summarise(Seq = toString(Seq),
                  value_1 = sum(unique(value_1), na.rm = T),
                  value_2 = sum(unique(value_2), na.rm = T)) %>%
        ungroup() %>% select(-d)
      

      【讨论】:

      • 虽然您已经接受了答案,但我对其进行了一些修改以处理不同 id 但在同一组中的两个值相同的情况。 unique() 不会正确处理这些。检查修改后的答案
      • 谢谢,是的,我必须做 df %>%arrange(ID1,ID2) %>% .... ,我认为它有效 :)
      • 哦!我假设你的 df 已经安排好了
      • 实际上,我遇到了 ID1 和 ID2 不再连续并且排序不起作用的问题。但是感谢所有的帮助!
      • 我通过在 ID1 和 ID2 列上使用 igraph() 分组来修复它,并将其用于 'd' 变量。
      猜你喜欢
      • 2013-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多