Inset a matrix of zeros across another matrix's diagonal - python

Watered down example
Consider I have the following matrix A,
1 2 4 3
1 7 3 6
2 4 1 1
6 9 3 6
I would want to convert it to the matrix B which looks like,
0 0 4 4
0 0 3 6
2 4 0 0
6 9 0 0
So basically I would want to have, let's say a 2x2 matrix of zeros in the diagonal of the 4x4 matrix given above.
Need for a general solution
What i have provided above is just an example and I am going to use (1296, 1296) sized matrix as input and I want to inset a 3x3 matrix of zeros inside its diagonal.
What I have done so far?
A simple range based loop and then setting values to zero like so,
for i in range(0, mat.shape[0] - 1, 3):
mat[i][i] = 0
mat[i][i + 1] = 0
mat[i][i + 2] = 0
mat[i + 1][i] = 0
mat[i + 1][i + 1] = 0
mat[i + 1][i + 2] = 0
mat[i + 2][i] = 0
mat[i + 2][i + 1] = 0
mat[i + 2][i + 2] = 0
I completely understand that this is a very crude and nasty way to do it. Please suggest a fast and "numpy" way of doing this.

You could try something like this:
start=0
stop=1296
step=3
for i in np.arange(start=start, stop=stop, step=step):
mat[i:i+step, i:i+step] = 0

Here is a loop free solution:
mat = np.ones((12,12))
np.einsum('ijik->ijk',mat.reshape((4,3,4,3)))[...] = 0
mat
# array([[0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
# [0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
# [0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
# [1., 1., 1., 0., 0., 0., 1., 1., 1., 1., 1., 1.],
# [1., 1., 1., 0., 0., 0., 1., 1., 1., 1., 1., 1.],
# [1., 1., 1., 0., 0., 0., 1., 1., 1., 1., 1., 1.],
# [1., 1., 1., 1., 1., 1., 0., 0., 0., 1., 1., 1.],
# [1., 1., 1., 1., 1., 1., 0., 0., 0., 1., 1., 1.],
# [1., 1., 1., 1., 1., 1., 0., 0., 0., 1., 1., 1.],
# [1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0.],
# [1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0.],
# [1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0.]])

This should fix it:
your_matrix = [
[1, 2, 4, 3],
[1, 7, 3, 6],
[2, 4, 1, 1],
[6, 9, 3, 6]
]
def calculate(matrix, x, y, size):
for index, x_row in enumerate(matrix):
for add in range(size):
if index == y+add:
for add_2 in range(size):
x_row[x+add_2] = 0
return matrix
your_matrix = calculate(matrix=your_matrix, x=1, y=1, size=2)
print(your_matrix)
The x, y is on the left top corner of your 0 box and the size is how big you want your box.

Related

Generate all n choose k binary vectors python

Is there any efficient way (numpy style) to generate all n choose k binary vectors (with k ones)?
for example, if n=3 and k=2, then I want to generate (1,1,0), (1,0,1), (0,1,1).
Thanks
I do not know how efficient this is, but here is a way:
from itertools import combinations
import numpy as np
n, k = 5, 3
np.array(
[
[1 if i in comb else 0 for i in range(n)]
for comb in combinations(np.arange(n), k)
]
)
>>>
array([[1., 1., 1., 0., 0.],
[1., 1., 0., 1., 0.],
[1., 1., 0., 0., 1.],
[1., 0., 1., 1., 0.],
[1., 0., 1., 0., 1.],
[1., 0., 0., 1., 1.],
[0., 1., 1., 1., 0.],
[0., 1., 1., 0., 1.],
[0., 1., 0., 1., 1.],
[0., 0., 1., 1., 1.]])

Fill a matrix from a matrix of indices

I want to fill a matrix from an array of indices :
import numpy as np
indx = [[0,1,2],[1,2,4],[0,1,3],[2,3,4],[0,3,4]]
x = np.zeros((5,5))
for i in range(5):
x[i,indx[i]] = 1.
The result is :
array([[ 1., 1., 1., 0., 0.],
[ 0., 1., 1., 0., 1.],
[ 1., 1., 0., 1., 0.],
[ 0., 0., 1., 1., 1.],
[ 1., 0., 0., 1., 1.]])
As desired.
Question
Is there a way to do this in pure python/numpy without looping ?
Use advanced-indexing after intialization -
x[np.arange(len(indx))[:,None], indx] = 1

How to generate and apply a square mask in numpy

I really just can't grok how masks work in Numpy.
I create a mask like
import numpy
def make_mask(center_x,center_y,len_x,len_y):
x,y = numpy.ogrid[:len_x, :len_y]
mask = (center_x-x)**2 + (center_y-y)**2
return mask
Then I attempt to use it
>>>a = numpy.ones((10,10))
>>>mask = make_mask(2,2,2,2,2)
>>>a[mask] = 0
>>>a
array([[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1],
[0,0,0,0,0,0,0,0],
[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1],
[0,0,0,0,0,0,0,0],
[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1],
[0,0,0,0,0,0,0,0]])
What I was expecting was something like
>>>a
array([[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1],
[1,1,0,0,1,1,1,1],
[1,1,0,0,1,1,1,1],
[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1]])
I've try a few different versions of the function. I just can't get the desired behavior. What am I doing wrong. I really don't understand how a 2D matrix indexes a 2D matrix.
Your mask is setting the 2nd, 5th and 8th rows to 0; the array is being flattened since it does not have the same shape as the array it is masking. It is being applied as:
a[2] = 0
a[5] = 0
a[8] = 0
I think you were expecting something more like:
mask = numpy.ones_like(a)
mask[center_y:center_y + len_y, center_x:center_x + len_x] = 0
which has the same size as the array you are tring to mask and gives the expected result.
If you take a look at what your make_mask function does, when written like this:
def make_mask(index_x,index_y,len_x,len_y):
x,y = numpy.ogrid[:len_x, :len_y]
mask = (index_x-x)**2 + (index_y-y)**2
return mask
you will see that you get
array([[8, 5],
[5, 2]])
and when you index a 10x10 matrix with that 2x2 matrix, I believe it treats all the values in that matrix as indexing the larger matrix by row. Which is why you see the 2nd row with all zeros, the 5th row with all zeros and the 8th row with all zeros.
To get the effect you desire, you can just use the indices you have, you don't even need a function:
a[startx:startx+lenx, starty:starty+leny] = 0
which gives:
array([[ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
[ 1., 1., 0., 0., 1., 1., 1., 1., 1., 1.],
[ 1., 1., 0., 0., 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]])

Numpy: Replace every n element in the first half of an array

If I have a numpy array and want to replace every nth element to 0 in the first half of the array( no change in the second half), how can I do this efficiently? Now my code is not efficient enough:
for i in xrange(1,half,n):
s[i] = 0
Just use a[:a.size//2:n] = 0. e.g.:
a = np.ones(10)
a[:a.size//2:2] = 0
a
array([ 0., 1., 0., 1., 0., 1., 1., 1., 1., 1.])
Another example:
a = np.ones(20)
n = 3
a[:a.size//2:n] = 0
a
array([ 0., 1., 1., 0., 1., 1., 0., 1., 1., 0., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1.])
You could slice the array by doing something like:
import numpy as np
# make an array of 11 elements filled with zeros
my_arr = np.zeros(11)
# get indexes to change in the array. range is: range(start, stop[, step])
a = range(0, 5, 2)
# print the original array
print my_arr
# Change the array
my_arr[a] = 1
# print the changes
print my_arr
Outputs:
array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
array([ 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0.])

Scipy boundary conditions on a sparse matrix pattern

My system is best described by a diagonal sparse matrix (Poisson). I have my diagonal sparse matrix, however, I want to change the boundary conditions (ie the "edges" of my matrix) to zero. It must be a common situation where a modeler wants to describe a system in a sparse diagonal matrix with distinct boundary conditions, is there a best practice for doing this?
[[0,0,0,0,..0],
[0,2,1,0,..0],
[0,1,2,1,..0],
...
[0,0,0,0,..0]]
It depends on which sparse matrix format you use. Apparently lil_matrix and dok_matrix can use slice assignments.
To construct a matrix efficiently, use either lil_matrix (recommended)
or dok_matrix. The lil_matrix class supports basic slicing and fancy
indexing with a similar syntax to NumPy arrays.
Which makes this rather easy:
In : x = scipy.sparse.lil_matrix(np.ones((6,6)))
In : x.todense()
Out:
matrix([[ 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1., 1.]])
In : x[:, 0] = 0
In : x[:, -1] = 0
In : x[0, :] = 0
In : x[-1, :] = 0
In : x.todense()
Out:
matrix([[ 0., 0., 0., 0., 0., 0.],
[ 0., 1., 1., 1., 1., 0.],
[ 0., 1., 1., 1., 1., 0.],
[ 0., 1., 1., 1., 1., 0.],
[ 0., 1., 1., 1., 1., 0.],
[ 0., 0., 0., 0., 0., 0.]])
PS: FYI, your matrix is called tridiagonal, not diagonal.

Categories

Resources