合并具有共享信息的行

4

我有一个数据框,其中有几行来自合并,但是未完全合并:

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以外的一些列具有相同的值。这些列不是数据库中“主键”的一部分(据我所知)。因此,如果有几个不同的值,就不应该合并。我尝试过的方法:
 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个回答

4

使用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的示例不清楚是否总是如此。您可以使用paste(x,collapse = '-')而不是first(x)来保留多个值。 - jdobres
1
@jdobres:我同意。然而,将数据转换为字符可能也不是原帖作者想要的。 - aichao
@jdobres如果一个ID在某一列上有不同的信息,我更倾向于保留两行不同的数据。 - llrs
此外,这会导致一个错误:“错误:期望一个字符串”。 - llrs

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

请注意,仅当键是唯一的时,此方法才有效。

这假设关键列不会改变且已知,是吗? - llrs
如果您想执行某个操作,那么必须以某种方式了解它们。您可以动态地计算它们(例如,第一个连续的不包含NA值的列)。 - sebastian-c

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          

它使用aggregateifelse检查结合。如果存在任何不缺失的元素,则会返回第一个元素。我将第一个元素视为至少有一个观察值。代码中的i可以替换为length(x)以选择最后一个元素。
如@jdobres在另一个答案的评论中建议的那样,可以使用带有collapse参数的paste来组合多个非缺失元素。当然,这将把向量的类型转换为字符类型,如果变量是数值型,则可能不希望这样做。
注意:我编辑了我的原始答案,包括“Age”在内,感谢@sebastian-c指出这一点。
如果“Age”不是关键部分,则
aggregate(b[-grep("^(ID)$", names(b))], b["ID"], 
          FUN=function(x) if(all(is.na(x))) NA else x[!is.na(x)][1])

will work.

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")

我喜欢基础R语言的答案!我以为Age是关键字(在多年内的ID),但我想没有理由让它成为关键字。 - sebastian-c
感谢您指出这一点,@sebastian-c。我刚刚更新了我的答案,包括年龄。 - lmo
我也喜欢基础的R语言答案。年龄不是关键部分。 - llrs
我可能想太多了,但为什么是第一个元素? - llrs
在你的问题的最后一行中,你有aggregate(b, by = list("ID", "Age"), c)。这里的by参数表示"Age"是键的一部分。我会添加只使用"ID"的代码。对于第二个注释,我取第一个元素,因为它必然存在。例如,第二个元素可能不存在。我也会加上一条注释。 - lmo

1
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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接