2016-02-01 8 views
1

У меня есть кадр данных, где каждая строка имеет уникальный идентификатор. Мне нужно реплицировать каждую из этих строк в зависимости от количества дней между датой начала и максимальной датой окончания и датой утверждения.R-петли - есть ли более эффективный способ?

ID <- c(1,2) 

Value <- c(10,20) 

StartDate <- c(as.Date("01/01/2015", '%d/%m/%Y'), 
    as.Date("01/01/2015", '%d/%m/%Y')) 

EndDate <- c(as.Date("31/01/2015", '%d/%m/%Y'), 
    as.Date("15/01/2015", '%d/%m/%Y')) 

AppDate <- c(as.Date("15/01/2015", '%d/%m/%Y'), 
    as.Date("15/02/2015", '%d/%m/%Y')) 

df <- data.frame(ID, Value, StartDate, EndDate, AppDate) 

df <- df[rep(row.names(df), ifelse(as.numeric(df$AppDate) > 
    as.numeric(df$EndDate),as.numeric(df$AppDate-df$StartDate), 
    as.numeric(df$EndDate-df$StartDate)) + 1),] 

Затем мне нужно добавить последовательный список дат от даты начала до максимальной даты окончания или даты утверждения.

Я сделал это через 2 петли. Внешний контур проходит через кадр данных для каждого уникального идентификатора. Второй цикл затем проходит через идентификатор и добавляет дату. Как только второй цикл завершен, он передает строку во внешний цикл в качестве новой начальной точки.

IDs <- unique(df$ID) 
df$Days <- rep(as.Date("01/01/1999",'%d/%m/%Y'), nrow(df)) 
counter <- 1 
for (i in 1:length(IDs)) { 
    ref <- IDs[i] 
    start <- 1 
     while (df$ID[counter] == ref) { 
      ifelse(start == 1, df$Days[counter] <- df$StartDate[counter], 
       df$Days[counter] <- df$StartDate[counter] + start -1) 
      ifelse (counter > nrow(df), break, counter <- counter + 1) 
      ifelse (counter > nrow(df), break, start <- start + 1) 
     } 
} 

Мой фактический набор данных имеет более 6000 идентификаторов и один раз я копировала строки он заканчивает тем, что более 500000 строк. Цикл занял более 15 минут, поэтому он явно неэффективен.

Итак, у меня есть 2 вопроса.

1). Каков наиболее эффективный способ сделать это в R

2). Что бы наиболее эффективный способ сделать это в общем-то в что-то вроде C++

благодаря

+1

Я считаю, что 'data.table' ответ в этом Q & A обеспечивает общий принцип эффективного решения: [Expanding последовательность в кадре данных] (http://stackoverflow.com/questions/11494511/расширения-а-последовательность-в-данных-кадра? LQ = 1). Google «R expand date data.table» должен содержать несколько аналогичных примеров. – Henrik

ответ

0

Вот одно решение, которое векторизации. Примечание. Ваш код не соответствует концепции максимального использования EndDate и AppDate, которые я пытался сделать, но если это не то, что вы хотите, вы можете соответствующим образом изменить код.

library(dplyr) 
df <- df %>% group_by(ID) %>% mutate(Days = rep(seq(min(StartDate), max(EndDate, df$AppDate), 'days'), ceiling(nrow(df)/n()))[1:n()]) 

выход будет выглядеть следующим образом (только первые несколько строк):

head(df) 
Source: local data frame [6 x 6] 
Groups: ID [1] 

    ID Value StartDate EndDate AppDate  Days 
    (dbl) (dbl)  (date)  (date)  (date)  (date) 
1  1 10 2015-01-01 2015-01-31 2015-01-15 2015-01-01 
2  1 10 2015-01-01 2015-01-31 2015-01-15 2015-01-02 
3  1 10 2015-01-01 2015-01-31 2015-01-15 2015-01-03 
4  1 10 2015-01-01 2015-01-31 2015-01-15 2015-01-04 
5  1 10 2015-01-01 2015-01-31 2015-01-15 2015-01-05 
6  1 10 2015-01-01 2015-01-31 2015-01-15 2015-01-06 

tail(df) 
Source: local data frame [6 x 6] 
Groups: ID [1] 

    ID Value StartDate EndDate AppDate  Days 
    (dbl) (dbl)  (date)  (date)  (date)  (date) 
1  2 20 2015-01-01 2015-01-15 2015-02-15 2015-02-10 
2  2 20 2015-01-01 2015-01-15 2015-02-15 2015-02-11 
3  2 20 2015-01-01 2015-01-15 2015-02-15 2015-02-12 
4  2 20 2015-01-01 2015-01-15 2015-02-15 2015-02-13 
5  2 20 2015-01-01 2015-01-15 2015-02-15 2015-02-14 
6  2 20 2015-01-01 2015-01-15 2015-02-15 2015-02-15 
+0

Спасибо, что отлично сработал. Мне нравится простота векторизованной версии и код, который я написал. Было намного медленнее, чем я ожидал, это самый быстрый способ сделать это? – MidnightDataGeek

+0

Зависит от размера данных, количества групп и диапазонов дат, с которыми вы имеете дело. Кроме того, это может помочь, если вы определите «медленный». Существует другой пакет под названием 'data.table', который считается более эффективным, чем' dplyr'. Я не использую его, потому что я нахожу 'dplyr' более универсальным и интуитивным (личное мнение). Кто-то может помочь вам скрыть код до 'data.table'. – Gopala

0

Обычно, я бы рекомендовал перекрестное соединение SQL запрос, который возвращает декартово произведение (все комбинации между двумя наборами). Однако вы можете реплицировать кросс-соединение в R с использованием merge() без каких-либо аргументов by и с all=True. Оттуда, фильтр для EndDate отсечки:

# CALCULATE CONDITIONAL END DATE 
df$TrueEndDate <- as.Date(ifelse(df$AppDate > df$EndDate, 
           df$AppDate, 
           df$EndDate), origin="1970-01-01") 

# CREATE A SEQUENTIAL DATES DATA FRAME (HERE IS 60 DAYS FROM 2015-01-01) 
dates <- data.frame(Date=as.Date(unlist(lapply(0:60, function(x) 
             as.Date("2015-01-01") + x)),      
           origin="1970-01-01")) 

# RUN CROSS JOIN MERGE, PULLING ONLY NEEDED FIELDS 
mergedf <- merge(df[c('ID', 'StartDate', 'TrueEndDate')], dates, all=TRUE) 

# FILTER OUT DATES PAST ROW'S TRUE END DATE 
mergedf <- mergedf[(mergedf$Date <= mergedf$TrueEndDate),] 

# CLEANUP 
mergedf <- mergedf[with(mergedf, order(ID)), ]  # ORDER BY ID 
row.names(mergedf) <- 1:nrow(mergedf)    # RESET ROW NAMES 

вы должны быть любопытным на эквивалентном перекрестное соединении SQL (который вы можете иметь R вызов на двигателе RDMS и импорт в качестве последнего кадра данных, может помочь для решения проблем производительности):

SELECT ID.ID, ID.Value, ID.StartDate, 
     CASE WHEN ID.AppDate > ID.EndDate 
      THEN ID.AppDate 
      ELSE ID.EndDate 
     END As TrueEndDate, 
Dates.Dates 
FROM ID, Dates 
WHERE Dates.Dates <= CASE WHEN ID.AppDate > ID.EndDate 
          THEN ID.AppDate ELSE ID.EndDate 
         END 
ORDER BY ID.ID, Dates.Dates 
Смежные вопросы