【问题标题】:Create all possible combinations of non-NA values for each group ID为每个组 ID 创建所有可能的非 NA 值组合
【发布时间】:2021-11-16 09:18:32
【问题描述】:

类似于this 的问题,但有一点不同:

给定以下数据框:

txt <- "ID    Col1    Col2    Col3    Col4
        1     6       10      NA      NA
        1     5       10      NA      NA
        1     NA      10      15      20
        2     17      25      NA      NA
        2     13      25      NA      NA
        2     NA      25      21      34
        2     NA      25      35      40"
DF <- read.table(text = txt, header = TRUE)

DF
  ID Col1 Col2 Col3 Col4
1  1    6   10   NA   NA
2  1    5   10   NA   NA
3  1   NA   10   15   20
4  2   17   25   NA   NA
5  2   13   25   NA   NA
6  2   NA   25   21   34
7  2   NA   25   35   40

我希望按组 ID 折叠行(类似于此示例中的 Col2),并且当每个组存在超过 1 个组合时,返回所有组合,如下所示:

  ID Col1 Col2 Col3 Col4
1  1    6   10   15   20
2  1    5   10   15   20
3  2   17   25   21   34
4  2   13   25   21   34
5  2   17   25   35   40
6  2   13   25   35   40

重要的是,以后我需要它来处理非数值数据。有什么建议?谢谢!

【问题讨论】:

  • 原始的第 3 行被折叠成第 1-2 行。第 6-7 行折叠成第 4-5 行,共有 4 种组合。

标签: r data.table aggregate na


【解决方案1】:

按“ID”分组,fill 其他列,ungroup 删除组属性并保留distinct

library(dplyr)
library(tidyr)
DF %>% 
    group_by(ID) %>% 
    fill(everything(), .direction = 'updown') %>%
    ungroup %>% 
    distinct(.keep_all = TRUE)

也可能是

DF %>% 
   group_by(ID) %>% 
   mutate(across(everything(), ~ replace(., is.na(.), 
           rep(.[!is.na(.)], length.out = sum(is.na(.))))))

或基于 cmets

DF %>%
   group_by(ID) %>%
   mutate(across(where(~ any(is.na(.))), ~ {
        i1 <- is.na(.)
        ind <- which(i1)
        i2 <- !i1
        if(i1[1] == 1) rep(.[i2], each = n()/sum(i2)) else 
               rep(.[i2], length.out = n())
     })) %>%
   ungroup %>% 
   distinct(.keep_all = TRUE)

-输出

# A tibble: 6 x 5
     ID  Col1  Col2  Col3  Col4
  <int> <int> <int> <int> <int>
1     1     6    10    15    20
2     1     5    10    15    20
3     2    17    25    21    34
4     2    13    25    21    34
5     2    17    25    35    40
6     2    13    25    35    40

【讨论】:

  • 谢谢!这两个选项给出不同的结果。所需的输出将是您的第二个响应的重复数据删除版本。第一个响应缺少原始帖子中第 4 行和第 7 行的折叠组合
  • @Aaron 在我发布此内容时,我怀疑您的预期是否是错字
  • 明白了,预计不是错字,谢谢!
  • 谢谢!出于我的目的,我不关心元素的排列,只要输出中存在每个组 ID 的 Col1 和 (Col3&Col4) 的所有组合
  • @Aaron 更新后的解决方案提供了您帖子中的预期。当 NA 元素的数量不同时,可能需要一些调整
【解决方案2】:

data.table 选项使用zoona.locf 来填充缺失值。

library(zoo)
library(data.table)

setDT(DF)
cols <- grep('Col', names(DF), value = TRUE)
DF[, (cols) := lapply(.SD, function(x) fcoalesce(na.locf(x, na.rm = FALSE), 
                      na.locf(x, na.rm = FALSE, fromLast = TRUE))), ID]
unique(DF)

#   ID Col1 Col2 Col3 Col4
#1:  1    6   10   15   20
#2:  1    5   10   15   20
#3:  2   17   25   21   34
#4:  2   13   25   21   34
#5:  2   13   25   35   40

【讨论】:

    【解决方案3】:

    In a comment,OP指出:

    为了我的目的,我不关心元素的排列这么久 因为每个组 ID 的 Col1 和 (Col3&Col4) 的所有组合都存在于 输出

    所以,如果我理解正确,问题不是关于折叠,而是关于创建列Col1Col2 的所有可能的非 NA 值组合,以及每个 ID 组的组合列(Col3Col4)。

    为此, 包中的 expand()nesting() 可用于创建组合。 na.omit() 删除所有包含任何 NA 的行之后

    library(dplyr)
    library(tidyr)
    DF %>% 
      group_by(ID) %>% 
      expand(Col1, Col2, nesting(Col3, Col4)) %>% 
      na.omit() %>% 
      ungroup()
    
         ID  Col1  Col2  Col3  Col4
      <int> <int> <int> <int> <int>
    1     1     5    10    15    20
    2     1     6    10    15    20
    3     2    13    25    21    34
    4     2    13    25    35    40
    5     2    17    25    21    34
    6     2    17    25    35    40
    

    这种方法也适用于非数值数据。

    编辑 1

    再想一想,我想知道输入数据集的特殊结构,即NAs 的位置:

    DF
    
      ID Col1 Col2 Col3 Col4
    1  1    6   10   NA   NA
    2  1    5   10   NA   NA
    3  1   NA   10   15   20
    4  2   17   25   NA   NA
    5  2   13   25   NA   NA
    6  2   NA   25   21   34
    7  2   NA   25   35   40
    

    在我看来,DF 似乎是由三个独立的子集构成的,第一个子集是 Col1

      ID Col1
    1  1    6
    2  1    5
    4  2   17
    5  2   13
    

    Col2的第二个

      ID Col2
    1  1   10
    4  2   25
    

    Col3Col4 的第三个

      ID Col3 Col4
    3  1   15   20
    6  2   21   34
    7  2   35   40
    

    基于这一观察,这里有一种不同的方法,它通过子集的一系列合并操作(笛卡尔连接)创建子集的所有可能组合:

    library(magrittr) # piping used her to improve readability
    list("Col1", "Col2", c("Col3", "Col4")) %>% 
      lapply(function(x) DF[c("ID", x)] %>% na.omit %>% unique) %>% 
      Reduce(merge, .)
    
      ID Col1 Col2 Col3 Col4
    1  1    6   10   15   20
    2  1    5   10   15   20
    3  2   17   25   21   34
    4  2   17   25   35   40
    5  2   13   25   21   34
    6  2   13   25   35   40
    

    在这里,lapply() 创建输入数据集的子集列表,然后使用 Reduce() 重复合并。

    编辑 2:

    在 4.1.0 版本中,R 获得了简单的本机正向管道语法 |&gt;\() 作为 function() 的简写符号。有了这个,Edit 1的代码可以重写为只使用base R(没有):

    list("Col1", "Col2", c("Col3", "Col4")) |> 
      lapply(\(x) DF[c("ID", x)] |> na.omit() |> unique()) |>
      (\(z) Reduce(merge, z))()
    

    【讨论】:

    • 谢谢!您理解正确,我编辑了问题标题以使我的目标更加明确。
    猜你喜欢
    • 1970-01-01
    • 2020-02-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    • 2021-03-10
    相关资源
    最近更新 更多