2010-07-13 4 views
8

Ho il seguente problema: Ho una serie temporale con più di 10000 voci e voglio eseguire alcuni calcoli con ciascuna di esse. Questo da solo non sarebbe un problema, ma ho bisogno di ottenere l'ultimo valore calcolato per ottenere quello successivo. Una forma molto semplice di quello che mi serve sarebbe simile a questa:Problema con iterazione su una serie storica in clojure

Val(n) = Val(n-1) + (time-series-entry/2) (! O qualcosa di simile)

Non ho idea di come gestire questo. Semplicemente facendo qualcosa di simile:

(defn calc-val 
    [time-series element] 
    (seq (cons (generate-val-element time-series element) 
      (calc-val time-series (inc element))))) 

non avrebbe funzionato, perché non posso (almeno io non so come!) Ottenere l'ultimo valore calcolato. Poi ho pensato: OK, usiamo Loop-Recur. Questo mi darebbe il valore corrispondente alla voce della serie temporale MA per il prossimo dovrei fare di nuovo tutti i calcoli. Iterate sarebbe la cosa giusta, ma non ha funzionato perché la funzione ha effetti collaterali.

Quindi sono bloccato qui su questo. Sarebbe bello se qualcuno potesse darmi un suggerimento.

risposta

3

Se si desidera solo un suggerimento; esaminare utilizzando partition.

Per un po 'più di un accenno ...

(defn calc-val 
    [time-series element] 
    (let [p (partition 2 1 time-series)] 
    (for [t p] 
     (let [first-value (first t) 
      second-value (second t)] 
     (do whatever you need to here))))) 

Anche se questo non è stato testato, dovrebbe funzionare o essere vicino al lavoro :)

Spiegazione

(partition n i seq) separa seq in parti che elenca la lunghezza n (2 in questo caso) con sovrapposizione i (1 in questo caso), quindi eseguiamo l'iterazione su quelli con for e facciamo ciò che vogliamo con le parti.

+0

Se io comprendere correttamente la domanda, il risultato della trasformazione su ogni passaggio dipende dal risultato della trasformazione nel passaggio precedente, non dalla precedente non trasformata. Quindi indirettamente dipende dall'intero frammento iniziale della serie seq che lo precede. (Spero di essere corretto dall'OP se sbaglio. :-)) –

+0

Se è così, assolutamente. L'ho capito in modo diverso (come il suo esempio imperativo ha fatto sembrare il contrario), ma sono felice di aver sbagliato o ragione! In ogni caso, è in buone mani;) – Isaac

+0

Sembra così anche a me. Spero che sia d'accordo. :-) –

7

Se si interessa solo il risultato finale, utilizzare reduce; se è necessario ottenere un seq di risultati di trasformazione di ciascun valore a turno (dove ogni trasformazione dipende da quelli precedenti), utilizzare reductions (trovato in clojure.contrib.seq-utils in 1.1 e in clojure.core in 1.2).

Qui di seguito, transform-first-entry fa quello che vuoi fare per la prima voce (se non c'è bisogno di trasformarlo in alcun modo, si può semplicemente lasciare fuori il primo argomento di reduce/reductions e utilizzare entries piuttosto che come (rest entries l'argomento finale); transform-entry è la funzione che prende il risultato della trasformazione della voce precedente e della voce corrente (in questo ordine) e produce il risultato della trasformazione per la voce corrente.

;;; only care about the final result 
(reduce transform-entry 
     (transform-first-entry (first series)) 
     (rest entries)) 

;;; need to get a seq of intermediate results 
(reductions ...arguments as above...) 

Si noti che reductions è pigro.

Supponendo che si voleva lasciare la prima voce invariata e applicare il vostro esempio trasformazione dal testo della domanda alle voci successive, è possibile utilizzare

(defn transform-entry [prev-transformed current] 
    (+ prev-transformed 
    (/ current 2))) 

come la funzione di riduzione in

(reduce transform-entry series) ; ...or reductions