Domanda

ho bisogno di memorizzare una grande lista di numeri interi in BigTable (db). Per l'efficienza li sto Memorizzazione come diff tra 2 elementi consecutivi.

Per esempio:

 original_list = [1005, 1004, 1003, 1004, 1006] 

Memorizzazione della lista di cui sopra (che contiene in realtà più di 1000k articoli) come

start = 1005
diff = [-1, -1, 1, 2]

Il più vicino ho potuto gestire a dire,

ltp = [start]
map(lambda x: ltp.append(ltp[-1] + x), tick)

Sto cercando un modo efficace per riconvertirlo in lista originale.

È stato utile?

Soluzione

I seguenti opere per me:

orig = [start]
for x in diff:
    orig.append(orig[-1] + x)

Uso map creerà una nuova matrice delle stesse dimensioni, riempito con None. Trovo anche un semplice ciclo for più leggibile, e in questo caso il più velocemente si può ottenere.

Altri suggerimenti

Per tali grandi strutture di dati NumPy funzionano bene. Per questo esempio, è sopra 200x più veloce (vedi sotto), e un po 'più facile da codice, fondamentalmente solo

add.accumulate(diff)

Confronto tra NumPy e manipolazione lista diretta:

import numpy as nx
import timeit

N = 10000

diff_nx = nx.zeros(N, dtype=nx.int)
diff_py = list(diff_nx)

start = 1005

def f0():
    orig = [start]
    for x in diff_py: 
        orig.append(orig[-1] + x)

def f1():
    diff_nx[0] = start
    nx.add.accumulate(diff_nx)

t = timeit.Timer("f0()", "from __main__ import f0, f1, diff_nx, diff_py, nx, start")
print t.timeit(number=1000)
t = timeit.Timer("f1()", "from __main__ import f0, f1, diff_nx, diff_py, nx, start")
print t.timeit(number=1000)

13.4044158459     # for list looping
0.0474112033844   # for numpy accumulate

In realtà, però, sembra meglio riutilizzare un algoritmo di compressione stabilito, come può essere fatto facilmente con PyTables , piuttosto che a rotazione proprio come sembra che si sta facendo qui.

Inoltre, qui, sto suggerendo di leggere nei dati con spazio per il termine di avvio anteposta, piuttosto che ricostruire la lista con il termine anteposta, naturalmente, in modo da non avere a che fare la copia.

Perfetto per i generatori:

def diff2abs( diffs, start ):
    yield start
    for diff in diffs:
        start += diff
        yield start

start = 1005
diffs = [-1, -1, 1, 2]
original_list = list( diff2abs( diffs, start ))

Molti degli altri intervistati hanno ragionevoli implementazioni dell'algoritmo che hai chiesto, ma sono poco chiare su cosa esattamente problema è che stai veramente cercando di risolvere.

A meno che i numeri che sono memorizzati sono molto grandi (cioè, traboccare un intero e richiede bignum), l'elenco delle diff non si otterrà alcun efficienza - un intero è un numero intero da POV Python runtime, così il vostro esempio " diff lista" dei [-1, -1, 1, 2] consumerà solo la quantità di memoria l'elenco [1005, 1004, 1003, 1004, 1006] originale.

class runningtotal:
    def __init__(self, start = 0):
        self.total = start
    def __call__(self, value):
        self.total += value
        return self.total

Ora prova:

>>> map(runningtotal(start), [0,]+diff)
[1005, 1004, 1003, 1004, 1006]

Come mshsayem suggerito, list comprehension uso - sono generalmente più veloce di cicli for o mappa / lambda (secondo farlo libro di Mark Lutz Learning Python)

.

Se davvero si vuole utilizzare una soluzione più FP-ish, la funzione corretta sarebbe "scan", wich [credo] non è implementata in Python modo che avrebbe dovuto implementare da soli (che non è un disco compito).

"scan" è fondamentalmente un ridurre, ma invece di ridurre l'elenco per un singolo valore, memorizza il risultato di ogni "iterazione" in una nuova lista.

Se implementato, si potrebbe fare qualcosa di simile:

scan(lambda x,y: x+y, [start]++diff)

Anche se non capisco il motivo per cui questo dovrebbe essere più efficiente, sono abbastanza sicuro che un ciclo for darà le migliori prestazioni:

l = [start]
for i in diff:
    l.append(l[-1] + i)

Non so circa il vostro ragionamento per la memorizzazione dei numeri interi come diff - rcoder ha dato una buona risposta sul perché questo genere non è più efficiente di memorizzazione dei numeri interi stessi - ma se non c'è bisogno di avere accesso ai l'intera lista in una sola volta, è più efficiente della memoria-saggio per l'utilizzo di un generatore. Dal momento che si dice che questo è un "grande lista", è possibile risparmiare un sacco di memoria in questo modo, invece di allocare l'intera lista in una sola volta. Ecco una comprensione generatore per ottenere la vostra lista di selezione indietro:

start = 1005
def mod_start(x):
    global start
    start += x
    return start
int_generator = (mod_start(i) for i in diffs)

È quindi possibile iterare su int_generator come si farebbe con qualsiasi lista, senza avere la lista intera in memoria in una sola volta. Si noti, tuttavia, che non si può pedice o tagliare un generatore, ma lo si può utilizzare in molte situazioni utili.

Si può ripulire l'esempio in modo che la variabile di avvio non ha bisogno di essere globale. Semplicemente non può essere locale alla funzione mod_start.

Modifica non è necessario usare la comprensione del generatore per ottenere un generatore. È inoltre possibile utilizzare una funzione di generatore con l'espressione resa, come THC4k ha fatto. Che evita il problema scope delle variabili di partenza ed è probabilmente un po 'più pulito. È inoltre possibile ottenere un elenco da un generatore in qualsiasi momento passando alla lista () funzione built-in.

Nessun commento sulle prestazioni di questo, ma è possibile utilizzare ridurre qui.

start = 1005
diffs = [-1,-1,1,2]
reduce(lambda undiffed_list, diff: undiffed_list + [undiffed_list[-1] + diff],diffs,[start])

si ottiene ciò che si vuole.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top