I need to create a 2D array where each row may start and end with a different number. Assume that first and last element of each row is given and all other elements are just interpolated according to length of the rows In a simple case let’s say I want to create a 3X3 array with same start at 0 but different end given by W below:
array([[ 0., 1., 2.], [ 0., 2., 4.], [ 0., 3., 6.]])
Is there a better way to do this than the following:
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
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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 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')
Method 2
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.]])
Method 3
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 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
============
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).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. ]])
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).T 1 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
.
Method 4
I extended a bit of the functionality based on @Divakar’s solutions. It sacrifices some speed but now is compatible for different lengths of N
instead of only scalar. Plus, this version it faster than @Saullo’s sollution.
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)]
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0