2015-05-23 8 views
12

considerare alcuni sequenza data e una lunghezza della finestra, dire il listciclico finestra scorrevole iterazione

a = [13 * i + 1 for i in range(24)] 

(in modo che

In [61]: a 
Out[61]: 
[1, 
14, 
27, 
40, 
..., 
287, 
300] 

)

e finestra lunghezza 3.

Mi piacerebbe prendere la somma della finestra scorrevole di questa sequenza, ma ciclicamente; vale a dire, per calcolare la lunghezza-24 list:

[sum([1, 14, 27]), 
sum([14, 27, 40]), 
..., 
sum([287, 300, 1]), 
sum([300, 1, 14])] 

il meglio che potevo venire con, utilizzando collections.deque e Stupid Lambda Tricks, era

d = collections.deque(range(24)) 
d.rotate(1) 
map(lambda _: d.rotate(-1) or sum(a[i] for i in list(d)[: 3]), range(24)) 

c'è qualcosa di meno orribile?

risposta

3

cosa circa il semplice

a = [13 * i + 1 for i in range(24)] 
w = 3 

aa = a + a[:w] 
print([sum(aa[i:i+w]) for i in range(len(a))]) 

nota che se la finestra è grande ci sono modi migliori per calcolare una somma finestra scorrevole in O(n) (cioè con un tempo costante per elemento, indipendentemente dalla dimensione della finestra).

L'idea è di eseguire una "conversione di scansione" sostituendo ogni elemento con la somma di tutti gli elementi dall'inizio (ciò richiede il passaggio singolo).

Dopo che la somma degli elementi da x0 a x1 viene poi calcolato in O (1) con

sum_table[x1] - sum_table[x0] 

in codice:

sum_table = [0] 
for v in a: 
    sum_table.append(sum_table[-1] + v) 
for v in a[:w]: 
    sum_table.append(sum_table[-1] + v) 
print([sum_table[i+w] - sum_table[i] for i in range(len(a))]) 
+0

Questo è migliore di quello che ho fatto sotto ogni aspetto. Vieni a pensare alla seconda metà della tua risposta (la classica coda add-head-sottrazione), questa potrebbe essere inserita anche nella comprensione delle liste usando lo Stupid-Lambda-Trick, no? –

+0

@AmiTavory: sono usato di più per creare una tabella di somma perché spesso ho bisogno di accedere alla somma della finestra scorrevole casualmente e solo per un piccolo sottoinsieme dei valori. Ovviamente puoi anche tenere una somma parziale e aggiungerla/sottrarla. – 6502

1

È possibile utilizzare map con zip funzione:

>>> new=a+a[:2] 
>>> new 
[1, 14, 27, 40, 53, 66, 79, 92, 105, 118, 131, 144, 157, 170, 183, 196, 209, 222, 235, 248, 261, 274, 287, 300, 1, 14] 
>>> map(sum,zip(new,new[1:],new[2:])) 
[42, 81, 120, 159, 198, 237, 276, 315, 354, 393, 432, 471, 510, 549, 588, 627, 666, 705, 744, 783, 822, 861, 588, 315] 
>>> 

Nota che abbiamo creato il new con concatenando i primi 2 elementi di a alla fine del a e zip(new,new[1:],new[2:]) vi darà i set desiderio sub allora si può utilizzare map funzione per applicare la funzione sum su di esso:

>>> zip(new,new[1:],new[2:]) 
[(1, 14, 27), (14, 27, 40), (27, 40, 53), (40, 53, 66), (53, 66, 79), (66, 79, 92), (79, 92, 105), (92, 105, 118), (105, 118, 131), (118, 131, 144), (131, 144, 157), (144, 157, 170), (157, 170, 183), (170, 183, 196), (183, 196, 209), (196, 209, 222), (209, 222, 235), (222, 235, 248), (235, 248, 261), (248, 261, 274), (261, 274, 287), (274, 287, 300), (287, 300, 1), (300, 1, 14)] 
+0

Ciò funzionerebbe. Tuttavia, supponiamo che la lista fosse di lunghezza 10.000, e la finestra scorrevole sia di lunghezza 793. Come sarebbe fattibile scrivere '' zip (nuovo, nuovo [1:], nuovo [2:], ... nuovo [792 :]) ''? –

+0

@AmiTavory 'map (sum, zip (* (nuovo [i:] per i in range (3))))' –

2

Se non ti dispiace utilizzando itertools, questo è una soluzione:

from itertools import cycle, islice 
a_one = islice(cycle(a), 1, None) 
a_two = islice(cycle(a), 2, None) 
sums = [sum(t) for t in zip(a, a_one, a_two)] 

si potrebbe anche scrivere un'astrazione per questo in termini di lunghezza della finestra:

wlen = 3 
a_rotations = (islice(cycle(a), i, None) for i in range(1, wlen)) 
sums = [sum(t) for t in zip(a, *a_rotations)] 

Ecco un'altra soluzione che è la soluzione più scalabile in termini di lunghezza della finestra:

alen = len(a) 
wlen = 3 
sums = [sum(a[:wlen])] 
for i in range(alen - 1): 
    sums.append(sums[i] - a[i] + a[i + wlen - alen]) 

Un'altra soluzione che combina in modo efficiente le due idee e prende in prestito la variabile salvare idea dalla soluzione di Stefan Pochmann:

from itertools import islice, cycle 
wlen = 3 
rotatediterator = islice(cycle(a), wlen, None) 
sums = [] 
lastsum = sum(a[:wlen]) 
for addval, subval in zip(rotatediterator, a): 
    sums.append(lastsum) 
    lastsum += addval - subval 
+0

Grazie.farebbe davvero il lavoro per questo caso, ma non sono sicuro che sia molto scalabile (in termini di scrittura anche del codice, non della complessità di runtime) nella lunghezza della finestra. Ho scritto lo stesso commento alla risposta di Kasra sopra. –

+0

@AmiTavory Ho aggiornato con una soluzione più scalabile in termini di lunghezza della finestra. Usando la seconda soluzione, sia la complessità temporale che la complessità del codice non sono influenzate molto dalle dimensioni della finestra. – Shashank

+1

Nel tuo ultimo si potrebbe invece aggiungere 'a [i + wlen - alen]'. Lo uso anche nella mia, la regola degli indici negativi :-) –

2

Questo funziona per qualsiasi n utilizzando iSlice e somma:

from itertools import cycle, islice 
def rolling_window(func, l, n): 
    for i in xrange(len(l)): 
     yield func(islice(cycle(l), i, n+i)) 


print(list(rolling_window(sum,l,3))) 

[42, 81, 120, 159, 198, 237, 276, 315, 354, 393, 432, 471, 510, 549, 588, 627, 666, 705, 744, 783, 822, 861, 588, 315] 

Oppure dalla resa da in python3:

from itertools import cycle, islice 
def rolling_window(func, l, n): 
    yield from (func(islice(cycle(l), i, n+i)) for i in range(len(l))) 

Oppure utilizzando modulo:

out, ln, n = [], len(l), 3 
sm = sum(l[:n]) 
for i in range(1, 25): 
    out.append(sm) 
    sm = sm - l[(i - 1) % ln] + l[(i+n-1) % ln] 
print(out) 
2

Un modo veloce (almeno per le grandi dimensioni delle finestre):

sums = [] 
s = sum(a[:3]) 
for i, n in enumerate(a, 3-len(a)): 
    sums.append(s) 
    s += a[i] - n 

simili, ma usando itertools.accumulate:

acc = list(accumulate([0] + a + a)) 
print([acc[i+3] - acc[i] for i in range(len(a))]) 

O come Shashank di ma con indici negativi:

sums = [sum(a[:3])] 
for i in range(-len(a), -1): 
    sums.append(sums[-1] - a[i] + a[i+3]) 

E una breve e semplice uno, ancora una volta con gli indici negativi:

[a[i] + a[i+1] + a[i+2] for i in range(-len(a), 0)] 
2

Ad ogni punto successivo, aggiungere in quello nuovo (a[i]) e sottrarre il vecchio (a[i-3]). Per tornare indietro, puoi concatenare gli intervalli.

s = [sum(a[:3])] 
for i in itertools.chain(range(3,len(a)), range(3)) : 
    s.append(s[-1] + a[i] - a[i-3])