roulement optimisée des fonctions sur des séries chronologiques avec fenêtre

Est-il possible d'utiliser rollapply (à partir de zoo package ou quelque chose de similaire) optimisé fonctions (rollmean,rollmedian etc) pour calculer les fonctions de laminage avec une fenêtre basée sur le temps, au lieu d'une basée sur un certain nombre d'observations? Ce que je veux est simple: pour chaque élément d'une série temporelle irrégulière, je veux calculer une fonction de roulement avec une fenêtre N-jours. Qui est, la fenêtre doit inclure toutes les observations jusqu'à N jours avant l'observation courante. Les séries chronologiques peuvent également contenir dupliquer.

Voici un exemple. Étant donné les séries chronologiques suivantes:

      date  value
 1/11/2011      5
 1/11/2011      4
 1/11/2011      2
 8/11/2011      1
13/11/2011      0
14/11/2011      0
15/11/2011      0
18/11/2011      1
21/11/2011      4
 5/12/2011      3

Un rouleau médian avec un 5-jour de la fenêtre, aligné à droite, devraient se traduire par le calcul suivant:

> c(
    median(c(5)),
    median(c(5,4)),
    median(c(5,4,2)),
    median(c(1)),
    median(c(1,0)), 
    median(c(0,0)),
    median(c(0,0,0)),
    median(c(0,0,0,1)),
    median(c(1,4)),
    median(c(3))
   )

 [1] 5.0 4.5 4.0 1.0 0.5 0.0 0.0 0.0 2.5 3.0

j'ai déjà trouvé des solutions là-bas, mais elles sont habituellement délicates, ce qui veut dire lente. J'ai réussi à mettre en œuvre mon propre calcul de fonction de roulement. Le problème est que pour de très longues séries chronologiques la version optimisée de median (rollmedian) peut faire une énorme différence de temps, puisqu'il prend en compte le chevauchement entre les fenêtres. Je voudrais éviter de réimplanter. Je pense qu'il y a un truc avec les paramètres rollapply qui va le faire fonctionner, mais je ne peux pas le comprendre. Merci d'avance pour l'aide.

20
demandé sur thelatemail 2013-04-12 03:02:45

4 réponses

La plupart des réponses suggèrent D'insérer NA pour rendre les séries chronologiques régulières. Toutefois, cela peut être lent dans le cas de longues séries chronologiques. En outre, il ne fonctionne pas pour les fonctions qui ne peuvent pas être utilisées avec NA.

l'argument width de rollapply (paquet zoo) peut être une liste (voir l'aide de rollapply pour plus de détails). Basé sur ceci j'ai écrit une fonction qui crée une liste à utiliser avec rollapply comme paramètre de largeur. La fonction extrait des index pour les objets de zoo irréguliers si le la fenêtre mobile doit être basée sur le temps et non sur l'index. Par conséquent, l'index de l'objet zoo doit être l'heure réelle.

# Create a zoo object where index represents time (e.g. in seconds) 

d <- zoo(c(1,1,1,1,1,2,2,2,2,2,16,25,27,27,27,27,27,31),     
         c(1:5,11:15,16,25:30,31))

# Create function 

createRollapplyWidth = function(zoodata, steps, window ){   

  mintime =  min(time(zoodata))     

  maxtime =  max(time(zoodata)) 

  spotstime = seq(from = mintime , to = maxtime, by = steps)

  spotsindex = list() 

    for (i in 1:length(spotstime)){
    spotsindex[[i]] =  as.numeric(which(spotstime[i]  <=  time(zoodata) & time(zoodata) < spotstime[i] + window))}

  rollapplywidth = list()
    for (i in 1:length(spotsindex)){
    if (!is.na(median(spotsindex[[i]])) ){ 
      rollapplywidth[[round(median(spotsindex[[i]]))]] = spotsindex[[i]] - round(median(spotsindex[[i]]))}
  }
  return(rollapplywidth)
  }


# Create width parameter for rollapply using function

rollwidth =  createRollapplyWidth(zoodata = d, steps = 5, window = 5) 

# Use parameter in rollapply 

result = rollapply(d, width = rollwidth , FUN =  sum, na.rm = T) 
result

Limitation: non basée sur la date mais sur le temps en secondes. Le paramètre "partial" de rollapply ne fonctionne pas.

1
répondu Ronan 2017-06-07 08:23:07

Voici mon bricolage avec le problème. Si cela correspond à ce que vous vouliez (Je ne sais pas si c'est satisfaisant en termes de vitesse), je peux l'écrire comme une réponse plus détaillée (même si c'est basé sur l'idée de @rbatt).

library(zoo)
library(dplyr)

# create a long time series
start <- as.Date("1800-01-01")
end <- as.Date(Sys.Date())

df <- data.frame(V1 = seq.Date(start, end, by = "day"))
df$V2 <- sample(1:10, nrow(df), replace = T)

# make it an irregular time series by sampling 10000 rows
# including allowing for duplicates (replace = T)
df2 <- df %>% 
  sample_n(10000, replace = T)

# create 'complete' time series & join the data & compute the rolling median
df_rollmed <- data.frame(V1 = seq.Date(min(df$V1), max(df$V1), by = "day")) %>% 
  left_join(., df2) %>% 
  mutate(rollmed = rollapply(V2, 5, median, na.rm = T, align = "right", partial = T)) %>% 
  filter(!is.na(V2)) # throw out the NAs from the complete dataset
0
répondu Felix 2015-11-24 11:08:10

N'ont pas vérifié la vitesse mais si aucune date a plus de max.dup occurences, alors ce doit être que le dernier 5 * max.dup entrées contiennent les 5 derniers jours de sorte que la ligne la fonction fn ci-dessous passe àrollapplyr le ferai:

k <- 5

dates <- as.numeric(DF$date)
values <- DF$value

max.dup <- max(table(dates))

fn <- function(ix, d = dates[ix], v = values[ix], n = length(ix)) median(v[d >= d[n]-k])

rollapplyr(1:nrow(DF), max.dup * k, fn, partial = TRUE)
## [1] 5.0 4.5 4.0 1.0 0.5 0.0 0.0 0.0 2.5 3.0

Remarque: Nous avons utilisé ce pour DF:

 Lines <- "
      date  value
 1/11/2011      5
 1/11/2011      4
 1/11/2011      2
 8/11/2011      1
13/11/2011      0
14/11/2011      0
15/11/2011      0
18/11/2011      1
21/11/2011      4
 5/12/2011      3
"
DF <- read.table(text = Lines, header = TRUE)
DF$date <- as.Date(DF$date, format = "%d/%m/%Y")
0
répondu G. Grothendieck 2015-11-24 13:23:38

nous pouvons le faire simplement en utilisant Application de base comme suit:

première mise en place des données (basée sur la note de @G-grothendieck)

library(data.table)
Lines <- "
      date  value
1/11/2011      5
1/11/2011      4
1/11/2011      2
8/11/2011      1
13/11/2011      0
14/11/2011      0
15/11/2011      0
18/11/2011      1
21/11/2011      4
5/12/2011      3
"
DT <- as.data.table(read.table(text = Lines, header = TRUE))
DT$date <- as.Date(DF$date, format = "%d/%m/%Y")
DT$row <- 1:NROW(DF)
setkey(DT, row, date) #mark columns as sorted, for speed

notez que j'ai ajouté un vecteur à la table de données contenant le numéro de ligne, de sorte que nous pouvons passer le numéro de ligne dans la fonction appliquer. J'ai également utilisé la table de données pour simplifier la syntaxe pour l'étape suivante, et pour accélérer la fonction si elle est appliquée à de grands tableaux. Maintenant, nous utilisons s'appliquent comme suit:

roll.median.DT <- function(x){
  this.date <- as.Date(x[1])
  this.row <- as.numeric(x[3])
  median(DT[row <= this.row & date >= (this.date-5)]$value) #NB DT is not defined within function, so it is found from parent scope
}
apply(DT, FUN=roll.median.DT, MARGIN = 1)
[1] 5.0 4.5 4.0 1.0 0.5 0.0 0.0 0.0 2.5 3.0
0
répondu dww 2016-04-12 15:18:24