python - transpuesta - La forma más rápida de hacer crecer una matriz numérica numpy
numero mas cercano python (5)
Requisitos:
- Necesito hacer crecer un array arbitrariamente grande a partir de los datos.
- Adivino el tamaño (aproximadamente 100-200) sin garantías de que la matriz se ajuste cada vez
- Una vez que crezca hasta su tamaño final, debo realizar cálculos numéricos en él, por lo que preferiría eventualmente llegar a una matriz numpy 2-D.
- La velocidad es crítica. Como ejemplo, para uno de los 300 archivos, el método update () se llama 45 millones de veces (toma 150s más o menos) y el método finalize () se llama 500k veces (toma un total de 106s) ... tomando un total de 250s 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 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
Intenté algunas cosas diferentes, con el tiempo.
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 ordinaria de Python: (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 un arraylist 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 sincronicé:
x = C()
for i in xrange(100000):
x.update([i])
Parece que las viejas listas de Python son bastante buenas;)
Si desea mejorar el rendimiento con operaciones de lista, eche un vistazo a la biblioteca de listas. Es una implementación optimizada de la lista de Python y otras estructuras.
Aún no lo comparé, pero los resultados en su página parecen prometedores.
Usando las declaraciones de clase en la publicación de Owen, aquí hay un tiempo revisado con algún efecto de la 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 el muro 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:
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}
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}
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 las finalizaciones. La Clase C es robusta frente a ambos.
hay una gran diferencia de rendimiento en la función que utiliza para la finalización. Considera 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
np.append () copia todos los datos en la matriz cada vez, pero enumera la capacidad de crecimiento por un factor (1.125). la lista es rápida, pero el uso de la memoria es mayor que la matriz. 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: