2014-10-10 10 views
5

Voglio calcolare un conteggio degli elementi nel tempo utilizzando le loro date di inizio e fine.R- Calcola un conteggio degli articoli nel tempo utilizzando le date di inizio e fine

alcuni dati di esempio

START <- as.Date(c("2014-01-01", "2014-01-02","2014-01-03","2014-01-03")) 
END <- as.Date(c("2014-01-04", "2014-01-03","2014-01-03","2014-01-04")) 
df <- data.frame(START,END) 
df 

 START  END 
1 2014-01-01 2014-01-04 
2 2014-01-02 2014-01-03 
3 2014-01-03 2014-01-03 
4 2014-01-03 2014-01-04 

Una tabella che mostra un conteggio di questi elementi attraverso il tempo (in base ai loro tempi di inizio e fine) è la seguente:

DATETIME COUNT 
2014-01-01 1 
2014-01-02 2 
2014-01-03 4 
2014-01-04 2 

Questo può essere fatto usando R, specialmente usando dplyr? Grazie molto.

+0

@RichardScriven. Ogni riga definisce un periodo da START a END. '2014-01-03' per esempio fa parte di tutti e quattro i periodi mostrati qui. – flodel

risposta

6

Questo lo farebbe. È possibile modificare i nomi delle colonne secondo necessità.

as.data.frame(table(Reduce(c, Map(seq, df$START, df$END, by = 1)))) 
#   Var1 Freq 
# 1 2014-01-01 1 
# 2 2014-01-02 2 
# 3 2014-01-03 4 
# 4 2014-01-04 2 

Come notato nei commenti, Var1 nella soluzione sopra è ormai un fattore, e non una data. Per mantenere la classe data nella prima colonna, si potrebbe fare un po 'di lavoro per la soluzione di cui sopra, o utilizzare plyr::count invece di as.data.frame(table(...))

library(plyr) 
count(Reduce(c, Map(seq, df$START, df$END, by = 1))) 
#   x freq 
# 1 2014-01-01 1 
# 2 2014-01-02 2 
# 3 2014-01-03 4 
# 4 2014-01-04 2 
+0

Si noti che funziona ancora se 'Reduce' è sostituito da' do.call'. –

+0

Attenzione che "Var1" è ora un fattore, non una data. – hadley

2

Si potrebbe utilizzare data.table

library(data.table) 
DT <- setDT(df)[, list(DATETIME= seq(START, END, by=1)), by=1:nrow(df)][, 
          list(COUNT=.N), by=DATETIME] 
DT 
#  DATETIME COUNT 
#1: 2014-01-01  1 
#2: 2014-01-02  2 
#3: 2014-01-03  4 
#4: 2014-01-04  2 

Dalla versione 1.9.4+, è anche possibile utilizzare la funzione foverlaps() per eseguire un "overlap join". È più efficiente in quanto non deve prima espandere le date per ogni riga e poi contare. Ecco come:

require(data.table) ## 1.9.4 
setDT(df) ## convert your data.frame to data.table by reference 

## 1. Some preprocessing: 
# create a lookup - the dates for which you need the count, and set key 
dates = seq(as.Date("2014-01-01"), as.Date("2014-01-04"), by="days") 
lookup = data.table(START=dates, END=dates, key=c("START", "END")) 

## 2. Now find overlapping coordinates 
# for each row in `df` get all the rows it overlaps with in `lookup` 
ans = foverlaps(df, lookup, type="any", which=TRUE) 

Ora, dobbiamo solo gruppo da yid (= indici in lookup) e contare:

## 3. count 
ans[, .N, by=yid] 
# yid N 
# 1: 1 1 
# 2: 2 2 
# 3: 3 4 
# 4: 4 2 

La prima colonna corrisponde ai numeri di riga in lookup. Se mancano alcuni numeri, il conteggio è 0 per loro.

+1

Questo è un caso per i join sovrapposti usando 'foverlaps()' creando un altro data.table con le date desiderate OP vuole trovare le sovrapposizioni per. Vuoi fare un tentativo. – Arun

+0

@Arun Grazie per aver modificato e mostrato i 'foverlaps'. – akrun

+0

Mio Dio.Come nuovo utente R sono stupito dall'ampia varietà di approcci disponibili per risolvere tali domande. Ho un po 'di ritardo da fare. Inoltre, grazie a Richard Scriven per aver chiarito la mia domanda. Apprezzo molto l'aiuto che tutti hanno fornito. Questo è fantastico. –

1

Utilizzando dplyr e dati raggruppati:

data_frame(
      START = as.Date(c("2014-01-01", "2014-01-02","2014-01-03","2014-01-03")), 
      END = as.Date(c("2014-01-04", "2014-01-03","2014-01-03","2014-01-04")) 
      ) -> df 
rbind(cbind(group = 'a', df),cbind(group = 'b', df)) %>% as_data_frame->df 
df 

df %>% 
    group_by(.,group) %>% 
    do(data.frame(table(Reduce(c, Map(seq, .$START, .$END, by = 1))))) 

Questo è un problema comune quando ad esempio si desidera trovare il numero di login su diverse pagine/macchine ecc dato intervalli di tempo per gli utenti

> df 
Source: local data frame [8 x 3] 

    group  START  END 
    (chr)  (date)  (date) 
1  a 2014-01-01 2014-01-04 
2  a 2014-01-02 2014-01-03 
3  a 2014-01-03 2014-01-03 
4  a 2014-01-03 2014-01-04 
5  b 2014-01-01 2014-01-04 
6  b 2014-01-02 2014-01-03 
7  b 2014-01-03 2014-01-03 
8  b 2014-01-03 2014-01-04 
> 
> df %>% 
+ group_by(.,group) %>% 
+ do(data.frame(table(Reduce(c, Map(seq, .$START, .$END, by = 1))))) 
Source: local data frame [8 x 3] 
Groups: group [2] 

    group  Var1 Freq 
    (chr)  (fctr) (int) 
1  a 2014-01-01  1 
2  a 2014-01-02  2 
3  a 2014-01-03  4 
4  a 2014-01-04  2 
5  b 2014-01-01  1 
6  b 2014-01-02  2 
7  b 2014-01-03  4 
8  b 2014-01-04  2 
0

Utilizzando dplyr e foreach:

library(dplyr) 
library(foreach) 

df <- data.frame(START = as.Date(c("2014-01-01", 
            "2014-01-02", 
            "2014-01-03", 
            "2014-01-03")), 
       END = as.Date(c("2014-01-04", 
           "2014-01-03", 
           "2014-01-03", 
           "2014-01-04"))) 
df 

r <- foreach(DATETIME = seq(min(df$START), max(df$END), by = 1), 
      .combine = rbind) %do% { 
    df %>% 
    filter(DATETIME >= START & DATETIME <= END) %>% 
    summarise(DATETIME, COUNT = n()) 
} 
r 
Problemi correlati