numpy - transpuesta - sumar columnas de una matriz python
Forma vectorizada de calcular dos matrices de productos de puntos por filas con Scipy (5)
Quiero calcular el producto de puntos de dos matrices de la misma dimensión lo más rápido posible. Esta es la forma en que lo estoy haciendo:
import numpy as np
a = np.array([[1,2,3], [3,4,5]])
b = np.array([[1,2,3], [1,2,3]])
result = np.array([])
for row1, row2 in a, b:
result = np.append(result, np.dot(row1, row2))
print result
y por supuesto la salida es:
[ 26. 14.]
Echa un vistazo a numpy.einsum para otro método:
In [52]: a
Out[52]:
array([[1, 2, 3],
[3, 4, 5]])
In [53]: b
Out[53]:
array([[1, 2, 3],
[1, 2, 3]])
In [54]: einsum(''ij,ij->i'', a, b)
Out[54]: array([14, 26])
Parece que einsum
es un poco más rápido que inner1d
:
In [94]: %timeit inner1d(a,b)
1000000 loops, best of 3: 1.8 us per loop
In [95]: %timeit einsum(''ij,ij->i'', a, b)
1000000 loops, best of 3: 1.6 us per loop
In [96]: a = random.randn(10, 100)
In [97]: b = random.randn(10, 100)
In [98]: %timeit inner1d(a,b)
100000 loops, best of 3: 2.89 us per loop
In [99]: %timeit einsum(''ij,ij->i'', a, b)
100000 loops, best of 3: 2.03 us per loop
Encontré esta respuesta y volví a verificar los resultados con Numpy 1.14.3 ejecutándose en Python 3.5. En su mayor parte, las respuestas anteriores son verdaderas en mi sistema, aunque encontré que para matrices muy grandes (vea el ejemplo a continuación), todos los métodos, excepto uno, están tan próximos entre sí que la diferencia de rendimiento no tiene sentido.
Para matrices más pequeñas, encontré que einsum
fue el más rápido por un margen considerable, hasta un factor de dos en algunos casos.
Mi gran ejemplo de matriz:
import numpy as np
from numpy.core.umath_tests import inner1d
a = np.random.randn(100, 1000000) # 800 MB each
b = np.random.randn(100, 1000000) # pretty big.
def loop_dot(a, b):
result = np.empty((a.shape[1],))
for i, (row1, row2) in enumerate(zip(a, b)):
result[i] = np.dot(row1, row2)
%timeit inner1d(a, b)
# 128 ms ± 523 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit np.einsum(''ij,ij->i'', a, b)
# 121 ms ± 402 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit np.sum(a*b, axis=1)
# 411 ms ± 1.99 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit loop_dot(a, b) # note the function call took negligible time
# 123 ms ± 342 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Así que einsum
sigue siendo el más rápido en matrices muy grandes, pero en una cantidad muy pequeña. ¡Sin embargo, parece ser una cantidad estadísticamente significativa (pequeña)!
Jugó un poco con esto y encontré inner1d
el más rápido:
La trama fue creada con perfplot (un pequeño proyecto mío)
import numpy
from numpy.core.umath_tests import inner1d
import perfplot
perfplot.show(
setup=lambda n: (numpy.random.rand(n, 3), numpy.random.rand(n, 3)),
n_range=[2**k for k in range(1, 18)],
kernels=[
lambda data: numpy.sum(data[0] * data[1], axis=1),
lambda data: numpy.einsum(''ij, ij->i'', data[0], data[1]),
lambda data: inner1d(data[0], data[1])
],
labels=[''np.sum(a*b, axis=1)'', ''einsum'', ''inner1d''],
logx=True,
logy=True,
xlabel=''len(a), len(b)''
)
Lo harás mejor evitando el append
, pero no puedo pensar en una manera de evitar el bucle de python. Un Ufunc personalizado tal vez? No creo que numpy.vectorize te ayude aquí.
import numpy as np
a=np.array([[1,2,3],[3,4,5]])
b=np.array([[1,2,3],[1,2,3]])
result=np.empty((2,))
for i in range(2):
result[i] = np.dot(a[i],b[i]))
print result
EDITAR
Según esta respuesta , parece que inner1d
podría funcionar si los vectores en su problema del mundo real son 1D.
from numpy.core.umath_tests import inner1d
inner1d(a,b) # array([14, 26])
Una forma sencilla de hacerlo es:
import numpy as np
a=np.array([[1,2,3],[3,4,5]])
b=np.array([[1,2,3],[1,2,3]])
np.sum(a*b, axis=1)
que evita el bucle de python y es más rápido en casos como:
def npsumdot(x, y):
return np.sum(x*y, axis=1)
def loopdot(x, y):
result = np.empty((x.shape[0]))
for i in range(x.shape[0]):
result[i] = np.dot(x[i], y[i])
return result
timeit npsumdot(np.random.rand(500000,50),np.random.rand(500000,50))
# 1 loops, best of 3: 861 ms per loop
timeit loopdot(np.random.rand(500000,50),np.random.rand(500000,50))
# 1 loops, best of 3: 1.58 s per loop