2016-07-29 60 views
4

假设我有两个数据集。其中一个包含开始/结束日期的促销清单,另一个包含每个程序的月度销售数据。按日期范围和分类变量组合数据集

promotions = data.frame(
    start.date = as.Date(c("2012-01-01", "2012-06-14", "2012-02-01", "2012-03-31", "2012-07-13")), 
    end.date = as.Date(c("2014-04-05", "2014-11-13", "2014-02-25", "2014-08-02", "2014-09-30")), 
    program = c("a", "a", "a", "b", "b")) 

sales = data.frame(
    year.month.day = as.Date(c("2013-02-01", "2014-09-01", "2013-08-01", "2013-04-01", "2012-11-01")), 
    program = c("a", "b", "a", "a", "b"), 
    monthly.sales = c(200, 200, 200, 400, 200)) 

请注意,sales$year.month.day用于表示年/月。 Day包含在内,因此R可以更简单地将列视为日期对象的向量,但与实际销售额无关。

我需要确定每个程序每月发生的促销次数。下面是产生我想要的输出循环的例子:

sales$count = rep(0, nrow(sales)) 
sub = list() 
for (i in 1:nrow(sales)) { 
    sub[[i]] = promotions[which(promotions$program == sales$program[i]),] 
    if (nrow(sub[[i]]) > 1) { 
    for (j in 1:nrow(sub[[i]])) { 
     if (sales$year.month.day[i] %in% seq(from = as.Date(sub[[i]]$start.date[j]), to = as.Date(sub[[i]]$end.date[j]), by = "day")) { 
     sales$count[i] = sales$count[i] + 1 
     } 
    } 
    } 
} 

输出示例:

sales = data.frame(
    year.month.day = as.Date(c("2013-02-01", "2014-09-01", "2013-08-01", "2013-04-01", "2012-11-01")), 
    program = c("a", "b", "a", "a", "b"), 
    monthly.sales = c(200, 200, 200, 400, 200), 
    count = c(3, 1, 3, 3, 2) 
) 

但是因为我的实际数据集是非常大的,这个循环崩溃时我在R.

运行

有没有更高效的方法来达到同样的效果?也许与dplyr有什么关系?

+0

你可以添加所需的输出数据帧吗?我不太了解你的循环输出。此外,如果您对每个计划每月的促销数量感兴趣,为什么需要销售数据框? – thepule

+0

我编辑了帖子以包含我的循环的输出。该循环将“计数”列添加到原始销售数据框架。 – heo

+0

对于我的分析,我需要每个程序的销售额和每月促销数量,所以是的,销售数据框是必要的。 – heo

回答

3

我哈德利的包的粉丝:

library(dplyr) 
library(lubridate) 

楼日期,以便他们在相同的格式sales数据框:

df <- promotions %>% 
    mutate(start.date = floor_date(start.date, unit = "month"), 
      end.date = floor_date(end.date, unit = "month")) 

展开的日期间隔:

df$output <- mapply(function(x,y) seq(x, y, by = "month"), 
     df$start.date, 
     df$end.date) 

根据日期范围,组和计数展开数据框,并合并到日期和时间的销售ogram:

df %>% tidyr::unnest(output) %>% 
    group_by(output, program) %>% 
    summarise(prom_num = n()) %>% 
    merge(sales, ., 
     by.x = c("year.month.day", "program"), 
     by.y = c("output", "program")) 

输出:

year.month.day program monthly.sales prom_num 
1  2012-11-01  b   200  2 
2  2013-02-01  a   200  3 
3  2013-04-01  a   400  3 
4  2013-08-01  a   200  3 
5  2014-09-01  b   200  1 
3

可以试试?data.table::foverlaps

library(data.table) 
setDT(sales)[, c("start.date", "end.date") := year.month.day] # Add overlap cols 
setkey(sales, program, start.date, end.date) # Key for join 
res <- foverlaps(setDT(promotions), sales)[, .N, by = year.month.day] # Count joins 
sales[res, count := i.N, on = "year.month.day"] # Update `sales` with results 
sales 
# year.month.day program monthly.sales start.date end.date count 
# 1:  2013-02-01  a   200 2013-02-01 2013-02-01  3 
# 2:  2013-04-01  a   400 2013-04-01 2013-04-01  3 
# 3:  2013-08-01  a   200 2013-08-01 2013-08-01  3 
# 4:  2012-11-01  b   200 2012-11-01 2012-11-01  2 
# 5:  2014-09-01  b   200 2014-09-01 2014-09-01  1 

这基本上是在sales创建间隔柱,由他们加入+通过program,计数重叠,并加入回sales。如果它真的困扰你,你可以通过做sales[, c("start.date", "end.date") := NULL]删除额外的列。谷歌foverlapsdata.table更多例子

5

你可以用sql来做到这一点。

library(sqldf) 
sqldf("select s.ymd,p.program,s.monthlysales, count(*) from promotions p outer left join sales s on p.program=s.program 
where s.ymd between p.startdate and p.enddate and p.program=s.program group by s.ymd, s.program") 

这将首先加入2个数据集,其中年月日的销售额是在这两个数据的推广和程序的启动和结束日期之间是相同的。那么它将按ymd进行分组并计数实例。我已经从变量的名称中删除了句点。

+0

这似乎每ymd只返回一行。对于某些数据集可能没问题,但对于每个分类变量都不需要每月计数的那些数据。 – heo

+1

我已经编辑过它,因此它会给出与程序一样多的ymd。 –

5

使用新实施非等距从data.table目前的开发版本加入:

require(data.table) # v1.9.7+ 
setDT(promotions) # convert to data.table by reference 
setDT(sales) 

ans = promotions[sales, .(monthly.sales, .N), by=.EACHI, allow.cartesian=TRUE, 
     on=.(program, start.date<=year.month.day, end.date>=year.month.day), nomatch=0L] 

ans[, end.date := NULL] 
setnames(ans, "start.date", "year.month.date") 
# program year.month.date monthly.sales N 
# 1:  a  2013-02-01   200 3 
# 2:  b  2014-09-01   200 1 
# 3:  a  2013-08-01   200 3 
# 4:  a  2013-04-01   400 3 
# 5:  b  2012-11-01   200 2 

参见开发安装说明版本号here