向没有值的项目 x 组对添加默认值(df %>% spread %>% gather 看起来很奇怪)
adding default values to item x group pairs that don't have a value (df %>% spread %>% gather seems strange)
简短版
如何操作
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 获得每周 运行 总计
也显示在 运行 总数没有增加的那几周。
一种可能性是使用 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)
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
简短版
如何操作
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 获得每周 运行 总计 也显示在 运行 总数没有增加的那几周。
一种可能性是使用 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)
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