合并具有共享信息的行

时间:2016-10-07 15:28:44

标签: r merge

我有一个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 

4 个答案:

答案 0 :(得分: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

请注意,这仅在密钥唯一时才有效。

答案 1 :(得分:2)

对于您提供的数据版本,以下是一个基本的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参数结合使用来组合多个非缺失元素。当然,这会将矢量的类型转换为字符,如果变量是数字,这可能是不可取的。

注意:我编辑了我的原始答案,在密钥中包含“Age”,感谢@ 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")

答案 2 :(得分:2)

使用dplyr的{​​{1}}方法:

summarise_all

该函数的定义是处理所有值为## 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 的情况。

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

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)) 值。

答案 3 :(得分:1)

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

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())
    }
}

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

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