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

Posted

技术标签:

【中文标题】如何使用 tidyr 在分组变量的每个值中填写已完成的行?【英文标题】:How do I use tidyr to fill in completed rows within each value of a grouping variable? 【发布时间】:2015-12-28 17:17:07 【问题描述】:

假设我有关于在多个选项之间进行选择的人的数据。我每人有一排,我想每人一排和选择。所以,如果我有 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

【问题讨论】:

抱歉,time 应该是 choice。我会解决的。 作为更新,mlogit 包含一个函数 mlogit.data 可以解决这个问题。 mlogit.data(dat, choice = "choice", shape = "wide") 也会产生所需的结果。我知道我要求提供 tidyr 解决方案,但对于未来的读者,我认为包含此解决方案可能会有所帮助。 【参考方案1】:

作为@jeremycg 答案的更新。从tidyr 0.5.1(或者甚至版本0.4.0)开始c() 不再起作用。请改用nesting()

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

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

【讨论】:

【参考方案2】:

看起来另一种方法是使用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

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

【讨论】:

【参考方案3】:

我认为您最好在准备数据时将数据分开,然后在需要进行回归之前合并。

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 列,而无需依赖数据框中的问题顺序。

【讨论】:

你可能是对的,这可能最终会是最干净的方法。【参考方案4】:

您可以使用 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

【讨论】:

嗯,好的。所以这里的解决方案似乎是complete(c(id, c, d, [all other variables that don't change]), choice),完全不用fill。对吗? 我会添加它,但所需的输出是由complete(dat, c(id, c, d), choice) 生成的。 使用tidyr 版本0.5.1 这不再起作用了。请参阅下面的答案。

以上是关于如何使用 tidyr 在分组变量的每个值中填写已完成的行?的主要内容,如果未能解决你的问题,请参考以下文章

当所需变量的数量未知时如何使用 tidyr::separate [重复]

R----tidyr包介绍学习

百分位数分组表[重复]

R语言—tidyr

tidyr 在多列上使用单独的行

如何在 Python 中直接获取字典键作为变量(而不是通过从值中搜索)?