我该如何使用tidyr在每个分组变量的值中填充已完成的行?

12

假设我有一些人选择了几个选项。每个人有一行数据,但我想要每个人和选项都有一行数据。比如,如果有10个人,每个人有3个选项,那么现在有10行数据,而我希望有30行。

其他所有变量都应该复制到新的行中。例如,如果有一个性别变量,那么它应该在 ID 内是恒定的。(我按照这种方式设置我的数据以便使用 mnlogit 进行分析。)

这似乎是两个 tidyr 函数 completefill 设计用来解决的问题。举个简单的例子:

library(lubridate)
library(tidyr)
dat <- data.frame(
    id = 1:3,
    choice = 5:7,
    c = c(9, NA, 11),
    d = ymd(NA, "2015-09-30", "2015-09-29")
    )

dat %>% 
  complete(id, choice) %>%
  fill(everything())

# Source: local data frame [9 x 4]
# 
#      id choice     c          d
#   (int)  (int) (dbl)     (time)
# 1     1      5     9       <NA>
# 2     1      6     9       <NA>
# 3     1      7     9       <NA>
# 4     2      5     9       <NA>
# 5     2      6     9 2015-09-30
# 6     2      7     9 2015-09-30
# 7     3      5     9 2015-09-30
# 8     3      6     9 2015-09-30
# 9     3      7    11 2015-09-29

但是这种方法存在一些问题——d的值被正确地向下传递了,但是ID为1的c的值替换了ID为2的(正确的)NA值。

我可以尝试一个解决方法,比如用999替换所有缺失的值,运行completefill,然后再用NA替换999。(如果我选择这条路线,我认为我必须将日期变量转换为字符变量,然后再转换回来。) 但也许在这里有人知道使用tidyr的简洁方法?

编辑: 这里期望的输出是:

# Source: local data frame [9 x 4]
# 
#     id     c          d choice
#  (int) (dbl)     (time)  (int)
# 1     1     9       <NA>      5
# 2     1     9       <NA>      6
# 3     1     9       <NA>      7
# 4     2    NA 2015-09-30      5
# 5     2    NA 2015-09-30      6
# 6     2    NA 2015-09-30      7
# 7     3    11 2015-09-29      5
# 8     3    11 2015-09-29      6
# 9     3    11 2015-09-29      7

1
抱歉,应该是“choice”而不是“time”。我会修复它。 - Jake Fisher
作为更新,mlogit包含一个名为mlogit.data的函数,可以解决这个问题。 mlogit.data(dat, choice = "choice", shape = "wide")也会产生所需的结果。我知道我要求使用tidyr的解决方案,但是为了未来的读者,我认为包括这个解决方案也可能有帮助。 - Jake Fisher
4个回答

21

更新@jeremycg回答。从tidyr 0.5.1(或甚至版本0.4.0)开始,c()已经不再起作用。改用nesting()代替:

dat %>% 
 complete(nesting(id, c, d), choice) 

注意:我试图编辑@jeremycg的回答,因为该回答在编写时是正确的(因此不需要新的答案),但不幸的是,该编辑被拒绝了。


11

您可以使用“分组”技巧,使用c()在不完整的情况下完成。这样可以只使用已经分组的变量的现有组合来完成。

library(tidyr)
dat %>% complete(c(id, c, d), choice) 
     id     c          d choice
  (int) (dbl)     (time)  (int)
1     1     9       <NA>      5
2     1     9       <NA>      6
3     1     9       <NA>      7
4     2    NA 2015-09-30      5
5     2    NA 2015-09-30      6
6     2    NA 2015-09-30      7
7     3    11 2015-09-29      5
8     3    11 2015-09-29      6
9     3    11 2015-09-29      7

2
嗯,好的。所以解决方案似乎是 complete(c(id, c, d, [所有不变量]), choice),然后完全不使用 fill。 对吗? - Jake Fisher
1
我会添加它,但期望的输出是由complete(dat, c(id, c, d), choice)生成的。 - Jake Fisher
使用 tidyr 版本 0.5.1,这个方法不再适用。请查看下面的答案。 - Manuel R

2

我认为在准备数据时最好将数据保持分离状态,然后在需要执行回归之前再合并。

subjectdata <- dat[,c("id", "c", "d")]
questiondata <- dat[,c("id", "choice")] %>% complete(id, choice)

然后

> merge(questiondata, subjectdata)
  id choice  c          d
1  1      5  9       <NA>
2  1      6  9       <NA>
3  1      7  9       <NA>
4  2      5 NA 2015-09-30
5  2      6 NA 2015-09-30
6  2      7 NA 2015-09-30
7  3      5 11 2015-09-29
8  3      6 11 2015-09-29
9  3      7 11 2015-09-29

根据需要,这样你还能为用户2获得一个有效的“d”列,而无需依赖于数据框中问题的顺序。

你可能是对的,这可能会是最干净的方法,在最终完成时。 - Jake Fisher

-1

看起来另一种方法是使用spreadgatherspread将每个可能的答案创建为一列,而gather则将单独的列并重塑为行。 使用这些数据:

dat %>%
  spread(choice, choice) %>%
  gather(choice, drop_me, `5`:`7`) %>%  # Drop me is a redundant column
  select(-drop_me) %>%
  arrange(id, choice)  # reorders so that the answer matches

#   id  c          d choice
# 1  1  9       <NA>      5
# 2  1  9       <NA>      6
# 3  1  9       <NA>      7
# 4  2 NA 2015-09-30      5
# 5  2 NA 2015-09-30      6
# 6  2 NA 2015-09-30      7
# 7  3 11 2015-09-29      5
# 8  3 11 2015-09-29      6
# 9  3 11 2015-09-29      7

我还没有进行任何测试来比较它们的效率。


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