如何在删除 NA 值时将多条记录合并为一条记录

How to collapse many records into one while removing NA values

假设我有以下数据框df

name <- c("Bill", "Rob", "Joe", "Joe")
address <- c("123 Main St", "234 Broad St", NA, "456 North Ave")
favteam <- c("Dodgers", "Mets", "Pirates", NA)

df <- data.frame(name = name, 
                 address = address,
                 favteam = favteam)
df

看起来像:

  name       address favteam
1 Bill   123 Main St Dodgers
2  Rob  234 Broad St    Mets
3  Joe          <NA> Pirates
4  Joe 456 North Ave    <NA>

我想要做的是按名称折叠(合并)行(或通常,任意数量的分组变量)并具有除 NA 之外的任何其他值替换 NA 最终数据中的值,如下所示:

df_collapse <- foo(df)

  name   address        favteam
1 Bill   123 Main St    Dodgers
2  Rob   234 Broad St      Mets
3  Joe   456 North Ave  Pirates

这是 dplyr 的一个选项:

library(dplyr)

df %>%
  group_by(name) %>%
  summarise_each(funs(first(.[!is.na(.)]))) # or summarise_each(funs(first(na.omit(.))))

#Source: local data frame [3 x 3]
#
#  name       address favteam
#1 Bill   123 Main St Dodgers
#2  Joe 456 North Ave Pirates
#3  Rob  234 Broad St    Mets

与 data.table:

library(data.table)
setDT(df)[, lapply(.SD, function(x) x[!is.na(x)][1L]), by = name]
#   name       address favteam
#1: Bill   123 Main St Dodgers
#2:  Rob  234 Broad St    Mets
#3:  Joe 456 North Ave Pirates

setDT(df)[, lapply(.SD, function(x) head(na.omit(x), 1L)), by = name]

编辑:

你说在你的实际数据中每个名字有不同数量的非 NA 响应。在这种情况下,以下方法可能会有所帮助。

考虑这个修改后的示例数据(查看最后一行):

name <- c("Bill", "Rob", "Joe", "Joe", "Joe")
address <- c("123 Main St", "234 Broad St", NA, "456 North Ave", "123 Boulevard")
favteam <- c("Dodgers", "Mets", "Pirates", NA, NA)

df <- data.frame(name = name, 
                 address = address,
                 favteam = favteam)

df
#  name       address favteam
#1 Bill   123 Main St Dodgers
#2  Rob  234 Broad St    Mets
#3  Joe          <NA> Pirates
#4  Joe 456 North Ave    <NA>
#5  Joe 123 Boulevard    <NA>

然后,您可以使用这种 data.table 方法来获取非 NA 响应,这些响应的数量可能因名称而异:

setDT(df)[, lapply(.SD, function(x) unique(na.omit(x))), by = name]
#   name       address favteam
#1: Bill   123 Main St Dodgers
#2:  Rob  234 Broad St    Mets
#3:  Joe 456 North Ave Pirates
#4:  Joe 123 Boulevard Pirates