I am trying to 'avoid walls' using an A* star (A-Star) algorithm.
My array look like this:
[1, 1, 1, 0, 0, 0, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 1, 1, 1],
[1, 0, 0, 0, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 1, 1, 1, 1, 1],
[1, 1, 0, 0, 0, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 1, 1, 1]
I can only walk on 0 (zeroes) and 1 (ones) are the walls.
I want my AI to walk on the center of the the path, assuming that there is enough room to walk. AI can walk diagonally.
for example instead of [1, 1, 1, 0, 0, 0, 1, 1, 1],(First array) since there is enough room not to block the path how can I replace it with [1, 1, 1, 1, 0, 1, 1, 1, 1],
Afterthought:
The optimal path here if we will walk on center is [4 3 2 2 3 4].
Also, what if we are given the shortest path possible for this case it
would be [3 3 3 3 4 4] if we are going from (3, 0) to (4, 5). If we
just don't want walls in our path like having a single element before
the wall, how can we arrive to [3 3 2 2 3 4] if we allow start and
finish to touch walls?
Edit:
Ali_Sh answer is what I am initially looking for and is the accepted answer.
If a be the main array, indices of the middle 0 in each row can be achieved by:
cond = np.where(a == 0)
unique = np.unique(cond[0], return_index=True, return_counts=True)
ind = unique[1] + unique[2] // 2
cols = cond[1][ind] # --> [4 3 2 2 3 4]
and it can be used to substitute 1 values in a ones array with the main array shape:
one = np.ones(shape=a.shape)
one[np.arange(len(one)), cols] = 0
which will:
[[1. 1. 1. 1. 0. 1. 1. 1. 1.]
[1. 1. 1. 0. 1. 1. 1. 1. 1.]
[1. 1. 0. 1. 1. 1. 1. 1. 1.]
[1. 1. 0. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 0. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 0. 1. 1. 1. 1.]]
Here's an example where it just finds all the values that are zero in each row and sets the path as the middle argument. If there was a row with two patches of zeros, this could run into trouble. In that case, you would need to make sure that the arguments above and below a zero patch are also zero patches.
I have used matplotlib here to visualize the path:
import matplotlib.pyplot as plt
p = []
A = [[1, 1, 1, 0, 0, 0, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 1, 1, 1],
[1, 0, 0, 0, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 1, 1, 1, 1, 1],
[1, 1, 0, 0, 0, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 1, 1, 1]]
for i in range(len(A)):
ptemp = []
for j in range(len(A[0])):
if A[i][j] == 0:
ptemp.append(j) # find all the zero values
p.append(ptemp[int(len(ptemp)/2)]) # set the path as the center zero value
print(p)
plt.imshow(A[::-1])
plt.plot(p[::-1],range(len(A)))
plt.show()
For the update section of the question, if we have another path for columns instead the optimal path that specified in my previous answer (e.g. [3 1 1 1 2 3] instead [4 3 2 2 3 4]), it can be applied just using:
cols = np.array([3, 1, 1, 1, 2, 3])
one = np.ones(shape=a.shape)
one[np.arange(len(one)), cols] = 0
# [[1. 1. 1. 0. 1. 1. 1. 1. 1.]
# [1. 0. 1. 1. 1. 1. 1. 1. 1.]
# [1. 0. 1. 1. 1. 1. 1. 1. 1.]
# [1. 0. 1. 1. 1. 1. 1. 1. 1.]
# [1. 1. 0. 1. 1. 1. 1. 1. 1.]
# [1. 1. 1. 0. 1. 1. 1. 1. 1.]]
If we want the all paths other than boundaries, we could add the following codes to the previous answer codes:
if we don't fully walk on the center but just avoid 'near walls' path
just even 1 offset from the walls:
cols_min = cols - (unique[2] - 2) // 2
cols_max = cols + (unique[2] - 2) // 2
one[np.arange(len(one)), cols_min] = 0
one[np.arange(len(one)), cols_max] = 0
# [[1. 1. 1. 1. 0. 1. 1. 1. 1.]
# [1. 1. 0. 0. 0. 1. 1. 1. 1.]
# [1. 1. 0. 1. 1. 1. 1. 1. 1.]
# [1. 1. 0. 1. 1. 1. 1. 1. 1.]
# [1. 1. 1. 0. 1. 1. 1. 1. 1.]
# [1. 1. 1. 1. 0. 1. 1. 1. 1.]]
For when we can touch the walls (here one of them) on the first and the last rows, we could add the following codes to the previous answer codes:
col_min_first = cols[0] - unique[2][0] // 2
col_min_last = cols[-1] - unique[2][-1] // 2
one[0, col_min_first:cols[0]] = 0
one[-1, col_min_last:cols[-1]] = 0
# [[1. 1. 1. 0. 0. 1. 1. 1. 1.]
# [1. 1. 1. 0. 1. 1. 1. 1. 1.]
# [1. 1. 0. 1. 1. 1. 1. 1. 1.]
# [1. 1. 0. 1. 1. 1. 1. 1. 1.]
# [1. 1. 1. 0. 1. 1. 1. 1. 1.]
# [1. 1. 1. 0. 0. 1. 1. 1. 1.]]
And, finally, if we want to find the shortest path, we can achieve the goal by finding the column with maximum number of 0 in it, firstly, and then, find the nearest column index 0 to that column for where the column not contains 0:
ind_max = np.argmax(np.sum(a == 0, axis=0))
mask_rows = a[:, ind_max] != 0
mask_col_min = a[:, ind_max - 1] == 0
mask_col_max = a[:, ind_max + 1] == 0
ind_max = np.where(mask_rows & mask_col_min, ind_max - 1, ind_max)
ind_max = np.where(mask_rows & mask_col_max, ind_max + 1, ind_max)
one = np.ones(shape=a.shape)
one[np.arange(len(one)), ind_max] = 0
# [[1. 1. 1. 0. 1. 1. 1. 1. 1.] | a = np.array([[1, 1, 1, 0, 0, 0, 1, 1, 1], [[1. 1. 1. 0. 1. 1. 1. 1. 1.]
# [1. 1. 1. 0. 1. 1. 1. 1. 1.] | [1, 0, 0, 0, 0, 0, 1, 1, 1], [1. 1. 1. 0. 1. 1. 1. 1. 1.]
# [1. 1. 1. 0. 1. 1. 1. 1. 1.] | [0, 0, 0, 1, 1, 1, 1, 1, 1], --> [1. 1. 0. 1. 1. 1. 1. 1. 1.]
# [1. 1. 1. 0. 1. 1. 1. 1. 1.] | [1, 0, 0, 0, 1, 1, 1, 1, 1], [1. 1. 1. 0. 1. 1. 1. 1. 1.]
# [1. 1. 1. 0. 1. 1. 1. 1. 1.] | [1, 1, 0, 0, 0, 1, 1, 1, 1], [1. 1. 1. 0. 1. 1. 1. 1. 1.]
# [1. 1. 1. 0. 1. 1. 1. 1. 1.]] | [1, 1, 1, 0, 0, 0, 1, 1, 1]]) [1. 1. 1. 0. 1. 1. 1. 1. 1.]]
Related
I have three lists, and I can build a numpy array of objects with (3,2,4) shape from them.
However, what I want is a numpy array with shape (2, 3, 4), where each axis corresponds to a "variable" in the list.
I tried using np.reshape with no success.
import numpy as np
p1 = [[1, 1, 1, 1], [1, 1, 1, 1]]
p2 = [[0, 0, 0, 0], [0, 0, 0, 0]]
p3 = [[2, 2, 2, 2], [2, 2, 2, 2]]
points = [p1, p2, p3]
nparr = np.array(points, dtype=np.float32)
What I obtain is
[[[1. 1. 1. 1.]
[1. 1. 1. 1.]]
[[0. 0. 0. 0.]
[0. 0. 0. 0.]]
[[2. 2. 2. 2.]
[2. 2. 2. 2.]]]
But what I would like to obtain is
[[[1. 1. 1. 1.]
[0. 0. 0. 0.]
[2. 2. 2. 2.]]
[[[1. 1. 1. 1.]
[0. 0. 0. 0.]
[2. 2. 2. 2.]]
Is there a clean way to achieve this without having to change the original input lists?
nparr = np.array([a for a in zip(p1,p2,p3)], dtype=np.float32)
or
nparr = np.squeeze(np.array([list(zip(p1,p2,p3))]), dtype=np.float32)
You can just do a transposition of the two first axis using:
nparr.transpose(1, 0, 2)
Since transpose return a view of the array with modified strides, this operation is very very cheap. However, on big array, it may be better to copy the view using .copy() so to work on contiguous views.
I am a complete beginner with NumPy and I am trying to generate the following matrix pattern. Below is my code. What I am not figuring out is that what am I doing wrong to get this result. Thanks in advance for any help.
import numpy as np
def matrix(n):
final = []
for i in range(n):
final.append(list(np.tile([0,1],int(n/2))) if i%2==0 else list(np.tile([1,0],int(n/2))))
print(np.array(final))
size = 8
matrix(size)
While using numpy you should avoid working with arrays and for loops for matrix creating and editing because for large matrices it would be very slow.
Try to examine this code:
import math
import numpy as np
def zero_borders(mat: np.ndarray) -> None:
"""Makes the borders of the array zero."""
mat[:, 0] = 0 # left border
mat[:, -1] = 0 # right border
mat[0, :] = 0 # upper border
mat[-1, :] = 0 # bottom border
def zero_center_square(mat: np.ndarray) -> None:
"""Makes small square of zeros in the center of the array."""
size = mat.shape[0]
i_low = size//2 - 1
i_high = math.ceil(size/2)
mat[i_low, i_low:i_high + 1] = 0 # upper edge of the square
mat[i_high, i_low:i_high + 1] = 0 # upper edge of the square
mat[i_low:i_high + 1, i_low] = 0 # left edge of the square
mat[i_low:i_high + 1, i_high] = 0 # right edge of the square
def matrix(n: int) -> np.ndarray:
"""Creates a square matrix with special pattern."""
mat = np.ones((n, n))
zero_borders(mat)
zero_center_square(mat)
return mat
def main():
print("Even size:")
print(matrix(8))
print("")
print("Odd size:")
print(matrix(9))
if __name__ == "__main__":
main()
The output:
Even size:
[[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 1. 1. 1. 1. 1. 1. 0.]
[0. 1. 1. 1. 1. 1. 1. 0.]
[0. 1. 1. 0. 0. 1. 1. 0.]
[0. 1. 1. 0. 0. 1. 1. 0.]
[0. 1. 1. 1. 1. 1. 1. 0.]
[0. 1. 1. 1. 1. 1. 1. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]]
Odd size:
[[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 1. 1. 1. 1. 1. 1. 1. 0.]
[0. 1. 1. 1. 1. 1. 1. 1. 0.]
[0. 1. 1. 0. 0. 0. 1. 1. 0.]
[0. 1. 1. 0. 1. 0. 1. 1. 0.]
[0. 1. 1. 0. 0. 0. 1. 1. 0.]
[0. 1. 1. 1. 1. 1. 1. 1. 0.]
[0. 1. 1. 1. 1. 1. 1. 1. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]]
You can use numpy ix_() like this:
>>> x = np.zeros((9,9), dtype=int)
>>> p1 = np.ix_([1,2,6,7],[1,2,3,4,5,6,7])
>>> x[p]=1
>>> p2 = np.ix_([3,4,5],[1,2,6,7])
>>> x[p2]=1
>>> x
array([[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 1, 1, 0],
[0, 1, 1, 0, 1, 0, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]])
You have not mentioned any particular pattern for lxl length of matrix, so I will write just code about how to generate the matrix in given image.
You can use NumPy (particularly numpy.pad()) to create that matrix easily as:
import numpy as np
# Create required matrix
matrix = np.pad(np.pad(np.pad(np.array([[1]]), (1, 1)), (2, 2), constant_values = 1), (1, 1))
# If you want that as list instead of NumPy array
list_matrix = list(list(i) for i in matrix)
I am new in numpy, and I am having troubles with simple managment of numpy arrays.
I am doing a task in which it said that loops has to be avoid as much as possible, and I need to edit the values of an array through another array of indexes.
indexes # [3, 16]
y # [0. 1. 1. 1. 0. 1. 0. 0. 0. 0. 1. 1. 1. 0. 1. 0. 0. 0. 1. 1.]
y[indexes] = 2 # [0. 1. 1. 2. 0. 1. 0. 0. 0. 0. 1. 1. 1. 0. 1. 0. 2. 0. 1. 1.]
But I don't need change the value simply by 2. I need make a conditional change. This what I have got, but I would need something like
y[indexes] = 0 if y[indexes] == 1 else 0
>>> [0. 1. 1. 0. 0. 1. 0. 0. 0. 0. 1. 1. 1. 0. 1. 0. 1. 0. 1. 1.]
And the line above should be the results.
This is the loop way answer, but I need a numpy way if exists:
for index in indexes:
y[index] = 1 if y[index] == 0 else 0
Thanks in advance.
I don't know if I understood your question. But I hope this helps you.
tip 01
import numpy as np
indexes = [1, 5, 7] # index list
y = np.array([9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]) #array example
y[indexes][2] #3rd(0,1,>>2<<) item of y array (1,5,>>7<<).
In this case it is y[7] equal 16.
tip 02
This can also be useful.
y = np.array([0,1,1,0,3,0,1,0,1,0])
y
array([0, 1, 1, 0, 3, 0, 1, 0, 1, 0])
np.where(y != 1, y, 0)
y
array([0, 0, 0, 0, 3, 0, 0, 0, 0, 0])
I've been looking for a way to (efficiently) compute a distance matrix from a target value and an input matrix.
If you consider an input array as:
[0 0 1 2 5 2 1]
[0 0 2 3 5 2 1]
[0 1 1 2 5 4 1]
[1 1 1 2 5 4 0]
Ho do you compute the spatial distance matrix associated to the target value 0?
i.e. what is the distance from each pixel to the closest 0 value?
Thanks in advance
You are looking for scipy.ndimage.morphology.distance_transform_edt. It operates on a binary array and computes euclidean distances on each TRUE position to the nearest background FALSE position. In our case, since we want to find out distances from nearest 0s, so the background is 0. Now, under the hoods, it converts the input to a binary array assuming 0 as the background, so we can just use it with the default parameters. Hence, it would be as simple as -
In [179]: a
Out[179]:
array([[0, 0, 1, 2, 5, 2, 1],
[0, 0, 2, 3, 5, 2, 1],
[0, 1, 1, 2, 5, 4, 1],
[1, 1, 1, 2, 5, 4, 0]])
In [180]: from scipy import ndimage
In [181]: ndimage.distance_transform_edt(a)
Out[181]:
array([[0. , 0. , 1. , 2. , 3. , 3.16, 3. ],
[0. , 0. , 1. , 2. , 2.83, 2.24, 2. ],
[0. , 1. , 1.41, 2.24, 2.24, 1.41, 1. ],
[1. , 1.41, 2.24, 2.83, 2. , 1. , 0. ]])
Solving for generic case
Now, let's say we want to find out distances from nearest 1s, then it would be -
In [183]: background = 1 # element from which distances are to be computed
# compare this with original array, a to verify
In [184]: ndimage.distance_transform_edt(a!=background)
Out[184]:
array([[2. , 1. , 0. , 1. , 2. , 1. , 0. ],
[1.41, 1. , 1. , 1.41, 2. , 1. , 0. ],
[1. , 0. , 0. , 1. , 2. , 1. , 0. ],
[0. , 0. , 0. , 1. , 2. , 1.41, 1. ]])
I have 2 arrays, x and y:
x = [[ 1. 2. 3. 4.]
[ 5. 6. 7. 8.]
[ 9. 0. 3. 6.]]
y = [[ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]]
I want a z matrix, as: z = [y[0], x, y[1], y[2]]:
[[ 1. 1. 2. 3. 4. 0. 0.]
[ 0. 5. 6. 7. 8. 1. 0.]
[ 0. 9. 0. 3. 6. 0. 1.]]
So I made this code:
z = np.c_[y[0], x]
for j in range(n):
z = np.c_[x, y[j]]
But it is not saving the matrix. My resulting z was just the last operation:
[[ 1. 2. 3. 4. 0.]
[ 5. 6. 7. 8. 0.]
[ 9. 0. 3. 6. 1.]]
How could I save the changes made on the matrix? I also tried to numpy.append() the same way, but it gives an error message:
ValueError: all the input arrays must have same number of dimensions
Using np.c to stack columns of y and x..
np.c_[np.array(y)[0],np.asanyarray(x),np.array(y)[1],np.array(y)[2]]
Out[536]:
array([[1, 1, 2, ..., 4, 0, 0],
[0, 5, 6, ..., 8, 1, 0],
[0, 9, 0, ..., 6, 0, 1]])
Or you can use np.roll to shift the columns before stacking them and shift again afterwards.
np.roll(np.c_[np.array(x),np.roll(np.array(y),-1,axis=1)],1,axis=1)
Out[549]:
array([[1, 1, 2, ..., 4, 0, 0],
[0, 5, 6, ..., 8, 1, 0],
[0, 9, 0, ..., 6, 0, 1]])
I think that the command you are looking for is numpy.insert(a, pos, col, axis = 1). If you make z = insert(y, 1, x, axis = 1) it will insert a new column on y with the values from x, and save the output in z.