getting the opposite diagonal of a numpy array
Question:
So in numpy arrays there is the built in function for getting the diagonal indices, but I can’t seem to figure out how to get the diagonal starting from the top right rather than top left.
This is the normal code to get starting from the top left:
>>> import numpy as np
>>> array = np.arange(25).reshape(5,5)
>>> diagonal = np.diag_indices(5)
>>> array
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]])
>>> array[diagonal]
array([ 0, 6, 12, 18, 24])
so what do I use if I want it to return:
array([ 4, 8, 12, 16, 20])
Answers:
There is
In [47]: np.diag(np.fliplr(array))
Out[47]: array([ 4, 8, 12, 16, 20])
or
In [48]: np.diag(np.rot90(array))
Out[48]: array([ 4, 8, 12, 16, 20])
Of the two, np.diag(np.fliplr(array))
is faster:
In [50]: %timeit np.diag(np.fliplr(array))
100000 loops, best of 3: 4.29 us per loop
In [51]: %timeit np.diag(np.rot90(array))
100000 loops, best of 3: 6.09 us per loop
Here are two ideas:
step = len(array) - 1
# This will make a copy
array.flat[step:-step:step]
# This will make a veiw
array.ravel()[step:-step:step]
Here is a simple way using numpy slicing. I personally find it not to hard on the eyes (but concede that fliplr
is a little more descriptive!).
Just to highlight this example’s contribution to existing answers, I ran the same simple benchmark.
In [1]: import numpy as np
In [3]: X = np.random.randint(0, 10, (5, 5))
In [4]: X
Out[4]:
array([[7, 2, 7, 3, 7],
[8, 4, 5, 9, 6],
[0, 2, 9, 0, 4],
[8, 2, 1, 0, 3],
[3, 1, 0, 7, 0]])
In [5]: Y = X[:, ::-1]
In [6]: Z1 = np.diag(Y)
In [7]: Z1
Out[7]: array([7, 9, 9, 2, 3])
Now to compare to the current fastest solution given.
In [8]: step = len(X) - 1
In [9]: Z2 = np.take(X, np.arange(step, X.size-1, step))
In [10]: Z2
Out[10]: array([7, 9, 9, 2, 3])
In [11]: np.array_equal(Z1, Z2)
Out[11]: True
Benchmarks
In [12]: %timeit np.diag(X[:, ::-1])
1.92 µs ± 29.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [13]: %timeit step = len(X) - 1; np.take(X, np.arange(step, X.size-1, step))
2.21 µs ± 246 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Initial comparisons indicate that my solution is additionally linear in complexity, while using the second ‘step’ solution is not:
In [14]: big_X = np.random.randint(0, 10, (10000, 10000))
In [15]: %timeit np.diag(big_X[:, ::-1])
2.15 µs ± 96.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [16]: %timeit step = len(big_X) - 1; np.take(big_X, np.arange(step, big_X.size-1, step))
100 µs ± 1.85 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
I generally use this method to either flip images (mirroring them), or to convert between opencv
‘s format of (channels, height, width) to matplotlib
‘s format of (height, width , channels). So for a three dimensional image, it’d simply be flipped = image[:, :, ::-1]
. Of course you can generalise it to flip along any dimension, by putting the ::-1
part in the desired dimension.
So in numpy arrays there is the built in function for getting the diagonal indices, but I can’t seem to figure out how to get the diagonal starting from the top right rather than top left.
This is the normal code to get starting from the top left:
>>> import numpy as np
>>> array = np.arange(25).reshape(5,5)
>>> diagonal = np.diag_indices(5)
>>> array
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]])
>>> array[diagonal]
array([ 0, 6, 12, 18, 24])
so what do I use if I want it to return:
array([ 4, 8, 12, 16, 20])
There is
In [47]: np.diag(np.fliplr(array))
Out[47]: array([ 4, 8, 12, 16, 20])
or
In [48]: np.diag(np.rot90(array))
Out[48]: array([ 4, 8, 12, 16, 20])
Of the two, np.diag(np.fliplr(array))
is faster:
In [50]: %timeit np.diag(np.fliplr(array))
100000 loops, best of 3: 4.29 us per loop
In [51]: %timeit np.diag(np.rot90(array))
100000 loops, best of 3: 6.09 us per loop
Here are two ideas:
step = len(array) - 1
# This will make a copy
array.flat[step:-step:step]
# This will make a veiw
array.ravel()[step:-step:step]
Here is a simple way using numpy slicing. I personally find it not to hard on the eyes (but concede that fliplr
is a little more descriptive!).
Just to highlight this example’s contribution to existing answers, I ran the same simple benchmark.
In [1]: import numpy as np
In [3]: X = np.random.randint(0, 10, (5, 5))
In [4]: X
Out[4]:
array([[7, 2, 7, 3, 7],
[8, 4, 5, 9, 6],
[0, 2, 9, 0, 4],
[8, 2, 1, 0, 3],
[3, 1, 0, 7, 0]])
In [5]: Y = X[:, ::-1]
In [6]: Z1 = np.diag(Y)
In [7]: Z1
Out[7]: array([7, 9, 9, 2, 3])
Now to compare to the current fastest solution given.
In [8]: step = len(X) - 1
In [9]: Z2 = np.take(X, np.arange(step, X.size-1, step))
In [10]: Z2
Out[10]: array([7, 9, 9, 2, 3])
In [11]: np.array_equal(Z1, Z2)
Out[11]: True
Benchmarks
In [12]: %timeit np.diag(X[:, ::-1])
1.92 µs ± 29.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [13]: %timeit step = len(X) - 1; np.take(X, np.arange(step, X.size-1, step))
2.21 µs ± 246 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Initial comparisons indicate that my solution is additionally linear in complexity, while using the second ‘step’ solution is not:
In [14]: big_X = np.random.randint(0, 10, (10000, 10000))
In [15]: %timeit np.diag(big_X[:, ::-1])
2.15 µs ± 96.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [16]: %timeit step = len(big_X) - 1; np.take(big_X, np.arange(step, big_X.size-1, step))
100 µs ± 1.85 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
I generally use this method to either flip images (mirroring them), or to convert between opencv
‘s format of (channels, height, width) to matplotlib
‘s format of (height, width , channels). So for a three dimensional image, it’d simply be flipped = image[:, :, ::-1]
. Of course you can generalise it to flip along any dimension, by putting the ::-1
part in the desired dimension.