【问题标题】:adding default values to item x group pairs that don't have a value (df %>% spread %>% gather seems strange)将默认值添加到没有值的项目 x 组对(df %>% spread %>% gather 似乎很奇怪)
【发布时间】:2015-08-02 03:01:56
【问题描述】:

短版

如何操作

df1 %>% spread(groupid, value, fill = 0) %>% gather(groupid, value, one, two)

以更自然的方式?

加长版

给定一个数据框

df1 <- data.frame(groupid = c("one","one","one","two","two","two", "one"),
                  value = c(3,2,1,2,3,1,22),
                  itemid = c(1:6, 6))

对于许多 itemid 和 groupid 对,我们有一个值,对于某些 itemid 有没有价值的groupids。我想添加一个默认值 这些案例的价值。例如。对于 itemid 1 和 groupid "two" 那里 没有值,我想在其中添加一个获取默认值的行。

下面的tidyr代码实现了这个,但是感觉很奇怪 方法(这里添加的默认值为0)。

df1 %>% spread(groupid, value, fill = 0) %>% gather(groupid, value, one, two)

我正在寻找有关如何以更自然的方式执行此操作的建议。

由于在几周后查看上面的代码,我可能会感到困惑 关于它的效果,我写了一个包装它的函数:

#' Add default values for missing groups
#' 
#' Given data about items where each item is identified by an id, and every
#' item can have a value in every group; add a default value for all groups
#' where an item doesn't have a value yet.
add_default_value <- function(data, id, group, value, default) {
  id = as.character(substitute(id))
  group = as.character(substitute(group))
  value = as.character(substitute(value))
  groups <- unique(as.character(data[[group]]))

  # spread checks that the columns outside of group and value uniquely
  # determine the row.  Here we check that that already is the case within
  # each group using only id.  I.e. there is no repeated (id, group).
  id_group_cts <- data %>% group_by_(id, group) %>% do(data.frame(.ct = nrow(.)))
  if (any(id_group_cts$.ct > 1)) {
    badline <- id_group_cts %>% filter(.ct > 1) %>% top_n(1, .ct)
    stop("There is at least one (", id, ", ", group, ")",
         " combination with two members: (",
         as.character(badline[[id]]), ", ", as.character(badline[[group]]), ")")
  }

  gather_(spread_(data, group, value, fill = default), group, value, groups)
}

最后一点:想要这个的原因是,我的组是有序的(第 1 周,第 2 周,...) 我希望每个 id 在每个组中都有一个值,以便之后 对每个 id 的组进行排序我可以使用 cumsum 来获得每周的运行总计 也显示在运行总数没有增加的周内。

【问题讨论】:

  • 您可以将left_joinexpand.grid 一起使用,然后将NA 替换为0。我不知道这是否比spread/gather 方法更自然left_join(expand.grid(groupid=unique(df1$groupid), itemid=unique(df1$itemid)), df1)library(data.table); setkey(setDT(df1), groupid, itemid)[CJ(groupid=unique(groupid), itemid=unique(itemid))][is.na(value), value:=0][]
  • expand.grid 也是此related post 中建议的解决方案
  • 这个问题似乎与前段时间的a question I asked 非常相似。最好的答案是使用xtabs - 对于这个例子:df1 %&gt;% xtabs(formula = value ~ itemid + groupid) %&gt;% data.frame 可以工作,或者没有管道:as.data.frame(xtabs(value ~ itemid + groupid, data = df1))
  • xtabs 看起来不错。我得再想一想,但看起来我的问题是关于 xtabs 的实现。

标签: r dplyr tidyr


【解决方案1】:

tidyr 的开发版本中有一个新函数complete 可以执行此操作。

df1 %>% complete(itemid, groupid, fill = list(value = 0))
##    itemid groupid value
## 1       1     one     3
## 2       1     two     0
## 3       2     one     2
## 4       2     two     0
## 5       3     one     1
## 6       3     two     0
## 7       4     one     0
## 8       4     two     2
## 9       5     one     0
## 10      5     two     3
## 11      6     one    22
## 12      6     two     1

【讨论】:

  • 使用devtools::install_github("hadley/tidyr")获取tidyr的开发版。
【解决方案2】:

一种可能性是使用tidyr 中的expand。这种方法与@akrun 的expand.grid 思想非常相似(它实际上在内部使用expand.grid)。在将扩展数据与原始数据连接后,我使用dplyr 包进行内务管理。

这种方法比spread/gather 方法长。就个人而言,我发现它更清楚发生了什么。在我的(相当小的)基准测试中,spread/gather 的表现略好于expand/join

# expand first
expand(df1, itemid, groupid) %>% 
  # then join back to data
  left_join(df1, by = c("itemid", "groupid")) %>%
  # because there is no fill argument in join
  mutate(value = ifelse(is.na(value), 0, value)) %>%
  # rearange
  arrange(groupid, itemid)

【讨论】:

  • 我不知道展开,谢谢。不幸的是,expand、left_join、mutate 组合仍然需要一个函数中的包装器(例如问题中的 add_default_value),以便我在一两周内理解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-23
相关资源
最近更新 更多