R:清理广泛而不整洁的数据帧

时间:2017-11-19 18:42:30

标签: r tidyr data-cleaning summarize

我的数据框看起来像:

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&#34;是&#34;或&#34;否&#34;与每个列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()

我非常感谢您对汇总中的命令或任何解决方案的帮助。谢谢。

3 个答案:

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

在示例数据集中,列&#39; a&#39;到了&#39; f&#39;都是factors,其中一些只有&#39;否&#39;水平。如果需要使用具有相同levels的所有列进行标准化,那么我们可能需要在factor中使用levels c('Yes', 'No')来调用summarise_at } {ie summarise_at(2:7, funs(factor(.[!is.na(.)][1], levels = c('Yes', 'No'))))

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

答案 2 :(得分:1)

您已收到一些好的答案,但它们都没有实际使用tidyr包。 (summarize()summarize_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的{​​{1}}参数和gather()的{​​{1}}参数。如果没有设置这两个参数,则输出将没有drop=FALSE列,并且只包含至少有一个非spread()条目的列。

以下是对其工作原理的描述:

a

整理您的数据 - 它有效地用新列NAgather(col, value, -id, -grp_id, factor_key=TRUE) %>% 替换列a - f,形成一个长格式的“整洁”数据框。 col列中的条目为字母value - col。由于我们使用了a,因此此列是因子,其中包含级别,而不仅仅是字符向量。

f

这将删除长数据中的所有factor_key=TRUE值。

na.omit() %>%

这消除了NA列。

select(-id) %>%

这会重新扩展数据,使用id列中的值来定义新列名称,并使用spread(col, value, fill=NA, drop=FALSE) 列中的值来填充新列的条目。如果缺少数据,则会使用值col(此处为value)。而fill表示当NA是一个因素时,每个级别的因子都会有一列,无论该级别是否出现在数据中。将drop=FALSE设置为一个因素,将col作为输出列。

我个人觉得这种方法比需要子集或col的方法更具可读性。此外,如果您的数据实际上并不是热门的话,这种方法将失败,而其他方法可能会“起作用”并为您提供意外的输出。这种方法的缺点是输出列a - lapply不是因子,而是字符向量。如果您需要因子输出,您应该能够(未经测试)

a

fmutate(value = factor(value, levels=c('Yes', 'No', NA))) %>% 函数之间的任何位置,以确保因子输出。