2016-04-14 5 views
0

Ich möchte einen Mittelwert aus einem 7-Tage-Rolling-Fenster mit 1-Tages-Inkrementen von Daten nehmen, die in 30-Minuten-Intervallen gesammelt werden. Ich habe versucht, data.table mit by bedingte Anweisung ohne Erfolg. Irgendein guidane würde sehr geschätzt werden.Nehmen Sie einen täglichen rollenden Mittelwert eines Sieben-Tage-Fensters für 30 Minuten abgetastete Daten.

# packages 
library(data.table) 
library(lubridate) 

# Set set.seed to have reproducible sampling 
set.seed(42) 

# Create some Data 
start = ymd_hms("2014-01-01 00:00:00") 
end = ymd_hms("2014-12-31 23:59:59") 

# Create data with 30 minute intervals. 
dat <- data.table(timestamp = seq(start, end, by = "30 min"), 
        sample1 = sample(1:20, 17520, replace = TRUE)) 

# Create date variable for merging datasets. 
dat[, date := as.Date(timestamp)] 

# Create data for 7 day window moving window with one day increments. 
dat2 <- data.table(start = seq(start, end, by = "1 day"), 
        end = seq(start + days(7), end + days(7), by = "1 day")) 

# Create date variable for merging datasets. 
dat2[, date := as.Date(start)] 

# mergre datasets. 
dat <- merge(dat, dat2, by="date") 

# Tried 
dat[, .(sample.mean = mean(sample1)), by = .(timestamp >= start & timestamp < end)] 
# timestamp sample.mean 
# 1:  TRUE 10.46638 

dat[, .(sample.mean = mean(sample1)), by = .(timestamp %in% c(start:end))] 
# timestamp sample.mean 
# 1:  TRUE 10.40059 
# 2:  FALSE 10.46767 
# Warning messages: 
# 1: In start:end : 
# numerical expression has 17520 elements: only the first used 
# 2: In start:end : 
# numerical expression has 17520 elements: only the first used 

dat[, .(sample.mean = mean(sample1)), by = .(timestamp %between% c(start, end))] 
# timestamp sample.mean 
# 1:  TRUE 19.00000 
# 2:  FALSE 10.46589 
+0

Sind die Fenster um Minuten oder Tage zentriert? Dh, sind Beobachtungen von 12:00 und 13:00 am selben Tag mit dem gleichen Umgebungsfenster identisch, oder sind die mit der 13:00 Beobachtung verbundenen Beobachtungen eine Stunde nach vorne geschoben? – MichaelChirico

+1

@ Frank danke für den Fang, aktualisiert damit Zufallsauswahl ist reproduzierbar – user3498523

+0

@ MichaelChirico, sind die Fenster um Tage zentriert. Jedes Fenster von sieben Tagen würde insgesamt 336 Proben haben und sollte um einen Tag erhöht werden. – user3498523

Antwort

2

Ich bin nicht 100% sicher, dass ich Ihre genauen Parameter zu verstehen, aber hier ist der grundlegende Ansatz:

setkey(dat, date) 

#pull the 7 previous days 
dat[ , dat[.(seq(.BY$date - 7L, 
       .BY$date, by = "day")), 
      #nomatch = 0L will exclude any requested dates outside the interval 
      mean(sample1), nomatch = 0L], by = date] 
#   date  V1 
# 1: 2014-01-01 12.31250 
# 2: 2014-01-02 10.94792 
# 3: 2014-01-03 11.27083 
# 4: 2014-01-04 11.10417 
# 5: 2014-01-05 10.79167 
# ---      
# 361: 2014-12-27 10.50260 
# 362: 2014-12-28 10.52344 
# 363: 2014-12-29 10.05990 
# 364: 2014-12-30 10.03906 
# 365: 2014-12-31 10.38542 

Einige mögliche Kesselflicker:

  • ändern 7L zu welchem ​​Fenster Sie würde wie; verwenden positiv, wenn Sie vorausschau mittelt wollen

  • Wenn Sie bytimestamp gehen wollen, müssen Sie die 7L einstellen müssen, um unabhängig von Einheiten (Sekunden/Minuten/Stunden/etc)

  • Die extreme passen Punkte des Intervalls sind technisch nicht korrekt, da das Fenster kürzer als angefordert ist; ausschließen nomatch und diese Punkte kehrt als NA

  • Verwenden .(var = mean(sample1))var die Ausgabespalte zu nennen.

+0

Ich bin nicht sehr vertraut mit 'library (zoo)', aber ich denke, es macht mehr Sinn, Mittel auf Tag Ebene zuerst zu berechnen, wenn Sie Stunden innerhalb des Tages ignorieren werden, wie 'dat [, mean (sample1), by = date] [, rmean: = rollmean (V1, 7, fill = NA)] 'und fusioniere dann mit' date' zurück zu 'dat'. – Frank

+1

@Frank aus dem oberen Teil meines Kopfes diese Zahlen sollten gleich sein, es sei denn, die Anzahl der Beobachtungen an jedem Tag ist unterschiedlich, in diesem Fall sollten Sie durch die Anzahl der Beobachtungen gewichtet werden und mit meiner Antwort trotzdem kommen: p (bis zu der Benutzer hängt jedoch davon ab, was Sie aus der Berechnung herausholen wollen) – MichaelChirico

+0

Ja, mein Punkt war nicht, ob Ihre Berechnung richtig ist, sondern über Effizienz. Sie nehmen den Mittelwert von 48 * 7 Zahlen für jeden Wert im Ergebnis, während Sie stattdessen Mittel von 7 Zahlen gleichzeitig nehmen könnten (vorausgesetzt, Zeug über Gewichte und was das OP ist, usw.), plus Rollen so Die Berechnungen sind natürlich nicht unabhängig. – Frank

2

Hier ist ein Ansatz:

library(zoo) 
daymeans = dat[, mean(sample1), by=date][, rmean := rollmean(V1, 7, fill=NA)] 
dat[daymeans, rmean := i.rmean, on="date"] 

Dies setzt voraus, dass Ihre Daten bereits von date sortiert ist; Wenn nicht, verwenden Sie keyby=date anstelle von by=date. Wenn Sie keine Zwischen Objekten jonglieren wollen, gibt es einen Einzeiler:

# Michael Chirico's suggestion from the comments 
dat[dat[, mean(sample1), by=date][, rollmean(V1, 7, fill=NA)], rmean := i.V1, on = "date"] 

Unter Umständen müssen Sie die Argumente rollmean zwicken Ihrer speziellen Definition des Fensters zu passen. @eddi vorgeschlagen, dass runmean aus der caTools-Bibliothek ist in der Regel schneller als Zoos rollmean und so ist wahrscheinlich auch einen Blick wert.


Crude Benchmark mit den Beispieldaten des OP:

dat2 = copy(dat) 

# Michael's answer 
system.time({ 
setkey(dat, date) 
dat[ , dat[.(seq(.BY$date - 7L, 
       .BY$date, by = "day")), 
      mean(sample1), nomatch = 0L], by = date] 
}) 

    user system elapsed 
    0.33 0.00 0.35 

# this answer 
system.time({ 
daymeans = dat2[, mean(sample1), by=date][, rmean := rollmean(V1, 7, fill=NA)] 
dat2[daymeans, rmean := i.rmean, on="date"] 
}) 

    user system elapsed 
     0  0  0 

Warum es schneller: Hier wir die Berechnung 365 Mittel von 48 Zahlen und dann ein rollendes Mittel der Länge 365; Das ist weniger rechenintensiv als 365 Fusionen zu machen, um 48 * 7 Zahlen zu finden und dann den Mittelwert der letzteren zu nehmen.

+1

great! Ich würde nicht einmal die Zwischentabelle erstellen, außer wir planen eine Wiederverwendung es ... 'dat2 [dat2 [, Mittelwert (Beispiel1), Datum] [, rollmean (V1, 7, Fülle = NA)], rmean: = i.V1, auf =" Datum "]' ist ein bisschen weniger verdaulich aber immer noch gut :) – MichaelChirico

+2

nette antwort; imo "standard" sollte 'caTools :: runmean' verwenden - waaay schneller als' zoo :: rollmean' – eddi

+1

@eddi Oh, danke, ich habe noch nie davon gehört, da ich selten/nie rolllaply Zeug. – Frank

Verwandte Themen