使用 dplyr 将行添加到分组数据?
Posted
技术标签:
【中文标题】使用 dplyr 将行添加到分组数据?【英文标题】:Add rows to grouped data with dplyr? 【发布时间】:2014-06-20 12:13:25 【问题描述】:我的数据采用 data.frame 格式,例如以下示例数据:
data <-
structure(list(Article = structure(c(1L, 1L, 3L, 1L, 1L, 1L,
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 2L, 1L, 2L, 1L
), .Label = c("10004", "10006", "10007"), class = "factor"),
Demand = c(26L, 780L, 2L, 181L, 228L, 214L, 219L, 291L, 104L,
72L, 155L, 237L, 182L, 148L, 52L, 227L, 2L, 355L, 2L, 432L,
1L, 156L), Week = c("2013-W01", "2013-W01", "2013-W01", "2013-W01",
"2013-W01", "2013-W02", "2013-W02", "2013-W02", "2013-W02",
"2013-W02", "2013-W03", "2013-W03", "2013-W03", "2013-W03",
"2013-W03", "2013-W04", "2013-W04", "2013-W04", "2013-W04",
"2013-W04", "2013-W04", "2013-W04")), .Names = c("Article",
"Demand", "Week"), class = "data.frame", row.names = c(NA, -22L))
我想按周和文章来总结需求栏。为此,我使用:
library(dplyr)
WeekSums <-
data %>%
group_by(Article, Week) %>%
summarize(
WeekDemand = sum(Demand)
)
但由于某些文章在某些周内没有售出,因此每篇文章的行数不同(WeekSums 数据框中仅显示有销售的周数)。如何调整我的数据,使每篇文章的行数相同(每周一行),包括需求为 0 的周数?
输出应如下所示:
Article Week WeekDemand
1 10004 2013-W01 1215
2 10004 2013-W02 900
3 10004 2013-W03 774
4 10004 2013-W04 1170
5 10006 2013-W01 0
6 10006 2013-W02 0
7 10006 2013-W03 0
8 10006 2013-W04 5
9 10007 2013-W01 2
10 10007 2013-W02 0
11 10007 2013-W03 0
12 10007 2013-W04 0
我试过了
WeekSums %>%
group_by(Article) %>%
if(n()< 4) rep(rbind(c(Article,NA,NA)), 4 - n() )
但这不起作用。在我最初的方法中,我通过将第 1-4 周的数据框与每篇文章的原始数据文件合并来解决这个问题。这样,我每篇文章有 4 周(行),但是使用 for 循环的实现效率非常低,所以我尝试对 dplyr(或任何其他更有效的包/函数)做同样的事情。任何建议将不胜感激!
【问题讨论】:
【参考方案1】:没有 dplyr 可以这样完成:
as.data.frame(xtabs(Demand ~ Week + Article, data))
给予:
Week Article Freq
1 2013-W01 10004 1215
2 2013-W02 10004 900
3 2013-W03 10004 774
4 2013-W04 10004 1170
5 2013-W01 10006 0
6 2013-W02 10006 0
7 2013-W03 10006 0
8 2013-W04 10006 5
9 2013-W01 10007 2
10 2013-W02 10007 0
11 2013-W03 10007 0
12 2013-W04 10007 0
这可以重写为 magrittr 或 dplyr 管道,如下所示:
data %>% xtabs(formula = Demand ~ Week + Article) %>% as.data.frame()
如果需要宽格式解决方案,可以省略末尾的 as.data.frame()
。
【讨论】:
xtabs
使用指定的公式创建一个 "table"
类的对象,其维度是右侧变量,其单元格是左侧变量的总和,如果该单元格为空,则为零. as.data.frame
应用于表格时会将其重塑为长表格。【参考方案2】:
由于dplyr
正在积极开发中,我想我会发布一个包含tidyr
的更新:
library(dplyr)
library(tidyr)
data %>%
expand(Article, Week) %>%
left_join(data) %>%
group_by(Article, Week) %>%
summarise(WeekDemand = sum(Demand, na.rm=TRUE))
产生:
Article Week WeekDemand
1 10004 2013-W01 1215
2 10004 2013-W02 900
3 10004 2013-W03 774
4 10004 2013-W04 1170
5 10006 2013-W01 0
6 10006 2013-W02 0
7 10006 2013-W03 0
8 10006 2013-W04 5
9 10007 2013-W01 2
10 10007 2013-W02 0
11 10007 2013-W03 0
12 10007 2013-W04 0
使用 tidyr >= 0.3.1 现在可以写成:
data %>%
complete(Article, Week) %>%
group_by(Article, Week) %>%
summarise(Demand = sum(Demand, na.rm = TRUE))
【讨论】:
感谢您展示解决问题的另一种方法!我不得不承认我喜欢xtabs
解决方案的简单性,但这也产生了预期的结果 (+1)【参考方案3】:
我想我会提供一个dplyr
-esque 解决方案。
expand.grid()
生成您正在寻找的成对组合。
使用left_join()
加入需求数据(用 NA 填充其余部分)。
解决办法:
full_data <- expand.grid(Article=data$Article,Week=data$Week)
out <- left_join(tbl_dt(full_data),data)
out[is.na(out)] <- 0 # fill with zeroes for summarise below.
然后和以前一样:
WeekSums <- out %>%
group_by(Article, Week) %>%
summarise(
WeekDemand = sum(Demand)
)
Fxnal 编程?
如果您经常使用这种转换,那么也许是一个便利功能:
xpand <- function(df, col1, col2,na_to_zero=TRUE)
require(dplyr)
# to substitute in the names "as is" need substitute then eval.
xpand_call <- substitute(
expanded <- df %>%
select(col1,col2) %>%
expand.grid()
)
eval(xpand_call)
out <- left_join(tbl_dt(expanded), df) # join in any other variables from df.
if(na_to_zero) out[is.na(out)] <- 0 # convert NAs to zeroes?
return(out)
这样你就可以做到:
expanded_df <- xpand(df,Article,Week)
【讨论】:
【参考方案4】:对于这种情况,您还可以使用dcast
和melt
。
library(dplyr)
library(reshape2)
data %>%
dcast(Article ~ Week, value.var = "Demand", fun.aggregate = sum) %>%
melt(id = "Article") %>%
arrange(Article, variable)
【讨论】:
以上是关于使用 dplyr 将行添加到分组数据?的主要内容,如果未能解决你的问题,请参考以下文章
R语言ggplot2可视化:使用dplyr包计算每个分组个数的比例使用ggplot2可视化条形图(bar plot)并在条形图上添加百分比标签
R语言进行数据聚合统计(Aggregating transforms)实战:使用R原生方法data.tabledplyr等方案计算分组均值并添加到可视化结果中