Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping/unwrapping a vector along array diagonals

Tags:

python

numpy

I've been searching for a way (more efficient that just writing loops to traverse the matrix) to create matrices from elements given in a wrapped diagonal order, and to extract values back out in this order. As an example, given a = [2,3,4,5,6,7], I would like to be able to generate the array

[  0,  2,  5,  7,
   0,  0,  3,  6,
   0,  0,  0,  4,
   0,  0,  0,  0]

and also be able to re-extract a from that array.

scipy.sparse.diags achieves something a lot like this but as the name implies is intended for sparse arrays. Is there any sort of functionality in numpy that provides for this, or some form of diagonal-based indexing? Or maybe some type of array transformation that would make this more feasible?

like image 713
TheONP Avatar asked Apr 01 '13 18:04

TheONP


3 Answers

Keeping with the approach that Josh Adel proposes, if you want to keep your data ordered by diagonals, not rows, you just need to mess a little around with the return of np.triu_indices to build your own index generation routine:

def my_triu_indices(n, k=0):
    rows, cols = np.triu_indices(n, k)
    rows = cols - rows - k
    return rows, cols

And now you can do:

>>> a = np.array([2,3,4,5,6,7])
>>> b = np.zeros((4, 4), dtype=a.dtype)
>>> b[my_triu_indices(4, 1)] = a
>>> b
array([[0, 2, 5, 7],
       [0, 0, 3, 6],
       [0, 0, 0, 4],
       [0, 0, 0, 0]])
>>> b[my_triu_indices(4, 1)]
array([2, 3, 4, 5, 6, 7])
like image 138
Jaime Avatar answered Oct 23 '22 04:10

Jaime


If you're willing to order a a bit differently you could do something like:

import numpy as np
a = [2,5,7,3,6,4]
b = np.zeros((4,4))
b[np.triu_indices(4,1)] = a
In [11]: b
Out[11]:
array([[ 0.,  2.,  5.,  7.],
       [ 0.,  0.,  3.,  6.],
       [ 0.,  0.,  0.,  4.],
       [ 0.,  0.,  0.,  0.]])

and then you can extract those values out via:

In [23]: b[np.triu_indices(4,1)]
Out[23]: array([ 2.,  5.,  7.,  3.,  6.,  4.])
like image 3
JoshAdel Avatar answered Oct 23 '22 02:10

JoshAdel


This is not straightforward but should work. If we breakdown how numpy finds diagonal indices we can rebuild it to get what you want.

def get_diag_indices(s,k):
    n = s
    if (k >= 0):
        i = np.arange(0,n-k)
        fi = i+k+i*n
    else:
        i = np.arange(0,n+k)
        fi = i+(i-k)*n
    return fi

indices=np.hstack(([get_diag_indices(4,1+x) for x in range(3)]))
a=np.array([2, 3, 4, 5, 6, 7])
out=np.zeros((4,4))

>>> out.flat[indices]=a
>>> out
array([[ 0.,  2.,  5.,  7.],
       [ 0.,  0.,  3.,  6.],
       [ 0.,  0.,  0.,  4.],
       [ 0.,  0.,  0.,  0.]])

>>> out.flat[indices]
array([ 2.,  3.,  4.,  5.,  6.,  7.])
like image 2
Daniel Avatar answered Oct 23 '22 02:10

Daniel