Getting an item's neighbor inside of a numpy array - python

I have an array that looks like this:
[['A0' 'B0' 'C0']
['A1' 'B1' 'C1']
['A2' 'B2' 'C2']]
I would like to get B1's neighbors which are B0 , C1 , B2 , A1, along with their indices.
This is what I came up with:
import numpy as np
arr = np.array([
['A0','B0','C0'],
['A1','B1','C1'],
['A2','B2','C2'],
])
def get_neighbor_indices(x,y):
neighbors = []
try:
top = arr[y - 1, x]
neighbors.append((top, (y - 1, x)))
except IndexError:
pass
try:
bottom = arr[y + 1, x]
neighbors.append((bottom, (y + 1, x)))
except IndexError:
pass
try:
left = arr[y, x - 1]
neighbors.append((left, (y, x - 1)))
except IndexError:
pass
try:
right = arr[y, x + 1]
neighbors.append((right, (y, x + 1)))
except IndexError:
pass
return neighbors
This will return a list of tuples (value, (y, x)).
Is there a better way to do this without relying on try/except?

You can do this directly in numpy without any exceptions, since you know the sizes of your array. The indices of the immediate neighbors of x, y are given by
inds = np.array([[x, y]]) + np.array([[1, 0], [-1, 0], [0, 1], [0, -1]])
You can easily make a mask that indicates which indices are valid:
valid = (inds[:, 0] >= 0) & (inds[:, 0] < arr.shape[0]) & \
(inds[:, 1] >= 0) & (inds[:, 1] < arr.shape[1])
Now extract the values that you want:
inds = inds[valid, :]
vals = arr[inds[:, 0], inds[:, 1]]
The simplest return value would be inds, vals, but if you insisted on keeping your original format, you could transform it into
[v, tuple(i) for v, i in zip(vals, inds)]
Addendum
You can easily modify this to work on arbitrary dimensions:
def neighbors(arr, *pos):
pos = np.array(pos).reshape(1, -1)
offset = np.zeros((2 * pos.size, pos.size), dtype=np.int)
offset[np.arange(0, offset.shape[0], 2), np.arange(offset.shape[1])] = 1
offset[np.arange(1, offset.shape[0], 2), np.arange(offset.shape[1])] = -1
inds = pos + offset
valid = np.all(inds >= 0, axis=1) & np.all(inds < arr.shape, axis=1)
inds = inds[valid, :]
vals = arr[tuple(inds.T)]
return vals, inds
Given an N dimensional array arr and N elements of pos, you can create the offsets by just setting each dimension sequentially to 1 or -1. The computation of the mask valid is greatly simplified by broadcasting together inds and arr.shape, as well as calling np.all across each N-sized row instead of doing it manually for each dimension. Finally, the conversion tuple(inds.T) turns inds into an actual fancy index by assigning each column to a separate dimension. The transpose is necessary becaue arrays iterate over rows (dim 0).

You can use this:
def get_neighbours(inds):
places = [(-1, 0), (1, 0), (0, -1), (0, 1)]
return [(arr[x, y], (y, x)) for x, y in [(inds[0] + p[0], inds[1] + p[1]) for p in places] if x >= 0 and y >= 0]
get_neighbours(1, 1)
# OUTPUT [('B0', (1, 0)), ('B2', (1, 2)), ('A1', (0, 1)), ('C1', (2, 1))]
get_neighbours(0, 0)
# OUTPUT [('A1', (0, 1)), ('B0', (1, 0))]

How about this?
def get_neighbor_indices(x,y):
return ( [(arr[y-1,x], (y-1, x))] if y>0 else [] ) + \
( [(arr[y+1,x], (y+1, x))] if y<arr.shape[0]-1 else [] ) + \
( [(arr[y,x-1], (y, x-1))] if x>0 else [] ) + \
( [(arr[y,x+1], (y, x+1))] if x<arr.shape[1]-1 else [] )

Related

sympy piecewise:i want to use array loop

sympy piecewise:i want use array loop
OK
from sympy import *
var('x')
myP1 = (0, x < -1)
myP2 = (x, x <= 1)
myP3 = (1, x >= 5)
myPw1= Piecewise( myP1,myP2,myP3 )
myPw2= Piecewise(*(myP1,myP2,myP3))
print("#",myPw1,myPw1.subs(x,6))
print("#",myPw2,myPw2.subs(x,6))
# Piecewise((0, x < -1), (x, x <= 1), (1, x >= 5)) 1
# Piecewise((0, x < -1), (x, x <= 1), (1, x >= 5)) 1
i want use array. (i want use Sum or use Append)
i want use array loop
TypeError
from sympy import *
var('x myP')
myP[1] = (0, x < -1)
myP[2] = (x, x <= 1)
myP[3] = (1, x >= 5)
myPw = Piecewise(*(myP[1] , myP[2] , myP[3]))
print("#",myPw,myPw.subs(x,6))
# myP[1] = (0, x < -1)
# TypeError: 'Symbol' object does not support item assignment
(2022-03-11)
i try tuple
from sympy import *
var('x')
myP=[]
myP=myP+list([(0, x < -1)])
myP=myP+list([(x, x <= 1)])
myP=myP+list([(1, x >= 5)])
myPw=Piecewise(*myP)
print("#",myPw)
myP=[]
myP=list([(0, x < -1)])+list([(x, x <= 1)])+list([(1, x >= 5)])
myPw=Piecewise(*myP)
print("#",myPw)
# Piecewise((0, x < -1), (x, x <= 1), (1, x >= 5))
# Piecewise((0, x < -1), (x, x <= 1), (1, x >= 5))
You created symbols x and myP and then you tried to treat myP like a list (or something indexable) by writing myP[1] = .... Instead, create a list and assign/append values:
>>> myP = []
>>> myP.append((0, x < -1))
>>> myP.append((x, x <= 1))
>>> myP.append((1, x >= 5))
>>> myPw = Piecewise(*myP)

Select a column without "losing" a dimension

Suppose I execute the following code
W = tf.random.uniform(shape = (100,1), minval = 0, maxval = 1)
Z = tf.random.uniform(shape = (100,1), minval = 0, maxval = 1)
X = tf.concat([W,Z],1)
The shape of X[:,1] would be [100,]. That is, X[:,1].shape would yield [100,]. If I want to select the second column of X and want the resulting array to have shape [100,1], what should I do? I looked at tf.slice but I'm not sure if it' helpful.
Maybe just use tf.newaxis for your use case:
import tensorflow as tf
W = tf.random.uniform(shape = (100,1), minval = 0, maxval = 1)
Z = tf.random.uniform(shape = (100,1), minval = 0, maxval = 1)
X = tf.concat([W,Z],1)
print(X[:, 1, tf.newaxis].shape)
# (100, 1)
Try this:
Y = X[:, 1]
Y.shape
# which is [100]
Y = tf.reshape(Y, [100,1])
Y.shape
# which is [100, 1]

Iterate over pairs in order of sum of absolute values

I want to iterate over pairs of integers in order of the sum of their absolute values. The list should look like:
(0,0)
(-1,0)
(0,1)
(0,-1)
(1,0)
(-2,0)
(-1,1)
(-1,-1)
(0,2)
(0,-2)
(1,1)
(1,-1)
(2,0)
[...]
For pairs with the same sum of absolute values I don't mind which order they come in.
Ideally I would like to be able to create the pairs forever so that I can use each one in turn. How can you do that?
For a fixed range I can make the list of pairs in an ugly way with:
sorted([(x,y)for x in range(-20,21)for y in range(-20,21)if abs(x)+abs(y)<21],key=lambda x:sum(map(abs,x))
This doesn't allow me to iterate forever and it also doesn't give me one pair at a time.
This seems to do the trick:
from itertools import count # Creates infinite iterator
def abs_value_pairs():
for absval in count(): # Generate all possible sums of absolute values
for a in range(-absval, absval + 1): # Generate all possible first values
b = abs(a) - absval # Compute matching second value (arbitrarily do negative first)
yield a, b
if b: # If first b is zero, don't output again, otherwise, output positive b
yield a, -b
This runs forever, and operates efficiently (avoiding recomputing anything unnecessarily).
This will do it. If you really want it to be infinite, remove the firs if statement.
import itertools
def makepairs(count=3):
yield (0,0)
for base in itertools.count(1):
if base > count: # optional escape
return # optional escape
for i in range(base+1):
yield (i, base-i)
if base != i:
yield (i, i-base)
if i:
yield (-i, base-i)
if base != i:
yield (-i, i-base)
print(list(makepairs(9)))
The solution below produces a sum stream with tuples of any length:
from itertools import count
def pairs(l = 2):
def groups(d, s, c = []):
if not d and sum(map(abs, c)) == s:
yield tuple(c)
elif d:
for i in [j for k in d[0] for j in {k, -1*k}]:
yield from groups(d[1:], s, c +[i])
for i in count():
yield from groups([range(i+1) for _ in range(l)], i)
p = pairs()
for _ in range(10):
print(next(p))
You could make an infinite generator function:
def pairSums(s = 0): # base generation on target sum to get pairs in order
while True: # s parameter allows starting from a given sum
for i in range(s//2+1): # partitions
yield from {(i,s-i),(s-i,i),(i-s,-i),(-i,i-s)} # no duplicates
s += 1 # next target sum
Output:
for p in pairSums(): print(p)
(0, 0)
(0, 1)
(0, -1)
(1, 0)
(-1, 0)
(2, 0)
(-2, 0)
(0, -2)
(0, 2)
(1, 1)
(-1, -1)
(3, 0)
(0, 3)
(0, -3)
(-3, 0)
(1, 2)
(-1, -2)
(2, 1)
...
First notice that you can lay your totals out in a grid for non-negative values:
x
3|3
2|23
1|123
0|0123
-+----
|0123y
Here we can see a pattern where the diagonals are your totals. Let's just trace some systematic line through them. The following shows an order you could walk through them:
x
3|6
2|37
1|148
0|0259
-+----
|0123y
Here the matrix contains the order of the iterations.
This solves your problem for non-negative values of x and y. To get the rest you can just negate x and y, making sure you don't do it for when they are zero. Something like this:
def generate_triplets(n):
yield 0, (0, 0)
for t in range(1, n + 1): # Iterate over totals t
for x in range(0, t + 1): # Iterate over component x
y = t - x # Calclulate component y
yield t, (x, y) # Default case is non-negative
if y > 0:
yield t, (x, -y)
if x > 0:
yield t, (-x, y)
if x > 0 and y > 0:
yield t, (-x, -y)
def generate_pairs(n):
yield from (pair for t, pair in generate_triplets(n))
# for pair in generate_pairs(10):
# print(pair)
for t, (x, y) in generate_triplets(3):
print(f'{t} = abs({x}) + abs({y})')
This outputs
0 = abs(0) + abs(0)
1 = abs(0) + abs(1)
1 = abs(0) + abs(-1)
1 = abs(1) + abs(0)
1 = abs(-1) + abs(0)
2 = abs(0) + abs(2)
2 = abs(0) + abs(-2)
2 = abs(1) + abs(1)
2 = abs(1) + abs(-1)
2 = abs(-1) + abs(1)
2 = abs(-1) + abs(-1)
2 = abs(2) + abs(0)
2 = abs(-2) + abs(0)
3 = abs(0) + abs(3)
3 = abs(0) + abs(-3)
3 = abs(1) + abs(2)
3 = abs(1) + abs(-2)
3 = abs(-1) + abs(2)
3 = abs(-1) + abs(-2)
3 = abs(2) + abs(1)
3 = abs(2) + abs(-1)
3 = abs(-2) + abs(1)
3 = abs(-2) + abs(-1)
3 = abs(3) + abs(0)
3 = abs(-3) + abs(0)
Or as pairs:
(0, 0)
(0, 1)
(0, -1)
(1, 0)
(-1, 0)
(0, 2)
(0, -2)
(1, 1)
(1, -1)
(-1, 1)
(-1, -1)
(2, 0)
(-2, 0)
...
For each sum, walk the diagonal in one quadrant and rotate each coordinate into the other quadrants:
from itertools import count
def coordinates():
yield 0, 0
for sum in count(1):
for x in range(sum):
y = sum - x
yield x, y
yield y, -x
yield -x, -y
yield -y, x
(I hope I understood the requirements) I used itertools product:
>>> for i in sorted(itertools.product(range(-5, 4), range(-5, 4)), key=lambda tup: abs(tup[0]) + abs(tup[1])):
print(i)
...
(0, 0)
(-1, 0)
(0, -1)
(0, 1)
(1, 0)
(-2, 0)
(-1, -1)
(-1, 1)
(0, -2)
(0, 2)
(1, -1)
(1, 1)
(2, 0)
(-3, 0)
(-2, -1)
(-2, 1)
(-1, -2)
(-1, 2)
(0, -3)
(0, 3)
(1, -2)
(1, 2)
(2, -1)
...

Nearest neighbors

I am writing a piece of code to print the nearest neighbors for the elements of a matrix. I get an
"invalid index" error
when I try to print the list of the neighbours (last line). Can you spot why?
Here's the code:
neighbours = ndarray((ran_x-2, ran_y-2,8),int)
for i in range(0, ran_x):
for j in range(0, ran_y):
if 1 < i < ran_x-1:
if 1 < j < ran_y-1:
neighbours = ([matrix[i-1,j-1],matrix[i-1,j],matrix[i-1,j+1],matrix[i,j-1],matrix[i,j+1],matrix[i+1,j-1],matrix[i+1,j],matrix[i+1,j+1]])
neighbours = np.array(neighbours)
for l in range(1, ran_x-1):
for m in range(1, ran_y-1):
print neighbours[l,m]
Look at the size of your array, it's a (ran_x - 2) * (ran_y - 2) elements array:
neighbours = ndarray((ran_x-2, ran_y-2,8),int)
And you try to access the elements at index ran_x-1 and ran_y-1 which are out of bound.
sliding window stride_tricks is great for this (https://stackoverflow.com/a/11000193/541038)
import numpy as np
from numpy.lib.stride_tricks import as_strided
def sliding_window(arr, window_size):
""" Construct a sliding window view of the array"""
arr = np.asarray(arr)
window_size = int(window_size)
if arr.ndim != 2:
raise ValueError("need 2-D input")
if not (window_size > 0):
raise ValueError("need a positive window size")
shape = (arr.shape[0] - window_size + 1,
arr.shape[1] - window_size + 1,
window_size, window_size)
if shape[0] <= 0:
shape = (1, shape[1], arr.shape[0], shape[3])
if shape[1] <= 0:
shape = (shape[0], 1, shape[2], arr.shape[1])
strides = (arr.shape[1]*arr.itemsize, arr.itemsize,
arr.shape[1]*arr.itemsize, arr.itemsize)
return as_strided(arr, shape=shape, strides=strides)
def cell_neighbors(arr, i, j, d):
"""Return d-th neighbors of cell (i, j)"""
w = sliding_window(arr, 2*d+1)
ix = np.clip(i - d, 0, w.shape[0]-1)
jx = np.clip(j - d, 0, w.shape[1]-1)
i0 = max(0, i - d - ix)
j0 = max(0, j - d - jx)
i1 = w.shape[2] - max(0, d - i + ix)
j1 = w.shape[3] - max(0, d - j + jx)
return w[ix, jx][i0:i1,j0:j1].ravel()
x = np.arange(8*8).reshape(8, 8)
print x
for d in [1, 2]:
for p in [(0,0), (0,1), (6,6), (8,8)]:
print "-- d=%d, %r" % (d, p)
print cell_neighbors(x, p[0], p[1], d=d)
The problem is you continuously reassign neighbours to a 1D array with length 8. Instead you should assign the neighbour data to a slice of the array you had already created:
for i in range(1, ran_x-1):
for j in range(1, ran_y-1):
neighbours[i-1,j-1,:] = [matrix[i-1,j-1],matrix[i-1,j],matrix[i-1,j+1],matrix[i,j-1],matrix[i,j+1],matrix[i+1,j-1],matrix[i+1,j],matrix[i+1,j+1]]
Note that I changed the ranges so you don't need the if statements. Your code would be faster and (arguably) neater as the following:
neighbours = np.empty((ran_x-2, ran_y-2, 8), int)
# bool array to extract outer ring from a 3x3 array:
b = np.array([[1,1,1],[1,0,1],[1,1,1]], bool)
for i in range(ran_x-2):
for j in range(ran_y-2):
neighbours[i,j,:] = matrix[i:i+3, j:j+3][b]
Of course it would be faster still to immediately print the neighbours without storing them at all, if that's all you need.

Determining neighbours of cell two dimensional list

I have a list of lists, something like
[[1, 2, 3,],[4, 5, 6,],[7, 8, 9]].
Represented graphically as:
1 2 3
4 5 6
7 8 9
I'm looking for an elegant approach to check the value of neighbours of a cell, horizontally, vertically and diagonally. For instance, the neighbours of [0][2] are [0][1], [1][1] and [1][2] or the numbers 2, 5, 6.
Now I realise, I could just do a bruteforce attack checking every value a la:
[i-1][j]
[i][j-1]
[i-1][j-1]
[i+1][j]
[i][j+1]
[i+1][j+1]
[i+1][j-1]
[i-1][j+1]
But thats easy, and I figured I can learn more by seeing some more elegant approaches.
# Size of "board"
X = 10
Y = 10
neighbors = lambda x, y : [(x2, y2) for x2 in range(x-1, x+2)
for y2 in range(y-1, y+2)
if (-1 < x <= X and
-1 < y <= Y and
(x != x2 or y != y2) and
(0 <= x2 <= X) and
(0 <= y2 <= Y))]
>>> print(neighbors(5, 5))
[(4, 4), (4, 5), (4, 6), (5, 4), (5, 6), (6, 4), (6, 5), (6, 6)]
I don't know if this is considered clean, but this one-liner gives you all neighbors by iterating over them and discarding any edge cases.
Assuming you have a square matrix:
from itertools import product
size = 3
def neighbours(cell):
for c in product(*(range(n-1, n+2) for n in cell)):
if c != cell and all(0 <= n < size for n in c):
yield c
Using itertools.product and thanks to Python's yield expression and star operator, the function is pretty dry but still readable enough.
Given a matrix size of 3, you can then (if needed) collect the neighbours in a list:
>>> list(neighbours((2,2)))
[(1, 1), (1, 2), (2, 1)]
What the function does can be visualized as follows:
mb...
from itertools import product, starmap
x, y = (8, 13)
cells = starmap(lambda a,b: (x+a, y+b), product((0,-1,+1), (0,-1,+1)))
// [(8, 12), (8, 14), (7, 13), (7, 12), (7, 14), (9, 13), (9, 12), (9, 14)]
print(list(cells)[1:])
for x_ in range(max(0,x-1),min(height,x+2)):
for y_ in range(max(0,y-1),min(width,y+2)):
if (x,y)==(x_,y_): continue
# do stuff with the neighbours
>>> a=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> width=height=3
>>> x,y=0,2
>>> for x_ in range(max(0,x-1),min(height,x+2)):
... for y_ in range(max(0,y-1),min(width,y+2)):
... if (x,y)==(x_,y_): continue
... print a[x_][y_]
...
2
5
6
If someone is curious about alternative way to pick direct (non-diagonal) neighbors, here you go:
neighbors = [(x+a[0], y+a[1]) for a in
[(-1,0), (1,0), (0,-1), (0,1)]
if ( (0 <= x+a[0] < w) and (0 <= y+a[1] < h))]
There's no cleaner way to do this. If you really want you could create a function:
def top(matrix, x, y):
try:
return matrix[x][y - 1];
except IndexError:
return None
Here is your list:
(x - 1, y - 1) (x, y - 1) (x + 1, y - 1)
(x - 1, y) (x, y) (x + 1, y)
(x - 1, y + 1) (x, y + 1) (x + 1, y + 1)
So the horizontal neighbors of (x, y) are (x +/- 1, y).
The vertical neighbors are (x, y +/- 1).
Diagonal neighbors are (x +/- 1, y +/- 1).
These rules apply for an infinite matrix.
To make sure the neighbors fit into a finite matrix, if the initial (x, y) is at the edge, just apply one more restriction to the coordinates of neighbors - the matrix size.
>>> import itertools
>>> def sl(lst, i, j):
il, iu = max(0, i-1), min(len(lst)-1, i+1)
jl, ju = max(0, j-1), min(len(lst[0])-1, j+1)
return (il, iu), (jl, ju)
>>> lst = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> tup = 0, 2
>>> [lst[i][j] for i, j in itertools.product(*sl(lst, *tup)) if (i, j) != tup]
[2, 5, 6]
I don't know how elegant it seems to you, but it seems to work w/o any hard-coding.
This generates all indices:
def neighboring( array ):
nn,mm = len(array), len(array[0])
offset = (0,-1,1) # 0 first so the current cell is the first in the gen
indices = ( (i,j) for i in range(nn) for j in range(mm) )
for i,j in indices:
all_neigh = ( (i+x,j+y) for x in offset for y in offset )
valid = ( (i,j) for i,j in all_neigh if (0<=i<nn) and (0<=j<mm) ) # -1 is a valid index in normal lists, but not here so throw it out
yield valid.next(), valid ## first is the current cell, next are the neightbors
for (x,y), neigh in neighboring( l ):
print l[x][y], [l[x][y] for x,y in neigh]
If lambdas daunt you here you are .But lambdas make your code look clean.#johniek_comp has a very clean solution TBH
k,l=(2,3)
x = (0,-1,+1)
y = (0,-1,+1)
cell_u = ((k+a,l+b) for a in x for b in y)
print(list(cell_u))
Inspired by one of the previous answers.
You can use min() and max() functions to shorten the calculations:
width = 3
height = 3
[(x2, y2) for x2 in range(max(0, x-1), min(width, x+2))
for y2 in range(max(0, y-1), min(height, y+2))
if (x2, y2) != (x, y)]
Thank you to #JS_is_bad for a great hint about the neighbors. Here is the running code for this problem:
def findNeighbours(l,elem):
#This try is for escaping from unbound error that happens
#when we try to iterate through indices that are not in array
try:
#Iterate through each item of multidimensional array using enumerate
for row,i in enumerate(l):
try:
#Identifying the column index of the givem element
column=i.index(elem)
except ValueError:
continue
x,y=row,column
# hn=list(((x,y+1),(x,y-1))) #horizontal neighbours=(x,y+/-1)
# vn=list(((x+1,y),(x-1,y))) #vertical neighbours=(x+/-1,y)
# dn=list(((x+1,y+1),(x-1,y-1),(x+1,y-1),(x-1,y+1))) #diagonal neighbours=(x+/-1,y+/-1)
#Creating a list with values that are actual neighbors for the extracted index of array
neighbours=[(x,y+1),(x,y-1),(x+1,y),(x-1,y),(x+1,y+1),(x-1,y-1),(x+1,y-1),(x-1,y+1)]
#Creating a universe of indices from given array
index_list=[(i,j) for i in range(len(l)) for j in range(len(l[i]))]
#Looping through index_list and nested loop for neighbours but filter for matched ones
# and extract the value of respective index
return_values=[l[index[0]][index[1]] for index in index_list for neighbour in neighbours if index==neighbour]
return return_values,neighbours
except UnboundLocalError:
return []
Inspired by johniek's answer here is my solution which also checks for boundaries.
def get_neighbours(node, grid_map):
row_index, col_index = node
height, width = len(grid_map), len(grid_map[0])
cells = list(starmap(lambda a, b: (row_index + a, col_index + b), product((0, -1, +1), (0, -1, +1))))
cells.pop(0) # do not include original node
cells = list(filter(lambda cell: cell[0] in range(height) and cell[1] in range(width), cells))
return cells
def numCells(grid):
x=len(grid)
y=len(grid[0])
c=0
for i in range(x):
for j in range(y):
value_=grid[i][j]
f=1
for i2 in range(max(0,i-1),min(x,i+2)):
for j2 in range(max(0,j-1),min(y,j+2)):
if (i2,j2) != (i,j) and value_<=grid[i2][j2]:
flag=0
break
if flag ==0:
break
else:
c+=1
return c
def getNeighbors(matrix: list, point: tuple):
neighbors = []
m = len(matrix)
n = len(matrix[0])
x, y = point
for i in range (x -1, x +2): #prev row to next row
for j in range(y - 1, y +2): #prev column to next col
if (0 <= i < m) and (0 <= j < n):
neighbors.append((i,j))
return neighbors
maybe you are checking a sudoku box. If the box is n x n and current cell is (x,y) start checking:
startingRow = x / n * n;
startingCol = y/ n * n

Categories