This question already has answers here:
Toeplitz matrix of toeplitz matrix
(3 answers)
Closed 3 years ago.
I want to create a square matrix like this one where its element is a square matrix either the B square matrix or the negative identity matrix or zeros. I have created the B matrix as well as the -I and also I have created a Z matrix of zeros. B, I and Z squares matrices with same n1*n1 (or n2 * n2) dimensions and the final matrix I want to have n*n dimensions where n = n1 * n2
If for example B, I and Z are 4*4 the final will be 16*16
I know how to concatenate and stack matrices but I don't know how to implement this better since need to make the below process 64! times.
for iter in range(64):
if iter == 0:
temp = B
temp = np.hstack((temp, I))
temp = np.hstack((temp, Z))
temp = np.hstack((temp, Z))
if iter == 1:
temp2 = I
temp2 = np.hstack((temp2, B))
temp2 = np.hstack((temp2, I))
temp2 = np.hstack((temp2, Z))
if iter == 2:
temp3 = Z
temp3 = np.hstack((temp3, I))
temp3 = np.hstack((temp3, B))
temp3 = np.hstack((temp3, I))
if iter == 3:
temp4 = Z
temp4 = np.hstack((temp4, Z))
temp4 = np.hstack((temp4, I))
temp4 = np.hstack((temp4, B))
.......
........
........
st1 = np.vstack((temp, temp2))
st2 = np.vstack((st1, temp3))
.......
Can I save n*n matrices into array elements and then concatenate or stack them?
Depending on whether you are dealing with numpy arrays or lists, you can use the following example to append arrays:
import numpy as np
x = np.array([[11.1, 12.1, 13.1], [21.1, 22.1, 23.1]])
print(x.shape)
y = np.array([[11.2, 12.2],[21.2, 22.2]])
print(y.shape)
z = np.append(x,y, axis=1)
print(z.shape)
print(z)
Please note that as mentioned by #user2699, the numpy append could get slow for large array sizes (Fastest way to grow a numpy numeric array).
With lists, you can use the append command:
x = [1, 2, 3]
x.append([4, 5])
print (x) #
This example is taken from: Difference between append vs. extend list methods in Python
np.block helps you create an array like this:
In [109]: B =np.arange(1,5).reshape(2,2)
In [110]: I =np.eye(2).astype(int)
In [111]: Z = np.zeros((2,2),int)
In [112]: np.block?
In [113]: np.block([[B,I,Z,Z],[I,B,I,Z],[Z,I,B,I],[Z,Z,I,B]])
Out[113]:
array([[1, 2, 1, 0, 0, 0, 0, 0],
[3, 4, 0, 1, 0, 0, 0, 0],
[1, 0, 1, 2, 1, 0, 0, 0],
[0, 1, 3, 4, 0, 1, 0, 0],
[0, 0, 1, 0, 1, 2, 1, 0],
[0, 0, 0, 1, 3, 4, 0, 1],
[0, 0, 0, 0, 1, 0, 1, 2],
[0, 0, 0, 0, 0, 1, 3, 4]])
block does a nested sequence of concatenate starting with the innermost lists. Previous versions used hstack on the inner lists, and vstack of those results.
In [118]: np.vstack((np.hstack([B,I,Z,Z]),np.hstack([I,B,I,Z])))
Out[118]:
array([[1, 2, 1, 0, 0, 0, 0, 0],
[3, 4, 0, 1, 0, 0, 0, 0],
[1, 0, 1, 2, 1, 0, 0, 0],
[0, 1, 3, 4, 0, 1, 0, 0]])
The lists of lists in [113] could be constructed with code, using the desired sizes, but I won't go into those details.
Another approach is to create a np.zeros((8,8)) target array, and fill in the desired blocks. Maybe even better to make a np.zeros((4,2,4,2)), fill that, and reshape later.
In [119]: res = np.zeros((4,2,4,2),int)
In [120]: res[np.arange(4),:,np.arange(4),:] = B
In [121]: res[np.arange(3),:,np.arange(1,4),:] = I
In [122]: res[np.arange(1,4),:,np.arange(3),:] = I
In [124]: res.reshape(8,8)
Out[124]:
array([[1, 2, 1, 0, 0, 0, 0, 0],
[3, 4, 0, 1, 0, 0, 0, 0],
[1, 0, 1, 2, 1, 0, 0, 0],
[0, 1, 3, 4, 0, 1, 0, 0],
[0, 0, 1, 0, 1, 2, 1, 0],
[0, 0, 0, 1, 3, 4, 0, 1],
[0, 0, 0, 0, 1, 0, 1, 2],
[0, 0, 0, 0, 0, 1, 3, 4]])
Related
I want to extract off-block-diagonal elements from a block-diagonal matrix, i.e.
import numpy as np
import scipy as sp
A = np.array([
[1, 1, 1],
[1, 1, 1],
[1, 1, 1]
])
B = np.array([
[2, 2],
[2, 2]
])
C = np.array([
[3]
])
D = sp.linalg.block_diag(A, B, C)
print(D)
>>> array([[1, 1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 0],
[0, 0, 0, 2, 2, 0],
[0, 0, 0, 2, 2, 0],
[0, 0, 0, 0, 0, 3]])
So, I need to extract the elements over the diagonal that do not belong to blocks, i.e. that ones which are zeros in D.
How to achieve that?
A straightforwad solution based on loops, can one make it better avoiding loops?
Taking upper triangle and then taking non-zero values is not what I want as it will not allow me to get indices for the original block matrix D, but only for it's upper triangle.
def block_triu_indices(block_sizes=None):
n = np.sum(block_sizes)
blocks = []
for block_size in block_sizes:
blocks.append(np.ones((block_size, block_size)))
A = sp.linalg.block_diag(*blocks)
row_idx = []
col_idx = []
for i in range(n):
for j in range(i+1, n):
if A[i,j]==0:
row_idx.append(i)
col_idx.append(j)
return (np.array(row_idx), np.array(col_idx))
block_triu_idx = block_triu_indices([3, 2, 1])
print(block_triu_idx)
>>> (array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 4]),
array([3, 4, 5, 3, 4, 5, 3, 4, 5, 5, 5]))
print(D[block_triu_idx])
>>> array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
As in the title, if I have a matrix a
a = np.diag(np.arange(5))
array([[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 2, 0, 0],
[0, 0, 0, 3, 0],
[0, 0, 0, 0, 4]])
How can I assign a new 4x4 matrix or even 3x4 matrix to a without i-th row and i-th column? Let's say
b = array([[1,1,1,1],
[1,1,1,1],
[1,1,1,1])
I want to slice a and remove the first and second row and the second column of the matrix, which is something in R like
a[c(-1,-2), -2] = b
a =
array([[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[1, 0, 1, 1, 1],
[1, 0, 1, 1, 1],
[1, 0, 1, 1, 1]])
But in python, I tried something like
a[[2,3,4],:][:,[0,1,3,4]]
output:
array([0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]])
This operation won't allow me to assign a new matrix to slices of a.
How can I do that? I really appreciate any help you can provide.
p.s.
I found in this special case, I can assign values by blocks. But what I actually want to ask is when we do slice like a[2:5, [0,2,3,4]], we can get a 3x4 matrix, and assign a new matrix to that position of the matrix. But I want to do is to slice 'a[[0,2,3,4],[0,2,3,4]]` to get a 4x4 matrix or other shapes(the index for row and column may even be random), and assign a new matrix to that position. But numpy gives me a 1d array.
newmatrix = a[[0, 1, 3, 4], :][:, [0, 1, 3, 4]]
Regarding setting the values of a matric part of a larger matrix, I think there is no direct option. But you can create the original matrix around the one to be added:
before = np.array([[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 2, 0, 0],
[0, 0, 0, 3, 0],
[0, 0, 0, 0, 4]])
insert_array = np.array([[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]])
first two rows without second column
first_step = np.delete(before[:2, :], 1, 1)
or
first_step = before[:2, [0, 2, 3, 4]]
appended to insert matrix
second_step = np.insert(insert_array, 0, first_step, axis=0)
second column appended
third_step = np.insert(second_step, 1, before[:, 1], axis=1)
final matrix
third_step = np.array([[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[1, 0, 1, 1, 1],
[1, 0, 1, 1, 1],
[1, 0, 1, 1, 1]])
I can't find a one-step solution to do that. But I think we can assign matrix by block.
a[2:5, 0] = 1
a[2:5, 2:5] = 1
Then I can get what I want.
In numpy you can set the indices of a 1d array to a value
import numpy as np
b = np.array([0, 0, 0, 0, 0])
indices = [1, 3]
b[indices] = 1
b
array([0, 1, 0, 1, 0])
I'm trying to do this with multi-rows and an index for each row in the most programmatically elegant and computationally efficient way possible. For example
b = np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
indices = [[1, 3], [0, 1], [0, 3]]
The desired result is
array([[0, 1, 0, 1, 0],
[1, 1, 0, 0, 0],
[1, 0, 0, 1, 0]])
I tried b[indices] and b[:,indices] but they resulted in an error or undesired result.
From searching, there are a few work arounds, but each tends to need at least 1 loop in python.
Solution 1: Run a loop through each row of the 2d array. The draw back for this is that the loop runs in python, and this part won't take advantage of numpy's c processing.
Solution 2: Use numpy put. The draw back is put works on a flattened version of the input array, so the indices need to be flattened too, and altered by the row size and number of rows, which would use a double for loop in python.
Solution 3: put_along_axis seems to only be able to set 1 value per row, so I would need to repeat this function for the number of values per row.
What would be the most computationally and programatically elegant solution? Anything where numpy would handle all the operations?
In [330]: b = np.zeros((3,5),int)
To set the (3,2) columns, the row indices need to be (3,1) shape (matching by broadcasting):
In [331]: indices = np.array([[1,3],[0,1],[0,3]])
In [332]: b[np.arange(3)[:,None], indices] = 1
In [333]: b
Out[333]:
array([[0, 1, 0, 1, 0],
[1, 1, 0, 0, 0],
[1, 0, 0, 1, 0]])
put along does the same thing:
In [335]: b = np.zeros((3,5),int)
In [337]: np.put_along_axis(b, indices,1,axis=1)
In [338]: b
Out[338]:
array([[0, 1, 0, 1, 0],
[1, 1, 0, 0, 0],
[1, 0, 0, 1, 0]])
On solution to build the indices in each dimension and then use a basic indexing:
from itertools import chain
b = np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
# Find the indices along the axis 0
y = np.arange(len(indices)).repeat(np.fromiter(map(len, indices), dtype=np.int_))
# Flatten the list and convert it to an array
x = np.fromiter(chain.from_iterable(indices), dtype=np.int_)
# Finaly set the items
b[y, x] = 1
It works even for indices lists with variable-sized sub-lists like indices = [[1, 3], [0, 1], [0, 2, 3]]. If your indices list always contains the same number of items in each sub-list then you can use the (more efficient) following code:
b = np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
indices = np.array(indices)
n, m = indices.shape
y = np.arange(n).repeat(m)
x = indices.ravel()
b[y, x] = 1
Simple one-liner based on Jérôme's answer (requires all items of indices to be equal-length):
>>> b[np.arange(np.size(indices)) // len(indices[0]), np.ravel(indices)] = 1
>>> b
array([[0, 1, 0, 1, 0],
[1, 1, 0, 0, 0],
[1, 0, 0, 1, 0]])
I have a numpy array of 0's and 1's.
def random_array(p):
return np.random.choice(2, 6, p=[p, 1-p])
my_matrix = np.array([random_array(j) for j in np.random.uniform(0.3, 1.0, 4)])
I can get indices of all num-zero elements as np.nonzero(my_matrix). May you help me to allocate all these indices to the column number (even column indices will work, where we start from 0 rather 1) in which it is present. For example,
array([[0, 2, 0, 4, 5, 6],
[0, 0, 0, 4, 5, 0],
[1, 2, 0, 4, 0, 0],
[0, 2, 3, 4, 5, 6]])
Here, all 1's have been replaced with the column number. Thus, this is for a matrix whose all non-zero elements were 1. If you can find the indices of column then that would also be fibulas because, I can get the same by adding 1.
Note: I do not wish to use any loop for this task.
If I understand well, you want to replace all no zero elements with their column indices (starting from 1 instead of 0) right ?
Then you can do it like:
idx = np.nonzero(my_matrix)
my_matrix[idx[0], idx[1]] = idx[1]+1
You can multiply your array with another array of the same size containing the corresponding row/column index:
## Dummy data
# Array size
s = (6,4)
# Axis along which we need to calculate the index:
a = 0
# Random binary array
x = np.random.rand(*s).round()
# Get the index along one axis using broadcasting (starting with 1)
x = x*(np.expand_dims(range(s[a]),len(s)-a-1)+1)
In [169]: def random_array(p):
...: return np.random.choice(2, 6, p=[p, 1-p])
...: my_matrix = np.array([random_array(j) for j in np.random.uniform(0.3, 1.0, 4)])
In [170]: my_matrix
Out[170]:
array([[0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 0, 1],
[1, 1, 0, 0, 1, 0]])
Just multiply the range index. By broadcasting (6,) arange is fine for columns:
In [171]: np.arange(1,7)*my_matrix
Out[171]:
array([[0, 0, 0, 0, 0, 0],
[0, 2, 3, 0, 0, 0],
[0, 0, 3, 4, 0, 6],
[1, 2, 0, 0, 5, 0]])
for rows
In [172]: np.arange(1,5)[:,None]*my_matrix
Out[172]:
array([[0, 0, 0, 0, 0, 0],
[0, 2, 2, 0, 0, 0],
[0, 0, 3, 3, 0, 3],
[4, 4, 0, 0, 4, 0]])
I'd like to accumulate indices that point to a m-by-n array to another array of that very shape to produce a heatmap. For example, these indices:
[
[0, 1, 2, 0, 1, 2]
[0, 1, 0, 0, 0, 2]
]
would produce the following array:
[
[2, 0, 0]
[1, 1, 0]
[1, 0, 1]
]
I've managed to succesfully implement an algorithm, but I started wondering, whether there is already a built-in NumPy solution for this kind of problem.
Here's my code:
a = np.array([[0, 1, 2, 0, 1, 2], [0, 1, 0, 0, 0, 2]])
def _gather_indices(indices: np.ndarray, shape: tuple):
heat = np.zeros(shape)
for i in range(indices.shape[-1]):
heat[tuple(indices[:, i])] += 1
Two methods could be suggested.
With np.add.at -
heat = np.zeros(shape,dtype=int)
np.add.at(heat,(a[0],a[1]),1)
Or with tuple() based one for a more aesthetic one -
np.add.at(heat,tuple(a),1)
With bincount -
idx = np.ravel_multi_index(a,shape)
np.bincount(idx,minlength=np.prod(shape)).reshape(shape)
Additionally, we could compute shape using the max-limits of the indices in a -
shape = a.max(axis=1)+1
Sample run -
In [147]: a
Out[147]:
array([[0, 1, 2, 0, 1, 2],
[0, 1, 0, 0, 0, 2]])
In [148]: shape = (3,3)
In [149]: heat = np.zeros(shape,dtype=int)
...: np.add.at(heat,(a[0],a[1]),1)
In [151]: heat
Out[151]:
array([[2, 0, 0],
[1, 1, 0],
[1, 0, 1]])
In [173]: idx = np.ravel_multi_index(a,shape)
In [174]: np.bincount(idx,minlength=np.prod(shape)).reshape(shape)
Out[174]:
array([[2, 0, 0],
[1, 1, 0],
[1, 0, 1]])