Pregunta

necesito para almacenar una gran lista de números enteros en Bigtable (db). Para una mayor eficiencia que los estoy almacenando diff entre los 2 elementos consecutivos.

para, por ejemplo:

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

Almacenamiento de la lista anterior (que en realidad contiene más de 1000k artículos) como

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

Lo más cerca que pude decir,

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

Estoy buscando una manera eficaz para convertir de nuevo en la lista original.

¿Fue útil?

Solución

Los siguientes obras para mí:

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

Uso map creará una nueva matriz del mismo tamaño, lleno de None. También me parece un simple bucle for más legible, y en este caso lo más rápido que puede obtener.

Otros consejos

Para este tipo de grandes estructuras de datos numpy funcionan bien. Para este ejemplo, es más de 200x más rápido (véase más adelante), y un poco más fácil de código, básicamente, sólo

add.accumulate(diff)

Comparación entre numpy y la lista de la manipulación directa:

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)

da

13.4044158459     # for list looping
0.0474112033844   # for numpy accumulate

Realmente, sin embargo, parece que es mejor volver a utilizar un algoritmo de compresión establecida, al igual que se puede hacer fácilmente con PyTables , en lugar de rodar su propia, como parece que está haciendo aquí.

Además, aquí, estoy sugiriendo que lea los datos con espacio para el término inicio antepuesto, en lugar de reconstruir la lista con el término antepuesto, por supuesto, por lo que no tiene que hacer la copia.

Perfecto para generadores:

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 ))

Varios de los otros encuestados tienen implementaciones del algoritmo razonables que solicitó, pero estoy claro sobre exactamente qué problema es lo que realmente estamos tratando de resolver.

A menos que los números que se almacenan son muy grandes (es decir, desbordar un entero y requieren bignums), el listado de diferencias no le gana ningún eficiencia - un entero es un número entero de la POV de ejecución de Python, por lo que su ejemplo " lista diff" de [-1, -1, 1, 2] consumirá apenas tanto la memoria como la [1005, 1004, 1003, 1004, 1006] lista original.

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

Ahora trata de:

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

Como mshsayem sugirió, uso listas por comprensión - son generalmente más rápido que los bucles o mapa / lambdas (de acuerdo hacer el libro de Mark Lutz Learning Python)

.

Si realmente desea utilizar una solución más FP-ish, la función apropiada sería "exploración", cosa que [creo] no se ha implementado en Python por lo que tendría que aplicar por sí mismo (que no es un disco tarea).

"exploración" es básicamente una reducir, pero en lugar de reducir la lista a un solo valor, almacena el resultado de cada una "repetición" en una nueva lista.

Si lo implementaron, se podría hacer algo como:

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

A pesar de que no entiendo por qué esto debería ser más eficiente, estoy bastante seguro de un bucle for le dará el mejor rendimiento:

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

No sé acerca de su razonamiento para almacenar los números enteros como diffs - rcoder dio una buena respuesta acerca de por qué esto no es generalmente más eficiente que el almacenamiento de los mismos enteros - pero si usted no necesita tener acceso a la lista completa de una vez, que es la memoria a gota más eficiente para que pueda utilizar un generador. Desde que decir que esto es una "lista grande", usted puede ahorrar una gran cantidad de memoria de esta manera, en lugar de asignar la lista completa de una vez. Aquí hay una comprensión del generador para obtener su lista de vuelta:

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

A continuación, puede iterar sobre int_generator como lo haría con cualquier lista, sin tener toda la lista en la memoria a la vez. Tenga en cuenta, sin embargo, que no se puede cortar el subíndice o un generador, pero se puede usar en muchas situaciones útiles.

Puede limpiar el ejemplo para que la variable de arranque no tiene que ser global. Simplemente no puede ser local a la función mod_start.

Editar No tiene que utilizar la comprensión del generador para obtener un generador. También puede utilizar una función de generador con la expresión de rendimiento, al igual que lo hizo THC4k. Esto evita el problema ámbito de las variables de inicio y es probablemente un poco más limpio. También puede obtener una lista de un generador en cualquier momento de pasarlo a la lista () función incorporada.

No hay comentarios en el rendimiento de este, pero se puede utilizar reducir aquí.

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

se obtiene lo que quiere.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top