Reputation: 1330
I have a 1d np.array
. Its length may vary according to user input, but it will always stay single-dimesional.
Please advise if there is an efficient way to create a symmetric 2d np.array
from it? By 'symmetric' I mean that its elements will be according to the rule k[i, j] = k[j, i].
I realise it is possible to do with a python for
loop and list
s, but that is very inefficient.
Many thanks in advance!
EXAMPLE:
For example, we have x = np.array([1, 2, 3])
. The desired result should be
M = np.array([[1, 2, 3],
[2, 1, 2],
[3, 2, 1])
Upvotes: 2
Views: 7077
Reputation: 2132
You want a Toeplitz matrix.
import numpy as np
from scipy.linalg import toeplitz
# Define the array
x = np.array([1, 2, 3])
# Define the first column and first row for the Toeplitz matrix
first_col = np.array([1, 2, 3]) # Directly use the array as first column
first_row = np.array([1, 2, 3]) # Directly use the array as first row
# Generate the Toeplitz matrix
result = toeplitz(first_col, first_row)
print(result)
[[1 2 3]
[2 1 2]
[3 2 1]]
Upvotes: 0
Reputation: 67507
What you're looking for is a special Toeplitz matrix and easy to generate with scipy
from numpy import concatenate, zeros
from scipy.linalg import toeplitz
toeplitz([1,2,3])
array([[1, 2, 3],
[2, 1, 2],
[3, 2, 1]])
another special matrix interpretation can be using Hankel matrix, which will give you minimum dimension square matrix for a given array.
from scipy.linalg import hankel
a=[1,2,3]
t=int(len(a)/2)+1
s=t-2+len(a)%2
hankel(a[:t],a[s:])
array([[1, 2],
[2, 3]])
Upvotes: 6
Reputation: 221584
Interpretation #1
Seems like you are reusing elements at each row. So, with that sort of idea, an implementation using broadcasting
would be -
def symmetricize(arr1D):
ID = np.arange(arr1D.size)
return arr1D[np.abs(ID - ID[:,None])]
Sample run -
In [170]: arr1D
Out[170]: array([59, 21, 70, 10, 42])
In [171]: symmetricize(arr1D)
Out[171]:
array([[59, 21, 70, 10, 42],
[21, 59, 21, 70, 10],
[70, 21, 59, 21, 70],
[10, 70, 21, 59, 21],
[42, 10, 70, 21, 59]])
Interpretation #2
Another interpretation I had when you would like to assign the elements from the input 1D
array into a symmetric 2D
array without re-use, such that we would fill in the upper triangular part once and then replicate those on the lower triangular region by keeping symmetry between the row and column indices. As such, it would only work for a specific size of it. So, as a pre-processing step, we need to perform that error-checking. After we are through the error-checking, we will initialize an output array and use row and column indices of a triangular array
to assign values once as they are and once with swapped indices to assign values in the other triangular part, thus giving it the symmetry effect.
It seemed like Scipy's squareform
should do be able to do this task, but from the docs, it doesn't look like it supports filling up the diagonal elements with the input array elements. So, let's give our solution a closely-related name.
Thus, we would have an implementation like so -
def squareform_diagfill(arr1D):
n = int(np.sqrt(arr1D.size*2))
if (n*(n+1))//2!=arr1D.size:
print "Size of 1D array not suitable for creating a symmetric 2D array!"
return None
else:
R,C = np.triu_indices(n)
out = np.zeros((n,n),dtype=arr1D.dtype)
out[R,C] = arr1D
out[C,R] = arr1D
return out
Sample run -
In [179]: arr1D = np.random.randint(0,9,(12))
In [180]: squareform_diagfill(arr1D)
Size of 1D array not suitable for creating a symmetric 2D array!
In [181]: arr1D = np.random.randint(0,9,(10))
In [182]: arr1D
Out[182]: array([0, 4, 3, 6, 4, 1, 8, 6, 0, 5])
In [183]: squareform_diagfill(arr1D)
Out[183]:
array([[0, 4, 3, 6],
[4, 4, 1, 8],
[3, 1, 6, 0],
[6, 8, 0, 5]])
Upvotes: 8