I\'m using the following code to create a list of indices for an array. However, I would like the index to run in Fortran order i.e. the inner loop being the faster varying
You could generate those indices with np.indices
and then a transpose and a reshape does the job -
np.indices((32,30)).T.reshape(-1,2)
Sample output -
In [36]: np.indices((32,30)).T.reshape(-1,2)
Out[36]:
array([[ 0, 0],
[ 1, 0],
[ 2, 0],
...,
[29, 29],
[30, 29],
[31, 29]])
Runtime test -
In [74]: points = [32,30]
# @218's soln
In [75]: %timeit np.transpose(np.nonzero(np.ones(points[::-1])))[:,::-1]
100000 loops, best of 3: 18.6 µs per loop
In [76]: %timeit np.indices((points)).T.reshape(-1,2)
100000 loops, best of 3: 16.1 µs per loop
In [77]: points = [320,300]
# @218's soln
In [78]: %timeit np.transpose(np.nonzero(np.ones(points[::-1])))[:,::-1]
100 loops, best of 3: 2.14 ms per loop
In [79]: %timeit np.indices((points)).T.reshape(-1,2)
1000 loops, best of 3: 1.26 ms per loop
Further performance boost
We can optimize it further by using flipped points
with np.indices
and then using np.column_stack
to create the final 2
columns array. Let's time and verify it against the already proposed one. Listing those two approaches below -
def app1(points):
return np.indices((points)).T.reshape(-1,2)
def app2(points):
R,C = np.indices((points[::-1]))
return np.column_stack((C.ravel(), R.ravel()))
Timings -
In [146]: points = [32,30]
In [147]: np.allclose(app1(points), app2(points))
Out[147]: True
In [148]: %timeit app1(points)
100000 loops, best of 3: 14.8 µs per loop
In [149]: %timeit app2(points)
100000 loops, best of 3: 17.4 µs per loop
In [150]: points = [320,300]
In [151]: %timeit app1(points)
1000 loops, best of 3: 1.1 ms per loop
In [152]: %timeit app2(points)
1000 loops, best of 3: 822 µs per loop
So, this one's better on bigger shapes.
We will make it generic so that we could work with as many params as given, like so -
def get_combinations(params, order='right'):
# params : tuple of input scalars that denotes sizes
# The order arg is used for the LSB position. So, with order='right', the
# rightmost column is the least significant, hence it will change the most
# when going through the rows. For order='left', the leftmost column
# would change the most.
all_indices = np.indices(params)
if order=='right':
return np.moveaxis(all_indices,0,-1).reshape(-1,len(params))
elif order=='left':
return all_indices.T.reshape(-1,len(params))
else:
raise Exception('Wrong side value!')
Sample case runs -
In [189]: get_combinations((2,3), order='left')
Out[189]:
array([[0, 0],
[1, 0],
[0, 1],
[1, 1],
[0, 2],
[1, 2]])
In [191]: get_combinations((2,3,2), order='right')
Out[191]:
array([[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[0, 2, 0],
[0, 2, 1],
[1, 0, 0],
[1, 0, 1],
[1, 1, 0],
[1, 1, 1],
[1, 2, 0],
[1, 2, 1]])