forma eficiente de utilizar lambda de pitón, mapa
-
20-09-2019 - |
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.
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.