Reputation: 320
I am having trouble of filling a square matrix of n x n
with a list of the size m
.
What I want to achieve is something like this:
arr = np.array([
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]], dtype=float)
And a list like this:
myList= [1, 2, 3, 4, 5, 6, 7]
The output should be:
arr = np.array([
[4, 5, 6, 7],
[3, 4, 5, 6],
[2, 3, 4, 5],
[1, 2, 3, 4]], dtype=float)
My function works, however I think this is not the most elegant way.
def fill_array(img, myList):
arr = np.ones(np.shape(img))
diag_size = (np.shape(img)[0] * 2) - 1
diag_idx = np.median(np.linspace(1, diag_size, diag_size))
# First iterate through the lower half, main diagonal and then upper half
i = 1
while i <= diag_size:
factor = myList[i - 1]
if i < diag_idx:
position = int(diag_idx - i)
np.fill_diagonal(arr[position:, :], factor)
elif i == diag_idx:
np.fill_diagonal(arr[0:, :], factor)
elif i > diag_idx:
position = int(i - diag_idx)
np.fill_diagonal(arr[:, position:], factor)
i += 1
return arr
Is there a better solution for that? Thanks in advance!
Upvotes: 2
Views: 628
Reputation: 30991
First create a function returning indices of the given diagonal:
def kthDiagIndices(a, offs):
rows, cols = np.diag_indices_from(a)
if offs < 0:
return rows[-offs:], cols[:offs]
elif offs > 0:
return rows[:-offs], cols[offs:]
else:
return rows, cols
Then define the function to generate the array as:
def fill_array(img, myList):
arr = np.ones_like(img, dtype=int)
# How many diagonals can be filled
diagNo = min(len(myList), img.shape[0] * 2 - 1)
# Shift from the index in myList to the diagonal offset
diagShift = diagNo // 2
# Fill each diagonal in arr
for i, v in enumerate(myList):
arr[kthDiagIndices(arr, i - diagShift)] = v
return arr
Note that this function is "resistant" to passing too long myList (longer than the number of available diagonals).
Another detail to change is to remove dtype=int, but as I saw, you tested your function on integer values.
To test this function, I created:
img = np.ones((4, 4), dtype=int)
myList = [11, 12, 13, 14, 15, 16, 17, 18]
Then I ran:
fill_array(img, myList)
getting:
array([[14, 15, 16, 17],
[13, 14, 15, 16],
[12, 13, 14, 15],
[11, 12, 13, 14]])
Experiment with shorter myLists.
Upvotes: 0
Reputation: 150765
Let's try as_strided
:
from numpy.lib.stride_tricks import as_strided
out = as_strided(np.array(myList, dtype=arr.dtype),
shape=arr.shape,
strides=arr.strides[1:]*2)[::-1]
Output:
array([[4., 5., 6., 7.],
[3., 4., 5., 6.],
[2., 3., 4., 5.],
[1., 2., 3., 4.]])
Upvotes: 3