【问题标题】:How to collapse many records into one while removing NA values如何在删除 NA 值时将多条记录合并为一条
【发布时间】:2015-04-15 01:51:04
【问题描述】:

假设我有以下数据框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

【问题讨论】:

  • 乔能否改变他对团队的看法,或者更正他在第二个或后续记录中的地址?
  • Joe 远离网格并积极避开数据收集者。我们只能找到他两次,他强烈反对告诉我们他的生活,但幸运的是他喜欢谈论棒球所以......
  • 玩笑不谈,不同的非 NA 响应给我的问题增加了另一个问题,但我想我会一步一步来。关于基于因子变量中的级别顺序实施层次结构的任何想法?这样的问题似乎没有在SO上得到回答......
  • “根据因子变量中的级别顺序实现层次结构”是什么意思?你能解释得更详细一点吗?如果每个名称出现不同的非 NA 响应,您期望什么?

标签: r coalesce


【解决方案1】:

这是 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

【讨论】:

  • 很好的答案——感谢您对细节的关注,@docendo。我特别喜欢dplyr 解决方案,因为它已经在我的清单上,以便更熟悉该包的语法。对于这部分:summarise_each(funs(first(.[!is.na(.)]))),句号是指dfname 分组的简写方式吗?我不知道dplyr 与索引配合得很好。另外,如果您能向我推荐一个关于dplyr 细节的详尽教程,我将不胜感激。
  • @mcjudd,很高兴它起作用了 :) summarise_each 中的 . 指的是当前数据,它是 a)分组和 b)按列。所以first(.[!is.na(.)]) 的意思是:在我们汇总的每一列和该列中的每组name 中,取第一个不是NA 的数据点并将其作为该组该列中的汇总值返回。不幸的是,我无法真正告诉您有关 dplyr 教程的更多信息。如果你只是用谷歌搜索,你会发现很多,例如this one by Hadley
  • @mcjudd,我不确定我是否完全理解您的意思,但您可以尝试使用... %&gt;% mutate_each(funs(replace(., which(. == 0), 1)) 扩展 dplyr 管道。您也可以使用 ifelse 但替换更快。从技术上讲,您也可以在每个汇总中执行此操作,但它会降低可读性,更重要的是您不需要按组执行此操作(并且在汇总之后数据不再分组,因此之后每个 mutate 会更好)。
  • 对不起,愚蠢的问题。弄清楚了。 summarise_each(funs(first(.[!is.na(.)]), max)) :)
  • @mcjudd,相关问题请参见stackoverflow.com/questions/27027347/…
猜你喜欢
  • 2021-04-14
  • 2017-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多