Vectorized NumPy linspace for multiple start and stop values
Here's an approach using 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]
Sample run -
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]])
Let's leverage multi-core!
We can leverage multi-core
with numexpr
module for large data and to gain memory efficiency and hence performance -
import numexpr as nedef 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')
NumPy >= 1.16.0:
It is now possible to supply array-like values to start
and stop
parameters of the np.linspace
.
For the example given in the question the syntax would be:
>>> np.linspace((0, 0, 0), (2, 4, 6), 3, axis=1)array([[0., 1., 2.], [0., 2., 4.], [0., 3., 6.]])
New axis
parameter specifies in which direction data will be generated. By default it is 0
:
>>> np.linspace((0, 0, 0), (2, 4, 6), 3)array([[0., 0., 0.], [1., 2., 3.], [2., 4., 6.]])
Like the OP's this use of linspace
assumes the start is 0 for all rows.
x=np.linspace(0,1,N)[:,None]*np.arange(0,2*N,2)
(edit - this is the transpose of what I should get; either transpose it or switch the use of [:,None]
)
For N=3000, it's noticeably faster than @Divaker's
solution. I'm not entirely sure why.
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 loopIn [133]: timeit create_ranges(np.zeros(N),np.arange(0,2*N,2),N)1 loop, best of 3: 197 ms per loopIn [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
============
With starts and stops I could use:
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).TIn [203]: xOut[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. ]])
With the extra calculations that makes it a bit slower than 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).T1 loop, best of 3: 227 ms per loop
All these solutions are just variations the idea of doing a linear interpolation between the starts
and stops
.