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
"""
Related
I have a numpy 2D array (50x50) filled with values. I would like to flatten the 2D array into one column (2500x1), but the location of these values are very important. The indices can be converted to spatial coordinates, so I want another two (x,y) (2500x1) arrays so I can retrieve the x,y spatial coordinate of the corresponding value.
For example:
My 2D array:
--------x-------
[[0.5 0.1 0. 0.] |
[0. 0. 0.2 0.8] y
[0. 0. 0. 0. ]] |
My desired output:
#Values
[[0.5]
[0.1]
[0. ]
[0. ]
[0. ]
[0. ]
[0. ]
[0.2]
...],
#Corresponding x index, where I will retrieve the x spatial coordinate from
[[0]
[1]
[2]
[3]
[4]
[0]
[1]
[2]
...],
#Corresponding y index, where I will retrieve the x spatial coordinate from
[[0]
[0]
[0]
[0]
[1]
[1]
[1]
[1]
...],
Any clues on how to do this? I've tried a few things but they have not worked.
For the simplisity let's reproduce your array with this chunk of code:
value = np.arange(6).reshape(2, 3)
Firstly, we create variables x, y which contains index for each dimension:
x = np.arange(value.shape[0])
y = np.arange(value.shape[1])
np.meshgrid is the method, related to the issue you described:
xx, yy = np.meshgrid(x, y, sparse=False)
Finaly, transform all elements it in the shape you want with these lines:
xx = xx.reshape(-1, 1)
yy = yy.reshape(-1, 1)
value = value.reshape(-1, 1)
According to your example, with np.indices:
data = np.arange(2500).reshape(50, 50)
y_indices, x_indices = np.indices(data.shape)
Reshaping your data:
data = data.reshape(-1,1)
x_indices = x_indices.reshape(-1,1)
y_indices = y_indices.reshape(-1,1)
Assuming you want to flatten and reshape into a single column, use reshape:
a = np.array([[0.5, 0.1, 0., 0.],
[0., 0., 0.2, 0.8],
[0., 0., 0., 0. ]])
a.reshape((-1, 1)) # 1 column, as many row as necessary (-1)
output:
array([[0.5],
[0.1],
[0. ],
[0. ],
[0. ],
[0. ],
[0.2],
[0.8],
[0. ],
[0. ],
[0. ],
[0. ]])
getting the coordinates
y,x = a.shape
np.tile(np.arange(x), y)
# array([0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3])
np.repeat(np.arange(y), x)
# array([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2])
or simply using unravel_index:
Y, X = np.unravel_index(range(a.size), a.shape)
# (array([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]),
# array([0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]))
I have a large array of 1's and 0's. Each time a 1 appears in it, I want to make sure that all the 1's adjacent (or diagonal) to that 1 are set to 0 - so that the array you are left with has one 1 for every "group" of ones that appeared in the original array (I don't mind which 1 is kept per group, just as long as there are no adjacent/neighbouring 1's).
For example, with the following array (much smaller than what I will be dealing with, but I want to illustrate the problem).
[[0. 0. 0. 1.]
[0. 1. 0. 0.]
[0. 1. 0. 0.]
[1. 0. 0. 1.]
[0. 0. 1. 0.]]
I would want to return an array that looks like:
[[0. 0. 0. 1.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[1. 0. 0. 0.]
[0. 0. 1. 0.]]
I have tried the following code for the above array (which is labelled as a) in order to achieve this:
# Define a function which finds if an entry has a 1 neighbouring it
def has_neighbour(matrix, x, y):
if matrix[x-1,y]==1 or matrix[x+1,y]==1 or matrix[x, y+1]==1 or matrix[x, y-1]==1 or matrix[x+1, y+1]==1 or matrix[x+1, y-1]==1 or matrix[x-1, y+1]==1 or matrix[x-1, y-1]==1:
# Find the entries in the matrix of 1 and if they have an adjacent 1, set them to zero
for i in range(4):
for j in range(3):
if a[i,j]==0 or (a[i,j] == 1 and has_neighbour(a, i, j)==True):
a[i,j] = 0
else:
a[i,j] = 1
# Re-search along the last row and column for adjacent 1's, setting them to zero where necessary. This is needed due to for loops not changing the last row and column of an array
for i in range(4):
if a[i,3]==0 or (a[i,3]==1 and (a[i+1,3]==1 or a[i-1,3]==1)):
a[i,3] = 0
else:
a[i,3]=1
for j in range(3):
if a[4, j]==0 or (a[4, j]==1 and (a[4, j+1]==1 or a[4, j-1]==1)):
a[4, j] = 0
else:
a[4, j] = 1
However, this code returns the following array:
[[0. 0. 0. 1.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 1.]
[0. 0. 1. 0.]]
Anyone know what I is going wrong here? I am very confused as it's getting rid of some 1's I don't want it to and not getting rid of some that I do want it to.
Edit
#JohnColeman's answer is the closest to working so far. However, it fails on the following matrix (I've attached his code, but changed the test part to the matrix that it is failing with):
def pick_reps(matrix):
m = len(matrix)
n = len(matrix[0])
reps = [[0]*n for _ in range(m)]
represented = set()
for i, row in enumerate(matrix):
for j,x in enumerate(row):
if x == 1:
#check if this position is in an already represented block
covered = False
if j > 0 and (i,j-1) in represented:
covered = True
elif i > 0 and (i-1,j) in represented:
covered = True
elif i > 0 and j > 0 and (i-1,j-1) in represented:
covered = True
elif i > 0 and j < n and (i-1,j+1) in represented:
covered = True
if not covered:
reps[i][j] = 1
represented.add((i,j))
return reps
#test:
matrix = [[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 1, 1, 0],
[1, 0, 1, 1],
[0, 0, 0, 1]]
reps = pick_reps(matrix)
for row in reps: print(row)
This should return a matrix with only one 1 in it. However, it returns:
[[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 1, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
I think your is_neighbo(u)r1 function still needs some work. There is a lot of logic down the line that should in fact be part of that function; in particular, the borderline cases should be accounted for inside the function. Here's one way to implement your idea:
def has_neighbor(matrix, x, y):
"""Return True if any of the neighboring cells are equal to 1"""
# just in case you need to change this value at some point
value = 1
# Define neighbors to check
corners = [(-1, -1),
(0, -1),
(1, -1),
(1, 0),
(1, 1),
(0, 1),
(-1, 1),
(-1, 0)]
# get the boundaries of the matrix
x_max, y_max = matrix.shape
# for each neighbor
for x_off, y_off in corners:
# get the new coordinates to check
x2 = x+x_off
y2 = y+y_off
# skip coordinates that are out of bounds
out_of_grid = [x2 < 0,
y2 < 0,
x2 >= x_max,
y2 >= y_max]
if any(out_of_grid):
continue
# finally!
if matrix[x2, y2] == value:
return True
It can now handle the borderline cases, which makes the final loop much simpler:
x_max, y_max = a.shape
for x in range(x_max):
for y in range(y_max):
if has_neighbor(a, x, y):
a[x, y] = 0
The output is as desired:
array([[0, 0, 0, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 0, 0, 0],
[0, 0, 1, 0]])
1 NB: In code, American spelling is more common.
This probably isn't very idiomatic, but you could iterate through the array - when you find a 1 you call a recursive function to clear all reachable 1s. This of course will clear the initial, representative element, so you set that back to 1.
a = [[0, 0, 0, 1],
[0, 1, 0, 0],
[0, 1, 0, 0],
[1, 0, 0, 1],
[0, 0, 1, 0]]
def clear(a, r, c):
if r < 0 or r == len(a) or c < 0 or c == len(a[0]) or a[r][c] == 0:
return
a[r][c] = 0
for i in [-1, 0, 1]:
for j in [-1, 0, 1]:
clear(a, r+i, c+j)
for r in range(len(a)):
for c in range(len(a[0])):
if a[r][c] == 1:
clear(a, r, c)
a[r][c] = 1
for row in a: print(row)
Output:
[0, 0, 0, 1]
[0, 1, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 0]
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)
I have an array n of count data, and I want to transform it into a matrix x in which each row contains a number of ones equal to the corresponding count number, padded by zeroes, e.g:
n = [0 1 3 0 1]
x = [[ 0. 0. 0.]
[ 1. 0. 0.]
[ 1. 1. 1.]
[ 0. 0. 0.]
[ 1. 0. 0.]]
My solution is the following, and is very slow. Is it possible to do better?
n = np.random.poisson(2,5)
max_n = max(n)
def f(y):
return np.concatenate((np.ones(y), np.zeros(max_n-y)))
x = np.vstack(map(f,n))
Here's one way to vectorize it:
>>> n = np.array([0,2,1,0,3])
>>> width = 4
>>> (np.arange(width) < n[:,None]).astype(int)
array([[0, 0, 0, 0],
[1, 1, 0, 0],
[1, 0, 0, 0],
[0, 0, 0, 0],
[1, 1, 1, 0]])
where if you liked, width could be max(n) or anything else you chose.
import numpy as np
n = np.array([0, 1, 3, 0, 1])
max_n = max(n)
np.vstack(n > i for i in range(max_n)).T.astype(int) # xrange(max_n) for python 2.x
Output:
array([[0, 0, 0],
[1, 0, 0],
[1, 1, 1],
[0, 0, 0],
[1, 0, 0]])
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