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.
Related
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.]])
I'm working with 3D boolean arrays that mask a volume. My goal is to take a mask and reduce the area of the mask by some margin, m, in all dimensions.
Is there an easy way to do this using some common libraries (numpy, scipy, pandas, etc..)?
I found some code online that uses multiple for loops to expand a mask by one dimension. This works for the expansion case but I feel like there is a more compact way out there.
Here is a minimum example of what I am looking for in 2D.
Original
array([[0., 0., 1., 0., 0.],
[0., 1., 1., 1., 0.],
[1., 1., 1., 1., 1.],
[0., 1., 1., 1., 0.],
[0., 0., 1., 0., 0.]])
Uniform reduction by 1 pixel
array([[0., 0., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 1., 1., 1., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 0., 0.]])
But I want this to be in 3D space. Thank you for any input.
You might be looking for scipy.ndimage.binary_erosion(a):
a = np.array([
[0., 0., 1., 0., 0.],
[0., 1., 1., 1., 0.],
[1., 1., 1., 1., 1.],
[0., 1., 1., 1., 0.],
[0., 0., 1., 0., 0.]
])
b = scipy.ndimage.binary_erosion(a) # returns an array of bool
Note that this will erode internal surfaces too
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
I have an application where I have to process 1000's of 2D arrays. The result of the processed array is based on half of a Kings Move neighborhood in the original array. I'm trying to avoid loops if I can due to speed considerations. So, here is an example Numpy Array:
array([[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 1., 1., 1., 0., 0., 0., 0.],
[ 0., 0., 0., 1., 5., 5., 5., 5., 0., 0., 0.],
[ 0., 1., 5., 5., 1., 1., 1., 1., 1., 0., 0.],
[ 5., 5., 5., 5., 1., 5., 1., 1., 1., 1., 0.],
[ 1., 1., 1., 5., 1., 1., 5., 5., 1., 1., 0.],
[ 5., 1., 5., 1., 1., 5., 5., 5., 1., 5., 0.],
[ 0., 5., 1., 5., 1., 1., 1., 1., 1., 0., 0.],
[ 0., 0., 1., 1., 1., 1., 1., 1., 0., 0., 0.],
[ 0., 0., 0., 1., 5., 5., 5., 0., 0., 0., 0.]])
At each element, I want the sum of the cell right above it, the upper right diagonal element, the cell to the immediate right and the lower diagonal. So, using the element at [6][0] I would want to sum 1 + 1 + 1 + 5.
Of course, I also have to handle the edge cases where one of the 4 cells is not there. I have started with the padded zeros on top and far right to manage some of that but I'm stuck right now. Any advice would be much appreciated!
What you're doing can be viewed as performing a convolution with a particular convolution kernel. Here's a solution using the scipy convolve2d function:
import numpy as np
import scipy as sp
import scipy.signal
x = np.random.randint(5,size=(10,10))
kernel = np.array([[0,1,1],[0,0,1],[0,0,1]])
kernel = np.fliplr(np.flipud(kernel))
check = sp.signal.convolve2d(x,kernel,mode='same')
print x
print check
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.]])