python numpy multidimensional-array vectorization numpy-broadcasting

python - Espacio lineal NumPy vectorizado para mĂșltiples valores de inicio y parada



multidimensional-array vectorization (4)

Necesito crear una matriz 2D donde cada fila pueda comenzar y terminar con un número diferente. Suponga que se da el primer y último elemento de cada fila y que todos los demás elementos solo se interpolan de acuerdo con la longitud de las filas.

array([[ 0., 1., 2.], [ 0., 2., 4.], [ 0., 3., 6.]])

¿Hay una mejor manera de hacer esto que la siguiente?

D=np.ones((3,3))*np.arange(0,3) D=D/D[:,-1] W=np.array([2,4,6]) # last element of each row assumed given Res= (D.T*W).T


NumPy> = 1.16.0:

Ahora es posible proporcionar valores de tipo matriz para start y stop parámetros del np.linspace .

Para el ejemplo dado en la pregunta, la sintaxis sería:

>>> np.linspace((0, 0, 0), (2, 4, 6), 3, axis=1) array([[0., 1., 2.], [0., 2., 4.], [0., 3., 6.]])

El nuevo parámetro del axis especifica en qué dirección se generarán los datos. Por defecto es 0 :

>>> np.linspace((0, 0, 0), (2, 4, 6), 3) array([[0., 0., 0.], [1., 2., 3.], [2., 4., 6.]])


Al igual que los OP, este uso de linspace supone que el inicio es 0 para todas las filas.

x=np.linspace(0,1,N)[:,None]*np.arange(0,2*N,2)

(editar - esta es la transposición de lo que debería obtener; transponerlo o cambiar el uso de [:,None] )

Para N = 3000, es notablemente más rápido que @Divaker''s solución de @Divaker''s . No estoy completamente seguro de por qué.

In [132]: timeit N=3000;x=np.linspace(0,1,N)[:,None]*np.arange(0,2*N,2) 10 loops, best of 3: 91.7 ms per loop In [133]: timeit create_ranges(np.zeros(N),np.arange(0,2*N,2),N) 1 loop, best of 3: 197 ms per loop In [134]: def foo(N): ...: D=np.ones((N,N))*np.arange(N) ...: D=D/D[:,-1] ...: W=np.arange(0,2*N,2) ...: return (D.T*W).T ...: In [135]: timeit foo(3000) 1 loop, best of 3: 454 ms per loop

============

Con arranques y paradas podría usar:

In [201]: starts=np.array([1,4,2]); stops=np.array([6,7,8]) In [202]: x=(np.linspace(0,1,5)[:,None]*(stops-starts)+starts).T In [203]: x Out[203]: array([[ 1. , 2.25, 3.5 , 4.75, 6. ], [ 4. , 4.75, 5.5 , 6.25, 7. ], [ 2. , 3.5 , 5. , 6.5 , 8. ]])

Con los cálculos adicionales que lo hacen un poco más lento que create_ranges .

In [208]: timeit N=3000;starts=np.zeros(N);stops=np.arange(0,2*N,2);x=(np.linspace(0,1,N)[:,None]*(stops-starts)+starts).T 1 loop, best of 3: 227 ms per loop

Todas estas soluciones son solo variaciones de la idea de hacer una interpolación lineal entre los starts y las stops .


Aquí hay un enfoque utilizando la broadcasting :

def create_ranges(start, stop, N, endpoint=True): if endpoint==1: divisor = N-1 else: divisor = N steps = (1.0/divisor) * (stop - start) return steps[:,None]*np.arange(N) + start[:,None]

Ejecución de muestra:

In [22]: # Setup start, stop for each row and no. of elems in each row ...: start = np.array([1,4,2]) ...: stop = np.array([6,7,6]) ...: N = 5 ...: In [23]: create_ranges(start, stop, 5) Out[23]: array([[ 1. , 2.25, 3.5 , 4.75, 6. ], [ 4. , 4.75, 5.5 , 6.25, 7. ], [ 2. , 3. , 4. , 5. , 6. ]]) In [24]: create_ranges(start, stop, 5, endpoint=False) Out[24]: array([[ 1. , 2. , 3. , 4. , 5. ], [ 4. , 4.6, 5.2, 5.8, 6.4], [ 2. , 2.8, 3.6, 4.4, 5.2]])

¡Aprovechemos multi-core!

Podemos aprovechar multi-core con el módulo numexpr para obtener datos de gran tamaño y ganar eficiencia de memoria y, por lo tanto, rendimiento

import numexpr as ne def create_ranges_numexpr(start, stop, N, endpoint=True): if endpoint==1: divisor = N-1 else: divisor = N s0 = start[:,None] s1 = stop[:,None] r = np.arange(N) return ne.evaluate(''((1.0/divisor) * (s1 - s0))*r + s0'')


Extendí un poco de la funcionalidad basada en las soluciones de @ Divakar. Sacrifica algo de velocidad, pero ahora es compatible con diferentes longitudes de N lugar de solo escalar. Además, esta versión es más rápida que la solución de @ Saullo .

def create_ranges_divak(starts, stops, N, endpoint=True): if endpoint==1: divisor = N-1 else: divisor = N steps = (1.0/divisor) * (stops - starts) uni_N = np.unique(N) if len(uni_N) == 1: return steps[:,None]*np.arange(uni_N) + starts[:,None] else: return [step * np.arange(n) + start for start, step, n in zip(starts, steps, N)]