Requisitos:
- Necesito hacer crecer una matriz arbitrariamente grande a partir de datos.
- Puedo adivinar el tamaño (aproximadamente 100-200) sin garantías de que la matriz se ajuste siempre
- Una vez que ha crecido hasta su tamaño final, necesito realizar cálculos numéricos en él, por lo que preferiría llegar eventualmente a una matriz numérica 2-D.
- La velocidad es fundamental. Por ejemplo, para uno de los 300 archivos, el método update () se llama 45 millones de veces (tarda 150 s más o menos) y el método finalize () se llama 500k veces (toma un total de 106 s) ... tomando un total de 250 s más o menos.
Aquí está mi código:
def __init__(self):
self.data = []
def update(self, row):
self.data.append(row)
def finalize(self):
dx = np.array(self.data)
Otras cosas que probé incluyen el siguiente código ... pero esto es mucho más lento.
def class A:
def __init__(self):
self.data = np.array([])
def update(self, row):
np.append(self.data, row)
def finalize(self):
dx = np.reshape(self.data, size=(self.data.shape[0]/5, 5))
Aquí hay un esquema de cómo se llama esto:
for i in range(500000):
ax = A()
for j in range(200):
ax.update([1,2,3,4,5])
ax.finalize()
# some processing on ax
python
performance
numpy
fodon
fuente
fuente
Respuestas:
Probé algunas cosas diferentes, con sincronización.
import numpy as np
El método que mencionas como lento: (32.094 segundos)
class A: def __init__(self): self.data = np.array([]) def update(self, row): self.data = np.append(self.data, row) def finalize(self): return np.reshape(self.data, newshape=(self.data.shape[0]/5, 5))
Lista de Python ol regular: (0,308 segundos)
class B: def __init__(self): self.data = [] def update(self, row): for r in row: self.data.append(r) def finalize(self): return np.reshape(self.data, newshape=(len(self.data)/5, 5))
Intentando implementar una lista de matrices en numpy: (0.362 segundos)
class C: def __init__(self): self.data = np.zeros((100,)) self.capacity = 100 self.size = 0 def update(self, row): for r in row: self.add(r) def add(self, x): if self.size == self.capacity: self.capacity *= 4 newdata = np.zeros((self.capacity,)) newdata[:self.size] = self.data self.data = newdata self.data[self.size] = x self.size += 1 def finalize(self): data = self.data[:self.size] return np.reshape(data, newshape=(len(data)/5, 5))
Y así es como lo cronometré:
x = C() for i in xrange(100000): x.update([i])
Así que parece que las listas de Python antiguas y regulares son bastante buenas;)
fuente
update
yfinalize
respectivamente. Véase mi calendario revisado que prueba una proporción de 100: de 1update
afinalize
np.append () copia todos los datos de la matriz cada vez, pero la lista aumenta la capacidad en un factor (1,125). list es rápido, pero el uso de memoria es mayor que el de array. Puede usar el módulo de matriz de la biblioteca estándar de Python si le importa la memoria.
Aquí hay una discusión sobre este tema:
Cómo crear una matriz dinámica
fuente
Usando las declaraciones de clase en la publicación de Owen, aquí hay un calendario revisado con algún efecto de finalización.
En resumen, encuentro que la clase C proporciona una implementación que es más de 60 veces más rápida que el método en la publicación original. (disculpas por la pared de texto)
El archivo que utilicé:
#!/usr/bin/python import cProfile import numpy as np # ... class declarations here ... def test_class(f): x = f() for i in xrange(100000): x.update([i]) for i in xrange(1000): x.finalize() for x in 'ABC': cProfile.run('test_class(%s)' % x)
Ahora, los tiempos resultantes:
UN:
903005 function calls in 16.049 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 16.049 16.049 <string>:1(<module>) 100000 0.139 0.000 1.888 0.000 fromnumeric.py:1043(ravel) 1000 0.001 0.000 0.003 0.000 fromnumeric.py:107(reshape) 100000 0.322 0.000 14.424 0.000 function_base.py:3466(append) 100000 0.102 0.000 1.623 0.000 numeric.py:216(asarray) 100000 0.121 0.000 0.298 0.000 numeric.py:286(asanyarray) 1000 0.002 0.000 0.004 0.000 test.py:12(finalize) 1 0.146 0.146 16.049 16.049 test.py:50(test_class) 1 0.000 0.000 0.000 0.000 test.py:6(__init__) 100000 1.475 0.000 15.899 0.000 test.py:9(update) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 100000 0.126 0.000 0.126 0.000 {method 'ravel' of 'numpy.ndarray' objects} 1000 0.002 0.000 0.002 0.000 {method 'reshape' of 'numpy.ndarray' objects} 200001 1.698 0.000 1.698 0.000 {numpy.core.multiarray.array} 100000 11.915 0.000 11.915 0.000 {numpy.core.multiarray.concatenate}
SEGUNDO:
208004 function calls in 16.885 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.001 0.001 16.885 16.885 <string>:1(<module>) 1000 0.025 0.000 16.508 0.017 fromnumeric.py:107(reshape) 1000 0.013 0.000 16.483 0.016 fromnumeric.py:32(_wrapit) 1000 0.007 0.000 16.445 0.016 numeric.py:216(asarray) 1 0.000 0.000 0.000 0.000 test.py:16(__init__) 100000 0.068 0.000 0.080 0.000 test.py:19(update) 1000 0.012 0.000 16.520 0.017 test.py:23(finalize) 1 0.284 0.284 16.883 16.883 test.py:50(test_class) 1000 0.005 0.000 0.005 0.000 {getattr} 1000 0.001 0.000 0.001 0.000 {len} 100000 0.012 0.000 0.012 0.000 {method 'append' of 'list' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1000 0.020 0.000 0.020 0.000 {method 'reshape' of 'numpy.ndarray' objects} 1000 16.438 0.016 16.438 0.016 {numpy.core.multiarray.array}
C:
204010 function calls in 0.244 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.244 0.244 <string>:1(<module>) 1000 0.001 0.000 0.003 0.000 fromnumeric.py:107(reshape) 1 0.000 0.000 0.000 0.000 test.py:27(__init__) 100000 0.082 0.000 0.170 0.000 test.py:32(update) 100000 0.087 0.000 0.088 0.000 test.py:36(add) 1000 0.002 0.000 0.005 0.000 test.py:46(finalize) 1 0.068 0.068 0.243 0.243 test.py:50(test_class) 1000 0.000 0.000 0.000 0.000 {len} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1000 0.002 0.000 0.002 0.000 {method 'reshape' of 'numpy.ndarray' objects} 6 0.001 0.000 0.001 0.000 {numpy.core.multiarray.zeros}
La clase A es destruida por las actualizaciones, la clase B es destruida por los finalizados. La clase C es robusta frente a ambos.
fuente
cProfile
. Es la primera importación y la última línea invocada en mi fragmento de código.hay una gran diferencia de rendimiento en la función que utiliza para la finalización. Considere el siguiente código:
N=100000 nruns=5 a=[] for i in range(N): a.append(np.zeros(1000)) print "start" b=[] for i in range(nruns): s=time() c=np.vstack(a) b.append((time()-s)) print "Timing version vstack ",np.mean(b) b=[] for i in range(nruns): s=time() c1=np.reshape(a,(N,1000)) b.append((time()-s)) print "Timing version reshape ",np.mean(b) b=[] for i in range(nruns): s=time() c2=np.concatenate(a,axis=0).reshape(-1,1000) b.append((time()-s)) print "Timing version concatenate ",np.mean(b) print c.shape,c2.shape assert (c==c2).all() assert (c==c1).all()
El uso de concatenar parece ser dos veces más rápido que la primera versión y más de 10 veces más rápido que la segunda versión.
Timing version vstack 1.5774928093 Timing version reshape 9.67419199944 Timing version concatenate 0.669512557983
fuente
Si desea mejorar el rendimiento con operaciones de lista, eche un vistazo a blist library. Es una implementación optimizada de la lista de Python y otras estructuras.
No lo comparé todavía, pero los resultados en su página parecen prometedores.
fuente