Related
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.]]
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.
If i want to do an outer product of 2 vectors to create a 2d matrix, each element a product of the two respective elements in the original vectors:
b = np.arange(5).reshape((1, 5))
a = np.arange(5).reshape((5, 1))
a * b
array([[ 0, 0, 0, 0, 0],
[ 0, 1, 2, 3, 4],
[ 0, 2, 4, 6, 8],
[ 0, 3, 6, 9, 12],
[ 0, 4, 8, 12, 16]])
I want the same for 3 (or for n) vectors.
An equivalent non numpy answer:
a = np.arange(5)
b = np.arange(5)
c = np.arange(5)
res = np.zeros((a.shape[0], b.shape[0], c.shape[0]))
for ia in range(len(a)):
for ib in range(len(b)):
for ic in range(len(c)):
res[ia, ib, ic] = a[ia] * b[ib] * c[ic]
print(res)
out:
[[[ 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. 0. 0.]
[ 0. 1. 2. 3. 4.]
[ 0. 2. 4. 6. 8.]
[ 0. 3. 6. 9. 12.]
[ 0. 4. 8. 12. 16.]]
[[ 0. 0. 0. 0. 0.]
[ 0. 2. 4. 6. 8.]
[ 0. 4. 8. 12. 16.]
[ 0. 6. 12. 18. 24.]
[ 0. 8. 16. 24. 32.]]
[[ 0. 0. 0. 0. 0.]
[ 0. 3. 6. 9. 12.]
[ 0. 6. 12. 18. 24.]
[ 0. 9. 18. 27. 36.]
[ 0. 12. 24. 36. 48.]]
[[ 0. 0. 0. 0. 0.]
[ 0. 4. 8. 12. 16.]
[ 0. 8. 16. 24. 32.]
[ 0. 12. 24. 36. 48.]
[ 0. 16. 32. 48. 64.]]]
How to do this with numpy [no for loops]?
Also, how to do this for a general function, not necessarily *?
NumPy provides you with np.outer() for computing the outer product.
This is a less powerful version of more versatile approaches:
ufunc.outer()
np.tensordot()
np.einsum()
np.einsum() is the only one capable of handling more than two input arrays:
import numpy as np
def prod(items, start=1):
for item in items:
start = start * item
return start
a = np.arange(5)
b = np.arange(5)
c = np.arange(5)
r0 = np.zeros((a.shape[0], b.shape[0], c.shape[0]))
for ia in range(len(a)):
for ib in range(len(b)):
for ic in range(len(c)):
r0[ia, ib, ic] = a[ia] * b[ib] * c[ic]
r1 = prod([a[:, None, None], b[None, :, None], c[None, None, :]])
# same as: r1 = a[:, None, None] * b[None, :, None] * c[None, None, :]
# same as: r1 = a.reshape(-1, 1, 1) * b.reshape(1, -1, 1) * c.reshape(1, 1, -1)
print(np.all(r0 == r2))
# True
r2 = np.einsum('i,j,k->ijk', a, b, c)
print(np.all(r0 == r2))
# True
# as per #hpaulj suggestion
r3 = prod(np.ix_(a, b, c))
print(np.all(r0 == r3))
# True
Of course, the broadcasting approach (which is the same that you used with the array.reshape() version of your code, except that it uses a slightly different syntax for providing the correct shape), can be automatized by explicitly building the slicing (or equivalently the array.reshape() parameters).
In [166]: a = np.arange(2)
...: b = np.arange(3)
...: c = np.arange(4)
As shown in comments and answer:
In [167]: R = np.einsum('i,j,k',a,b,c)
We can also np.ix_ construct arrays that broadcast against each other. This is often used to construct block indexing arrays, but works here as well:
In [168]: A,B,C = np.ix_(a,b,c)
In [169]: A,B,C
Out[169]:
(array([[[0]],
[[1]]]),
array([[[0],
[1],
[2]]]),
array([[[0, 1, 2, 3]]]))
In [170]: R1 = A*B*C
testing:
In [171]: np.allclose(R,R1)
Out[171]: True
That broadcasted product can be done in one line with:
In [172]: np.prod(np.array(np.ix_(a,b,c),object)).shape
Out[172]: (2, 3, 4)
Without that explicit object dtype casting I get a future warning about creating an ragged array.
np.meshgrid(a,b,c, sparse=True, indexing='ij') is an alternative to ix_.
While these ix_ etc expressions are nice, you should become thoroughly comfortable using:
a[:, None, None] * b[None, :, None] * c[None, None, :]
This kind of dimension expansion gives you the most power and flexibility.
The simplest approach (from this answer) is to use:
functools.reduce(np.multiply.outer, (a, b, c))
This works for any number of dimensions, and unlike np.prod(np.ix_(...)) it does not result in numpy deprecation warnings about introducing jagged arrays.
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 searched the net to find a guide for Scipy sparse matrices and I failed. I would be happy if anybody would share any source for it but now going to question:
I have an array of tuples. I want to change the array of tuples to a sparse matrix where the tuples appear on the main diagonal and diagonal just beside to it as the following example shows it. What is the fancy(efficient) way of doing it?
import numpy as np
A=np.asarray([[1,2],[3,4],[5,6],[7,8]])
B=np.zeros((A.shape[0],A.shape[0]+1))
for i in range(A.shape[0]):
B[i,i]=A[i,0]
B[i,i+1]=A[i,1]
print B
Output being:
[[ 1. 2. 0. 0. 0.]
[ 0. 3. 4. 0. 0.]
[ 0. 0. 5. 6. 0.]
[ 0. 0. 0. 7. 8.]]
You can build those really fast as a CSR matrix:
>>> A = np.asarray([[1,2],[3,4],[5,6],[7,8]])
>>> rows = len(A)
>>> cols = rows + 1
>>> data = A.flatten() # we want a copy
>>> indptr = np.arange(0, len(data)+1, 2) # 2 non-zero entries per row
>>> indices = np.repeat(np.arange(cols), [1] + [2] * (cols-2) + [1])
>>> import scipy.sparse as sps
>>> a_sps = sps.csr_matrix((data, indices, indptr), shape=(rows, cols))
>>> a_sps.A
array([[1, 2, 0, 0, 0],
[0, 3, 4, 0, 0],
[0, 0, 5, 6, 0],
[0, 0, 0, 7, 8]])
Try diags from scipy
import numpy as np
import scipy.sparse
A = np.asarray([[1,2],[3,4],[5,6],[7,8]])
B = scipy.sparse.diags([A[:,0], A[:,1]], [0, 1], [4, 5])
When I print B.todense(), it gives me
[[ 1. 2. 0. 0. 0.]
[ 0. 3. 4. 0. 0.]
[ 0. 0. 5. 6. 0.]
[ 0. 0. 0. 7. 8.]]