I am trying to sort a numpy ndarray clockwise (in ascending order). You can understand it as taking all the values in the array, sort them, then lay them out in a clockwise spiral to a new array with the same shape. The directions of the clockwise spiral are shown below:
For example, say I have an array of
import numpy as np
a1 = np.array(([2, 4, 6],
[1, 5, 3],
[7, 9, 8]))
The expected output is
np.array([[1, 2, 3],
[8, 9, 4],
[7, 6, 5]])
If I have an array of
a2 = np.array(([2, 4, 6],
[1, 5, 3],
[7, 9, 8],
[12, 11, 10]))
The expected output is
np.array([[1, 2, 3],
[10, 11, 4],
[9, 12, 5],
[8, 7, 6]])
What I have tried so far
My idea is to track the row index x
and the column index y
of the moving iterator and the current index cur
of the sorted flattened list sa
. The number of rows to go through (lenr
) and the number of columns to go through (lenc
) are subtracted by 1
once the moving iterator passes lenr
rows horizontally and lenc
columns vertically. Here is the function that I managed to write:
def clockwise_sorted(a, key=None, reverse=False):
nr, nc = a.shape
res = a.tolist()
sa = a.ravel().tolist()
if key is None:
sa.sort(reverse=reverse)
else:
sa.sort(key=key, reverse=reverse)
res[0] = sa[:nc]
cur, lenr, lenc = nc, nr - 1, nc - 1
x, y = 0, nc - 1
while (lenc > 0 and lenr > 0):
# go down, then go left
for _ in range(lenr):
x += 1
res[x][y] = sa[cur]
cur += 1
for _ in range(lenc):
y -= 1
res[x][y] = sa[cur]
cur += 1
lenr -= 1
lenc -= 1
# go up, then go right
for _ in range(lenr):
x -= 1
res[x][y] = sa[cur]
cur += 1
for _ in range(lenc):
y += 1
res[x][y] = sa[cur]
cur += 1
lenr -= 1
lenc -= 1
return np.array(res)
For square matrices, my code works fine:
print(clockwise_sorted(a1))
#[[1 2 3]
# [8 9 4]
# [7 6 5]]
#%timeit 5.98 µs ± 413 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
But it doesn't work for non-square matrices:
print(clockwise_sorted(a2))
#[[ 1 2 3]
# [10 11 4]
# [ 9 9 5]
# [ 8 7 6]]
Clearly, it misses the 12
at [2,1]
.
Please note that I don't expect you to understand and fix my code. I don't like my code so much because I feel like there should be more numpyic ways to do this. Therefore, I want to see some numpyic solutions. However, it would be nice if someone can comment on what is wrong with my code.
([2, 4, 6], [1, 5, 3], [7, 9, 8])
get turned into the output[[1, 2, 3], [8, 9, 4], [7, 6, 5]]
if not by some kind of arbitrary permutation? Based on the diagram I would have expected[2, 4, 6, 3, 8, 9, 7, 1, 5]
. Would you consider editing the question to be a bit more clear about it?