2

我有一个看起来像这样的数据框:

d<-data.frame(id=(1:9), 
                  grp_id=(c(rep(1,3), rep(2,3), rep(3,3))), 
                  a=rep(NA, 9), 
                  b=c("No", rep(NA, 3), "Yes", rep(NA, 4)), 
                  c=c(rep(NA,2), "No", rep(NA,6)), 
                  d=c(rep(NA,3), "Yes", rep(NA,2), "No", rep(NA,2)), 
                  e=c(rep(NA, 7), "No", NA), 
                  f=c(NA, "No", rep(NA,3), "No", rep(NA,2), "No"))
>d
  id grp_id  a    b    c    d    e    f
1  1      1 NA   No <NA> <NA> <NA> <NA>
2  2      1 NA <NA> <NA> <NA> <NA>   No
3  3      1 NA <NA>   No <NA> <NA> <NA>
4  4      2 NA <NA> <NA>  Yes <NA> <NA>
5  5      2 NA  Yes <NA> <NA> <NA> <NA>
6  6      2 NA <NA> <NA> <NA> <NA>   No
7  7      3 NA <NA> <NA>   No <NA> <NA>
8  8      3 NA <NA> <NA> <NA>   No <NA>
9  9      3 NA <NA> <NA> <NA> <NA>   No

在每个组 (grp_id) 中,只有 1 个“是”或“否”值与每个列 a:f 相关联。

我想为每个 grp_id 创建一行以获得如下所示的数据框:

grp_id  a    b    c    d    e    f
     1 NA   No   No <NA> <NA>   No
     2 NA  Yes <NA>  Yes <NA>   No
     3 NA <NA> <NA>   No   No   No

我认识到 tidyr 包可能是最好的工具,第一步可能是

d %>% 
   group_by(grp_id) %>%
     summarise()

我将不胜感激有关摘要中的命令或任何解决方案的帮助。谢谢。

4

3 回答 3

1

您已经收到了一些很好的答案,但他们都没有真正使用这个tidyr包。(函数的summarize()andsummarize_at()族来自dplyr。)

事实上,tidyr只为您的问题提供一个解决方案是非常可行的。

d %>%
    gather(col, value, -id, -grp_id, factor_key=TRUE) %>%
    na.omit() %>%
    select(-id) %>%
    spread(col, value, fill=NA, drop=FALSE)

唯一困难的部分是确保您a在输出中获得该列。对于您的示例数据,它完全是NA. 诀窍是factor_key=TRUE论据gather()drop=FALSE论据spread()。如果没有设置这两个参数,则输出将没有a列,并且只会包含至少有一个非NA条目的列。

以下是它如何工作的描述:

gather(col, value, -id, -grp_id, factor_key=TRUE) %>%

这会整理您的数据 - 它有效地替换列a-f使用新列colvalue,形成一个长格式的“整洁”数据框。列中的条目col是字母a- f。并且因为我们使用factor_key=TRUE了 ,所以这一列是一个带有级别的因子,而不仅仅是一个字符向量。

na.omit() %>%

NA这将从长数据中删除所有值。

select(-id) %>%

这消除了id列。

spread(col, value, fill=NA, drop=FALSE)

这会重新扩展数据,使用列中的值col来定义新的列名,并使用列中的值value来填充新列的条目。当数据丢失时,将使用fill(here NA) 的值。这drop=FALSE意味着当col是一个因素时,无论该级别是否出现在数据中,该因素的每个级别都会有一列。这与设置col为一个因素一起,是a作为输出列的内容。

我个人发现这种方法比需要子集或其他lapply东西的方法更具可读性。此外,如果您的数据实际上不是单热数据,则此方法将失败,而其他方法可能“有效”并为您提供意外的输出。这种方法的缺点是输出列a-f不是因子,而是字符向量。如果您需要因子输出,您应该能够做到(未经测试)

mutate(value = factor(value, levels=c('Yes', 'No', NA))) %>%

gather()和函数之间的任何地方,spread()以确保因子输出。

于 2017-11-20T23:33:59.977 回答
1

我们可以使用summarise_at第一个非 NA 元素并对其进行子集化

library(dplyr)
d %>%
   group_by(grp_id) %>%
   summarise_at(2:7, funs(.[!is.na(.)][1]))
# A tibble: 3 x 7
#   grp_id     a      b      c      d      e      f
#    <dbl> <lgl> <fctr> <fctr> <fctr> <fctr> <fctr>
#1      1    NA     No     No   <NA>   <NA>     No
#2      2    NA    Yes   <NA>    Yes   <NA>     No
#3      3    NA   <NA>   <NA>     No     No     No

在示例数据集中,“a”到“f”列都有factors一些只有“否”级别。如果它需要标准化,所有列都具有相同的levels,那么我们可能需要调用iefactorlevels指定c('Yes', 'No')summarise_atsummarise_at(2:7, funs(factor(.[!is.na(.)][1], levels = c('Yes', 'No'))))

于 2017-11-19T18:45:08.437 回答
1

我们可以使用aggregate. 不使用任何包。

 YN <- function(x) c(na.omit(as.character(x)), NA)[1]
 aggregate(d[3:8], d["grp_id"], YN)

给予:

##   grp_id    a    b    c    d    e  f
## 1      1 <NA>   No   No <NA> <NA> No
## 2      2 <NA>  Yes <NA>  Yes <NA> No
## 3      3 <NA> <NA> <NA>   No   No No

上面给出了字符列。如果您更喜欢因子列,请使用:

YNfac <- function(x) factor(YN(x), c("No", "Yes"))
aggregate(d[3:8], d["grp_id"], YNfac)

注意: YN 的其他替代实现是:

YN <- function(x) sort(as.character(x), na.last = TRUE)[1]

YN <- function(x) if (all(is.na(x))) NA_character_ else na.omit(as.character(x))[1]

library(zoo)
YN <- function(x) na.locf0(as.character(x), fromLast = TRUE)[1]
于 2017-11-19T19:04:44.263 回答