【问题标题】:Merging rows with shared information合并具有共享信息的行
【发布时间】:2017-02-16 16:00:45
【问题描述】:

我有一个 data.frame,其中有几行来自未完全合并的合并:

b <- read.table(text = "
      ID   Age    Steatosis       Mallory Lille_dico Lille_3 Bili.AHHS2cat
68 HA-09   16   <NA>          <NA>       <NA>       5             NA
69 HA-09   16   <33% no/occasional       <NA>      NA             1")

如何按列合并它们?

预期输出:

      ID  Age     Steatosis       Mallory Lille_dico Lille_3 Bili.AHHS2cat
69 HA-09   16  <33% no/occasional       <NA>       5             1

请注意,某些列(ID 除外)在两行上具有相同的值。这些列不是数据库(AFAIK)的“主键”的一部分。所以如果有几个不同的值不应该合并。我尝试过的事情:

 merge(b[1, ], b[2, ], all = T) # Doesn't merge the rows, just the data.frames
 cast(b, ID ~ .) # I can count them but not merging them into a single row
 aggregate(b, by = list("ID", "Age"), c) # Error 

【问题讨论】:

    标签: r merge


    【解决方案1】:

    使用summarise_alldplyr 方法:

    ## using `na.strings` to identify NA entries in posted data
    b <- read.table(text = "
          ID   Age    Steatosis       Mallory Lille_dico Lille_3 Bili.AHHS2cat
    68 HA-09   16   <NA>          <NA>       <NA>       5             NA
    69 HA-09   16   <33% no/occasional       <NA>      NA             1", na.strings = c("NA", "<NA>"))
    
    library(dplyr)
    f <- function(x) {
      x <- na.omit(x)
      if (length(x) > 0) first(x) else NA
    }
    res <- b %>% group_by(ID,Age) %>% summarise_all(funs(f))
    ##Source: local data frame [1 x 7]
    ##Groups: ID [?]
    ##
    ##      ID   Age Steatosis       Mallory Lille_dico Lille_3 Bili.AHHS2cat
    ##  <fctr> <int>    <fctr>        <fctr>      <lgl>   <int>         <int>
    ##1  HA-09    16      <33% no/occasional         NA       5             1
    

    函数的定义是处理所有值为NA的情况。


    正如@jdobres 建议的那样,如果您要合并多个非NA 值(每列),您可能希望使用以下方法将所有这些扁平化为字符串表示:

    library(dplyr)
    f <- function(x) {
      x <- na.omit(x)
      if (length(x) > 0) paste(x,collapse='-') else NA
    }
    res <- b %>% group_by(ID,Age) %>% summarise_all(funs(f))
    

    在您发布的数据中,结果将与上述相同,因为汇总的所有列最多有一个非NA 值。

    【讨论】:

    • 这假设对于每个 ID,每列将恰好有 1 个非 NA 值。 OP 的示例不清楚这是否总是如此。除了first(x),您可以使用paste(x, collapse = '-') 来保留多个值。
    • @jdobres:我同意。但是,我想说,将数据转换为字符也可能不是 OP 想要的。
    • @jdobres 如果 ID 具有不同的列信息,我希望保留两个不同的行
    • 另外,这会导致错误“错误:需要一个字符串”
    【解决方案2】:

    虽然我确信 dplyrtidyr 是可能的,但这里有一个 data.table 解决方案:

    b <- read.table(text = "
          ID   Age    Steatosis       Mallory Lille_dico Lille_3 Bili.AHHS2cat
                    68 HA-09   16   <NA>          <NA>       <NA>       5             NA
                    69 HA-09   16   <33% no/occasional       <NA>      NA             1",
                    na.strings = c("NA", "<NA>"))
    
    keycols <- c("ID", "Age")
    library(data.table)
    b_dt <- data.table(b)
    
    filter_nas <- function(x){
      if(all(is.na(x))){
        return(unique(x))
      }
      return(unique(x[!is.na(x)]))
    }
    
    b_dt[, lapply(.SD, filter_nas ), by = mget(keycols)]
    
    
          ID Age Steatosis       Mallory Lille_dico Lille_3 Bili.AHHS2cat
    1: HA-09  16      <33% no/occasional         NA       5             1
    

    请注意,这仅在键是唯一的情况下才有效。

    【讨论】:

    • 这假设 keycolumns 不会改变并且是已知的,不是吗?
    • 如果您希望能够执行操作,就必须以某种方式知道它们。您可以动态计算它们(第一个没有 NA 或其他东西的连续列)
    【解决方案3】:

    对于您提供的数据版本,这是一个应该可以工作的基本 R 方法:

    aggregate(b[-grep("^(ID|Age)$", names(b))], b[c("ID", "Age")], 
              FUN=function(x) if(all(is.na(x))) NA else x[!is.na(x)][1])
    
       ID Age Steatosis       Mallory Lille_dico Lille_3  Bili.AHHS2cat
     1 HA-09  16      <33% no/occasional         NA       5  1          
    

    它使用aggregateif else 检查。如果应该存在,这将返回第一个不丢失的元素。我采用第一个元素,因为至少有一个观察结果。代码中的i 可以替换为length(x) 来选择最后一个元素。

    正如@jdobres 在对另一个答案的评论中所建议的那样,可以将paste 与 collapse 参数结合使用来组合多个非缺失元素。这当然会将向量的类型转换为字符,如果变量是数字,这可能是不可取的。

    注意:感谢@sebastian-c 指出这一点,我编辑了我的原始答案以在密钥中包含“年龄”。


    如果“年龄”不是键的一部分,那么

    aggregate(b[-grep("^(ID)$", names(b))], b["ID"], 
              FUN=function(x) if(all(is.na(x))) NA else x[!is.na(x)][1])
    

    会起作用的。

    数据

    b <- read.table(text = "
          ID   Age    Steatosis       Mallory Lille_dico Lille_3 Bili.AHHS2cat
    68 HA-09   16   NA          NA       NA       5             NA
    69 HA-09   16   <33% no/occasional     NA      NA             1")
    

    【讨论】:

    • 我喜欢 base R 的答案!我认为Age 是关键(多年的 ID),但我想没有理由这样做。
    • 感谢您指出这一点,@sebastian-c。我刚刚更新了我的答案以包括年龄。
    • 我也喜欢基本的 R 答案。年龄不是关键
    • 我可能想多了,但为什么是第一个元素?
    • 在你问题的最后一行,你有aggregate(b, by = list("ID", "Age"), c)。这里的 by 参数表示“Age”是键的一部分。我将添加仅使用“ID”的代码。对于第二条评论,我采用第一个元素,因为它必然存在。例如,第二个元素可能不会。我也会对此添加注释。
    【解决方案4】:

    Llopis 要求在给定 ID 具有不同的列信息时保留两行的请求使事情变得复杂。首先让我们创建一些示例数据来说明这种情况:

    b <- read.table(text = "ID   Age    Steatosis       Mallory Lille_dico Lille_3 Bili.AHHS2cat
                    HA-09   16   <NA>          <NA>       <NA>       5             NA
                    HA-09   16   <33% no/occasional       <NA>      NA             1
                    HA-10   20   no <NA> <NA> 2 NA
                    HA-10   20   yes <NA> 0 NA NA",
                    na.strings = c("NA", "<NA>"), header = T)
    
         ID Age Steatosis       Mallory Lille_dico Lille_3 Bili.AHHS2cat
    1 HA-09  16      <NA>          <NA>         NA       5            NA
    2 HA-09  16      <33% no/occasional         NA      NA             1
    3 HA-10  20        no          <NA>         NA       2            NA
    4 HA-10  20       yes          <NA>          0      NA            NA
    

    这仍然可以完成,但是用于汇总的自定义函数(我们称之为f)变得有点复杂:

    f <- function(x) {
        x <- x[!is.na(x$value),]
        if (nrow(x) > 0) {
            y <- unique(x[colnames(x) != 'row.ID'])
            y$row.ID <- 1:nrow(y)
            return(y)
        } else {
            return(data.frame())
        }
    }
    

    请注意,此函数引用了一个名为“row.ID”的列,我们将在应用该函数之前创建它:

    library(tidyverse) # gives access to dplyr and tidyr packages
    
    b2 <- gather(b, variable, value, -ID, -Age) %>% # gather the many columns into a simplified key/value pair of columns (one called 'variable', the other, 'value') for each ID
        group_by(ID, variable) %>% # perform subsequent operations per ID and variable
        mutate(row.ID = 1:n()) %>% # add a row identifier
        do(f(.)) %>% # apply our custom function
        spread(variable, value, convert = T) %>% # un-gather the variable/value columns
        ungroup # remove grouping metadata
    
          ID   Age row.ID Bili.AHHS2cat Lille_3 Lille_dico       Mallory Steatosis
    * <fctr> <int>  <int>         <int>   <int>      <int>         <chr>     <chr>
    1  HA-09    16      1             1       5         NA no/occasional      <33%
    2  HA-10    20      1            NA       2          0          <NA>        no
    3  HA-10    20      2            NA      NA         NA          <NA>       yes
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-14
      • 2016-01-09
      • 1970-01-01
      • 2013-09-27
      • 2021-04-16
      相关资源
      最近更新 更多