2016-07-29 2 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 используется, чтобы указать год/месяц. День включен, поэтому 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

Я отредактировал сообщение, чтобы включить вывод моего цикла. Цикл добавляет столбец «count» к исходному кадру данных. – heo

+0

Для моего анализа мне нужны продажи и количество рекламных акций в месяц для каждой программы, так что да, необходим data data.frame. – heo

ответ

3

Я фанат пакетов Хедли:

library(dplyr) 
library(lubridate) 

даты этажа, так что они находятся в том же формате, что и sales dataframe:

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], если это действительно вас беспокоит. Google foverlaps и data.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 продаж находится между начальной и конечной датой продвижения и программы в обоих данных одинаковы. то он будет группироваться по ymd и подсчитывать экземпляр. Я удалил периоды из имен переменной.

+0

Кажется, это возвращает только одну строку за ymd. это может быть хорошо для некоторых наборов данных, но не для тех, которые требуют ежемесячных подсчетов для каждой категориальной переменной. – heo

+1

Я отредактировал ее так, чтобы она давала u столько же 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.

Смежные вопросы