How to create a lower diagonal matrix with numpy? - python

How can I generate a lower diagonal matrix of dynamic size with Numpy?
For example, if the size n of the matrix is 4 I would like to obtain such a matrix:
| 0 0 0 0 |
| 1 0 0 0 |
| 0 1 0 0 |
| 0 0 1 0 |

You could create it, by first creating a matrix with zeros, and then fill in the ones:
import numpy as np
# create matrix with zeros
n=4
mat = np.zeros((n,n))
# create indexes for where the 1s belong
rows = np.arange(1,n)
cols = np.arange(n-1)
# fill in the 1s
mat[rows, cols] = 1
output:
[[0. 0. 0. 0.]
[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]]

I found out the shortest way to be using np.eye:
import numpy as np
n = 4
np.eye(n, k=-1, dtype=int)
The output is:
array([[0, 0, 0, 0],
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0]])

Related

Update 2D numpy array using pairs of indices without iteration

Reproducible setup
I have an array of n pairs of indices:
indexarray=\
np.array([[0,2,4,6],
[1,3,5,7]])
I also have a 2D array:
zeros = np.zeros((10,9))
and a list of n values:
values = [1,2,3,4]
I would like to add each kth element from the values list to the element in the zeros list having indeces equal to the kth indices pair
A solution
# works, but for loop is not suitable for real-world use-case
for index, (row, col) in enumerate(indexarray.T):
zeros[row, col] = values[index]
Visualize what I get:
plt.imshow(zeros)
Results as expected.
How can I do this without iteration?
Similar but different questions:
Index 2D numpy array by a 2D array of indices without loops : here the output is an array of pairs of indices, not a 2D array, as here.
Fill 2D numpy array from three 1D numpy arrays : setup is more complicated, they create a 2D array from 1D arrays, unlike here, where we already have a 2D array
numpy arrays: filling and extracting data quickly : way more complicated setup, do not start from 2D array
How to convert List of Lists of Tuples- pairs (index,value) into 2D numpy array : aim is different and for loop is not avoided
Simply use:
import numpy as np
indexarray = np.array([[0, 2, 4, 6],
[1, 3, 5, 7]])
values = [1, 2, 3, 4]
rows, cols = indexarray[0], indexarray[1]
zeros = np.zeros((10, 9))
zeros[rows, cols] = values
print(zeros)
Output
[[0. 1. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 2. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 3. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 4. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]]
An alternative that will add together repeating coordinates, is to use add.at:
np.add.at(zeros, (rows, cols), values)
A second alternative is to use a sparse matrix constructor, for example:
from scipy.sparse import csr_matrix
rows, cols = indexarray[0], indexarray[1]
zeros = csr_matrix((values, (rows, cols)), shape=(10, 9)).toarray()
Output
[[0 1 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 2 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 3 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 4 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]]
You can directly use indexarray in indexing.
r, c = indexarray
zeros[r, c] = values
You can simply use:
zeros[indexarray[0], indexarray[1]] = values

Looking to replace min and max of ndarray in Python

I have the following ndarray :
c_dist = [[0. 5.83095189]
[2.23606798 3.60555128]
[5.83095189 0. ]
[5.83095189 2.82842712]
[4.12310563 2.23606798]]
and I would like for each sub-array to replace the min with 1 and the max with 0, in order to obtain the following :
[[1. 0.]
[1. 0.]
[0. 1.]
[0. 1.]
[0. 1.]]
I used the following :
for i in range(len(c_dist)):
max_of_row = c_dist[i].max()
for elements_of_row in range(len(c_dist[i])):
if c_dist[i][elements_of_row] == max_of_row:
c_dist[i][elements_of_row] = 1
else:
c_dist[i][elements_of_row] = 0
but it is obviously not very elegant.
Is there an python way of doing the comparison array by array please ?
Try this in one line:
c_dist = [[0. ,5.83095189],
[2.23606798 ,3.60555128],
[5.83095189 ,0. ],
[5.83095189 ,2.82842712],
[4.12310563 ,2.23606798]]
new_list = [[int(i<=j), int(i>j)] for i,j in c_dist]
The result will be:
In [6]: new_list
Out[6]: [[1, 0], [1, 0], [0, 1], [0, 1], [0, 1]]
If you have more than 2 columns:
out = c_dist.copy()
np.put_along_axis(out, c_dist.argmax(0), 1)
np.put_along_axis(out, c_dist.argmin(0), 0)
Or if there are multiple max and min values per row:
out = np.where(c_dist == c_dist.max(0, keepdims = True), 1, c_dist)
out = np.where(c_dist == c_dist.min(0, keepdims = True), 0, out)

Producing binary matrix with random lists

so first of all here is my code I have questions about, I feel like the answer will be simple, but I am just too blind to see it
dic = {}
for i in range(0,9):
dic['rand_num{0}'.format(i)] = np.sort(random.sample(range(0,8),5))
mat = np.zeros([8,8])
for rand_num in dic.values():
print(rand_num)
for i in range(0,5):
matrixval = rand_num[i]
#print(matrixval)
for j in range(0,8):
mat[matrixval,j] = 1
print(mat)
I created 8 different lists which will help me determine, where to set a 1 in the matrix.
so eg. if my first list looks like this:
rand_List = [0,3,5,6,7]
the matrix of the first row or column should look like this
matrix = [1, 0, 0, 1, 0, 1, 1, 1]
My position for the matrix value is set in the second loop: for i in range(0,5): matrixval = rand_num[i] . However, as soon I run my code, my matrix just gives me ones instead of ones and zeros. I feel like I have an iteration problem in my last for loop, and I don't know how to fix it.
If anyone could help me, or just give me a hint, how to actually solve this hopefully small mistake of mine, I would really appreciate it.
Hopefully this provides the expected result:
dic = {}
for i in range(0,8):
dic['rand_num{0}'.format(i)] = np.sort(random.sample(range(0,8),5))
mat = np.zeros([8,8])
for j, rand_num in enumerate(dic.values()):
print(rand_num)
for i in range(0,5):
matrixval = rand_num[i]
mat[j,matrixval] = 1
print(mat)
Output:
[1 2 3 5 7]
[0 1 2 5 6]
[0 1 2 3 5]
[1 2 3 4 6]
[0 1 3 4 7]
[1 2 4 5 7]
[0 1 3 4 7]
[1 2 3 4 6]
[[0. 1. 1. 1. 0. 1. 0. 1.]
[1. 1. 1. 0. 0. 1. 1. 0.]
[1. 1. 1. 1. 0. 1. 0. 0.]
[0. 1. 1. 1. 1. 0. 1. 0.]
[1. 1. 0. 1. 1. 0. 0. 1.]
[0. 1. 1. 0. 1. 1. 0. 1.]
[1. 1. 0. 1. 1. 0. 0. 1.]
[0. 1. 1. 1. 1. 0. 1. 0.]]
The dic you are using is not needed. Here is a solution without it.
import random
lists = []
for i in range(0, 9):
lists.append(sorted(random.sample(range(0, 8), 5)))
mat = []
for lst in lists:
arr = [0] * 8
for index in lst:
arr[index] = 1
mat.append(arr)
print(mat)
Outputs:
[[0, 1, 1, 0, 0, 1, 1, 1],
[1, 0, 1, 1, 0, 0, 1, 1],
[1, 1, 1, 1, 0, 0, 0, 1],
[0, 1, 1, 1, 0, 1, 1, 0],
[1, 1, 0, 1, 1, 0, 1, 0],
[1, 0, 1, 1, 0, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 0, 1, 1, 1, 0],
[1, 0, 1, 0, 1, 0, 1, 1]]
You can also use numpy.random.randint to generate your binary 2d array.
import numpy as np
arr = np.random.randint(2, size=(8, 8))
print(arr)
Outputs:
[[1 0 1 0 1 0 1 1]
[0 0 0 0 1 0 1 1]
[1 0 1 1 0 0 1 1]
[1 1 0 1 0 1 1 0]
[0 0 1 0 0 1 1 1]
[1 0 0 0 1 1 1 1]
[1 1 0 1 0 0 1 1]
[1 0 1 0 0 1 0 0]]

Remove Decimals from Array

I have 2 arrays containing zeros & ones. I want to perform hstack() on them but not getting the desired output.
Python Code..
import numpy as np
zeros = np.zeros(8)
ones = np.ones(8)
zerosThenOnes = np.hstack((zeros, ones)) # A 1 by 16 array
Current Output..
[ 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 1. 1.]
Expected Output..
[ 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ]
I can't understand what silly mistake I'm doing.
You must tell numpy to return the values as integers
import numpy as np
zeros = np.zeros((8,), dtype=np.int)
ones = np.ones((8,), dtype=np.int)
zerosThenOnes = np.hstack((zeros, ones))
To print out zerosThenOnes like this [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]
Use:
print([x for x in zerosThenOnes])
Numpy Zeros
np.hstack((np.zeros(8), np.ones(8))).astype(int)
for np.array output, or
map( int, np.hstack((np.zeros(8), np.ones(8))) )
for list output

Adding different sized/shaped displaced NumPy matrices

In short: I have two matrices (or arrays):
import numpy
block_1 = numpy.matrix([[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0]])
block_2 = numpy.matrix([[ 1, 1, 1],
[ 1, 1, 1],
[ 1, 1, 1],
[ 1, 1, 1]])
I have the displacement of block_2 in the block_1 element coordinate system.
pos = (1,1)
I want to be able to add them (quickly), to get:
[[0 0 0 0 0]
[0 1 1 1 0]
[0 1 1 1 0]
[0 1 1 1 0]]
In long: I would like a fast way to add two different shape matrices together, where one of the matrices can be displaced. The resulting matrix must have the shape of the first matrix, and the overlapping elements between the two matrices are summed. If there is no overlap, just the first matrix is returned unmutated.
I have a function that works fine, but it's kind of ugly, and elementwise:
def add_blocks(block_1, block_2, pos):
for i in xrange(0, block_2.shape[0]):
for j in xrange(0, block_2.shape[1]):
if (i + pos[1] >= 0) and (i + pos[1] < block_1.shape[0])
and (j + pos[0] >= 0) and (j + pos[0] < block_1.shape[1]):
block_1[pos[1] + i, pos[0] + j] += block_2[i,j]
return block_1
Can broadcasting or slicing perhaps do this?
I feel like maybe I'm missing something obvious.
An easy solution that looks like MATLAB solution is:
import numpy as np
block1 = np.zeros((5,4))
block2 = np.ones((3,2))
block1[1:4,2:4] += block2 # use array slicing
print(block1)
[[0. 0. 0. 0.]
[0. 0. 1. 1.]
[0. 0. 1. 1.]
[0. 0. 1. 1.]
[0. 0. 0. 0.]]
So package it as a reusable function:
import numpy as np
def addAtPos(mat1, mat2, xypos):
"""
Add two matrices of different sizes in place, offset by xy coordinates
Usage:
- mat1: base matrix
- mat2: add this matrix to mat1
- xypos: tuple (x,y) containing coordinates
"""
x, y = xypos
ysize, xsize = mat2.shape
xmax, ymax = (x + xsize), (y + ysize)
mat1[y:ymax, x:xmax] += mat2
return mat1
block1 = np.zeros((5,4))
block2 = np.ones((3,2))
pos = (2,1)
print(addAtPos(block1, block2, pos))
[[0. 0. 0. 0.]
[0. 0. 1. 1.]
[0. 0. 1. 1.]
[0. 0. 1. 1.]
[0. 0. 0. 0.]]
You just have to find the overlapping range, and then add the arrays using slicing.
b1 = np.zeros((4,5))
b2 = np.ones((4,3))
pos_v, pos_h = 2, 3 # offset
v_range1 = slice(max(0, pos_v), max(min(pos_v + b2.shape[0], b1.shape[0]), 0))
h_range1 = slice(max(0, pos_h), max(min(pos_h + b2.shape[1], b1.shape[1]), 0))
v_range2 = slice(max(0, -pos_v), min(-pos_v + b1.shape[0], b2.shape[0]))
h_range2 = slice(max(0, -pos_h), min(-pos_h + b1.shape[1], b2.shape[1]))
b1[v_range1, h_range1] += b2[v_range2, h_range2]
They're added in-place, but you could also create a new array. I might have missed some corner cases, though, but it seems to work fine.
Here's #jorgeca's great code as a function, with some tests - I expanded the slices to try to make it a little more readable:
import numpy as np
def addAtPos(matrix1, matrix2, xypos, inPlace=False):
"""
Add matrix2 into matrix1 at position xypos (x,y), in-place or in new matrix.
Handles matrix2 going off edges of matrix1.
"""
x, y = xypos
h1, w1 = matrix1.shape
h2, w2 = matrix2.shape
# get slice ranges for matrix1
x1min = max(0, x)
y1min = max(0, y)
x1max = max(min(x + w2, w1), 0)
y1max = max(min(y + h2, h1), 0)
# get slice ranges for matrix2
x2min = max(0, -x)
y2min = max(0, -y)
x2max = min(-x + w1, w2)
y2max = min(-y + h1, h2)
if inPlace:
# add matrix2 into matrix1, in place
matrix1[y1min:y1max, x1min:x1max] += matrix2[y2min:y2max, x2min:x2max]
else:
# create and return a new matrix
matrix1copy = matrix1.copy()
matrix1copy[y1min:y1max, x1min:x1max] += matrix2[y2min:y2max, x2min:x2max]
return matrix1copy
def test_addAtPos():
matrix1 = np.zeros((2,2))
matrix2 = np.ones((2,2))
test(addAtPos(matrix1, matrix2, ( 0, 0)), [[1,1],[1,1]])
test(addAtPos(matrix1, matrix2, ( 2, 2)), [[0,0],[0,0]])
test(addAtPos(matrix1, matrix2, (-1,-1)), [[1,0],[0,0]])
test(addAtPos(matrix1, matrix2, ( 1,-1)), [[0,1],[0,0]])
test(addAtPos(matrix1, matrix2, ( 1, 1)), [[0,0],[0,1]])
test(addAtPos(matrix1, matrix2, (-1, 1)), [[0,0],[1,0]])
def test(actual, expected, message=''):
"Compare actual and expected values and print OK or FAIL"
passed = (actual == expected)
if type(passed) == np.ndarray:
passed = passed.all()
actual = str(actual).replace('\n', '')
expected = str(expected).replace('\n', '')
if passed:
print('[OK] ', message, actual)
else:
print('[FAIL]', message, actual, ' != expected value of', expected)
test_addAtPos()
Output:
[OK] [[1. 1.] [1. 1.]]
[OK] [[0. 0.] [0. 0.]]
[OK] [[1. 0.] [0. 0.]]
[OK] [[0. 1.] [0. 0.]]
[OK] [[0. 0.] [0. 1.]]
[OK] [[0. 0.] [1. 0.]]
This is great, and here's how to extend the addition to a 3D matrix by adding a few lines to jorgeca's code:
import numpy as np
#two 3d arrays, of different size.
b1 = np.zeros((5,5,5), dtype=np.int) # a 5x5x5 matrix of zeroes
b2 = np.ones((3,3,3), dtype=np.int) # a 3x3x3 matrix of ones
pos_v, pos_h, pos_z = 2, 2, 2 # a 3d offset -> to plonk b2 in the corner of b1
v_range1 = slice(max(0, pos_v), max(min(pos_v + b2.shape[0], b1.shape[0]), 0))
h_range1 = slice(max(0, pos_h), max(min(pos_h + b2.shape[1], b1.shape[1]), 0))
z_range1 = slice(max(0, pos_z), max(min(pos_z + b2.shape[2], b1.shape[2]), 0))
v_range2 = slice(max(0, -pos_v), min(-pos_v + b1.shape[0], b2.shape[0]))
h_range2 = slice(max(0, -pos_h), min(-pos_h + b1.shape[1], b2.shape[1]))
z_range2 = slice(max(0, -pos_z), min(-pos_z + b1.shape[2], b2.shape[2]))
b1[v_range1, h_range1, z_range1] += b2[v_range2, h_range2, z_range2]
This might help someone who wants to do the same in 3d (like me).
I'm sure there is a fast NumPy way to do this, but there is a more efficient way to do it even in normal Python:
block_1 = [ [ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0]]
block_2 = [ [ 1, 1, 1],
[ 1, 1, 1],
[ 1, 1, 1],
[ 1, 1, 1]]
pos = (1, 1)
x, y = pos
# width of the rows in block_2
length = len(block_2[0])
# skip the first y rows
for row_1, row_2 in zip(block_1[y:], block_2):
# set length elements offset by x to the sum.
row_1[x:length + x] = map(sum, zip(row_2, row_1[x:length + x]))
print '\n'.join(' '.join(map(str, row)) for row in block_1)
"""
0 0 0 0 0
0 1 1 1 0
0 1 1 1 0
0 1 1 1 0
"""

Categories