How to efficiently find permutations of all coordinates in 2D plane? - python

I need to list all coordinates of a 2D plane, with x and y axis ranging from 0 to 1066. Most answers I found recommends creating a list beforehand of all value of x and y axis, but I don't think that is the most efficient, since there will be 1067^2 elements as the result. The list should look something like this:
list = [(0,0), (0,1),...(0,1066), (1,0), (1,1),...,(1,1066),...,(1066,0),...,(1066,1066)].
I was thinking of using permutations since order matters, but I am still figuring out the best method.

You can create a generator that you can use to iterate over every single pair of coordinates, without creating a rather large list of values:
As a generator expression:
sizex, sizey = 3, 3 # replace with your own values
g = ((x, y) for x in range(sizex) for y in range(sizey))
print(type(g))
for coord in g:
print(coord)
output:
<class 'generator'>
(0, 0)
(0, 1)
(0, 2)
(1, 0)
(1, 1)
(1, 2)
(2, 0)
(2, 1)
(2, 2)
A generator as a lambda function:
sizex, sizey = 3, 3 # replace with your own values
g = lambda sizex, sizey: ((x, y) for x in range(sizex) for y in range(sizey))
print(type(g))
for coord in g(sizex, sizey):
print(coord)
out:
<class 'function'>
(0, 0)
(0, 1)
(0, 2)
(1, 0)
(1, 1)
(1, 2)
(2, 0)
(2, 1)
(2, 2)
As a regular function:
def g(sizex, sizey):
for x in range(sizex):
for y in range(sizey):
yield(x, y)
sizex, sizey = 3, 3 # replace with your own values
print(type(g))
for coord in g(sizex, sizey):
print(coord)

Use Cartesian product to create lazy loading
impot itertools
mx, my = 1066, 1066
for x, y in itertools.product(range(mx), range(my)):
print(x, y)
0 0
0 1
0 2
.
.
.
1065 1063
1065 1064
1065 1065

Related

Find neighbors given a specific coordinate in a 2D array

I am trying to solve a problem using python and this is my first time to write python so I hope you could help me out. I have a 2D array its values is -1,0,1 what I want to do is take the co-ordinates of a specific element and get the co-ordinates of all the adjacent elements
Matrix = [[ 1,-1, 0],
[ 1, 0, 0],
[-1,-1, 1]]
for example if I have (0,0) so the function could return (0,1),(1,0)
Since you want to work from the coordinates, a simple way I can think of is to define a grid graph using NetworkX and to look for the neighbours:
import networkx as nx
import numpy as np
a = np.array([[1,-1,0],
[1,0,0],
[-1,-1,1]])
G = nx.grid_2d_graph(*a.shape)
list(G.neighbors((0,0)))
# [(1, 0), (0, 1)]
Or for the "coordinates" of the middle value for instance:
list(G.neighbors((1,1)))
# [(0, 1), (2, 1), (1, 0), (1, 2)]
If you want to use them to index the array:
ix = list(G.neighbors((0,0)))
a[tuple(ix)]
# array([ 1, -1])
It's not the best solution but it can help if you don't want to import any lib:
def get_neighbors(matrix, x, y):
positions = []
positions.append(get_neighbor(matrix, x, y-1))
positions.append(get_neighbor(matrix, x, y+1))
positions.append(get_neighbor(matrix, x-1, y))
positions.append(get_neighbor(matrix, x+1, y))
return list(filter(None, positions))
def get_neighbor(matrix, x, y):
if (x >= 0 and x < len(matrix[0])) and (y >= 0 and y < len(matrix[1])):
return (x, y)
get_neighbors(your_matrix, x_position, y_position)

More Pythonic way to build random clusters in Python

I would like to create a function which will create a uniform distribution random cluster centered around a set of co-ordinates and with a specified radius, I have done this with the below:
import numpy as np
# create cluster builder
def cluster(center, radius=10, n=50):
xx = np.random.uniform(center[0]-radius, center[0]+radius, size=n)
yy = np.random.uniform(center[1]-radius, center[1]+radius, size=n)
zz = np.random.uniform(center[2]-radius, center[2]+radius, size=n)
return xx, yy, zz
# create random cluster
xx1, yy1, zz1 = cluster((25, 15, 5))
This works as expected, but I just feel that they're must be a more Pythonic way to build the cluster function. Does anyone have any suggestions?
np.random.uniform also accepts low and high as arrays/lists. Hence, we can simply do -
c = np.asarray(center)
xx,yy,zz = np.random.uniform(c-radius, c+radius, size=(n,3)).T
If any older version only supported scalar low and high, we can use some scaling -
xx,yy,zz = np.random.uniform(size=(3,n))*radius*2 + c[:,None] - radius
You can define cluster() so that it vectorizes all operations
def cluster(center, radius=10, n=50):
center = np.atleast_1d(np.asarray(center))[:, None, None]
radius = np.atleast_1d(np.asarray(radius))[None, :, None]
shape = (center.shape[0], radius.shape[1], n)
return np.random.uniform(center - radius, center + radius, size=shape)
and get all values in one single call:
cluster(25, 10).shape # (1, 1, 50)
cluster((25, 15, 5), 10).shape # (3, 1, 50)
cluster((25, 15, 5), (10, 5)).shape # (3, 2, 50)
cluster((25, 15, 5), (10, 5), n=100).shape # (3, 2, 100)
of course you can still separate the result into xx, yy, zz:
xx, yy, zz = cluster((25, 15, 5), (10, 5), n=100)
xx.shape # (2, 100)
yy.shape # (2, 100)
zz.shape # (2, 100)

How to segment a matrix by neighbouring values?

Suppose I have a matrix like this:
m = [0, 1, 1, 0,
1, 1, 0, 0,
0, 0, 0, 1]
And I need to get the coordinates of the same neighbouring values (but not diagonally):
So the result would be a list of lists of coordinates in the "matrix" list, starting with [0,0], like this:
r = [[[0,0]],
[[0,1], [0,2], [1,0], [1,1]],
[[0,3], [1,2], [1,3], [2,0], [2,1], [2,2]]
[[2,3]]]
There must be a way to do that, but I'm really stuck.
tl;dr: We take an array of zeros and ones and use scipy.ndimage.label to convert it to an array of zeros and [1,2,3,...]. We then use np.where to find the coordinates of each element with value > 0. Elements that have the same value end up in the same list.
scipy.ndimage.label interprets non-zero elements of a matrix as features and labels them. Each unique feature in the input gets assigned a unique label. Features are e.g. groups of adjacent elements (or pixels) with the same value.
import numpy as np
from scipy.ndimage import label
# make dummy data
arr = np.array([[0,1,1,0], [1,1,0,0], [0,0,0,1]])
#initialise list of features
r = []
Since OP wanted all features, that is groups of zero and non-zero pixels, we use label twice: First on the original array, and second on 1 - original array. (For an array of zeros and ones, 1 - array just flips the values).
Now, label returns a tuple, containing the labelled array (which we are interested in) and the number of features that it found in that array (which we could use, but when I coded this, I chose to ignore it. So, we are interested in the first element of the tuple returned by label, which we access with [0]:
a = label(arr)[0]
b = label(1-arr)[0]
Now we check which unique pixel values label has assigned. So we want the set of a and b, repectively. In order for set() to work, we need to linearise both arrays, which we do with .ravel(). We have to subtract {0} in both cases, because for both a and b we are interested in only the non-zero values.
So, having found the unique labels, we loop through these values, and use np.where to find where on the array a given value is located. np.where returns a tuple of arrays. The first element of this tuple are all the row-coordinates for which the condition was met, and the second element are the column-coordinates.
So, we can use zip(* to unpack the two containers of length n to n containers of length 2. This means that we go from list of all row-coords + list of all column-coords to list of all row-column-coordinate pairs for which the condition is met. Finally in python 3, zip is a generator, which we can evaluate by calling list() on it. The resulting list is then appended to our list of coordinates, r.
for x in set(a.ravel())-{0}:
r.append(list(zip(*np.where(a==x))))
for x in set(b.ravel())-{0}:
r.append(list(zip(*np.where(b==x))))
print(r)
[[(0, 1), (0, 2), (1, 0), (1, 1)],
[(2, 3)],
[(0, 0)],
[(0, 3), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2)]]
That said, we can speed up this code slightly by making use of the fact that label returns the number of features it assigned. This allows us to avoid the set command, which can take time on large arrays:
a, num_a = label(arr)
for x in range(1, num_a+1): # range from 1 to the highest label
r.append(list(zip(*np.where(a==x))))
A solution with only standard libraries:
from pprint import pprint
m = [0, 1, 1, 0,
1, 1, 0, 0,
0, 0, 0, 1]
def is_neighbour(x1, y1, x2, y2):
return (x1 in (x2-1, x2+1) and y1 == y2) or \
(x1 == x2 and y1 in (y2+1, y2-1))
def is_value_touching_group(val, groups, x, y):
for d in groups:
if d['color'] == val and any(is_neighbour(x, y, *cell) for cell in d['cells']):
return d
def check(m, w, h):
groups = []
for i in range(h):
for j in range(w):
val = m[i*w + j]
touching_group = is_value_touching_group(val, groups, i, j)
if touching_group:
touching_group['cells'].append( (i, j) )
else:
groups.append({'color':val, 'cells':[(i, j)]})
final_groups = []
while groups:
current_group = groups.pop()
for c in current_group['cells']:
touching_group = is_value_touching_group(current_group['color'], groups, *c)
if touching_group:
touching_group['cells'].extend(current_group['cells'])
break
else:
final_groups.append(current_group['cells'])
return final_groups
pprint( check(m, 4, 3) )
Prints:
[[(2, 3)],
[(0, 3), (1, 3), (1, 2), (2, 2), (2, 0), (2, 1)],
[(0, 1), (0, 2), (1, 1), (1, 0)],
[(0, 0)]]
Returns as a list of groups under value key.
import numpy as np
import math
def get_keys(old_dict):
new_dict = {}
for key, value in old_dict.items():
if value not in new_dict.keys():
new_dict[value] = []
new_dict[value].append(key)
else:
new_dict[value].append(key)
return new_dict
def is_neighbor(a,b):
if a==b:
return True
else:
distance = abs(a[0]-b[0]), abs(a[1]-b[1])
return distance == (0,1) or distance == (1,0)
def collate(arr):
arr2 = arr.copy()
ret = []
for a in arr:
for i, b in enumerate(arr2):
if set(a).intersection(set(b)):
a = list(set(a+b))
ret.append(a)
for clist in ret:
clist.sort()
return [list(y) for y in set([tuple(x) for x in ret])]
def get_groups(d):
for k,v in d.items():
ret = []
for point in v:
matches = [a for a in v if is_neighbor(point, a)]
ret.append(matches)
d[k] = collate(ret)
return d
a = np.array([[0,1,1,0],
[1,1,0,0],
[0,0,1,1]])
d = dict(np.ndenumerate(a))
d = get_keys(d)
d = get_groups(d)
print(d)
Result:
{
0: [[(0, 3), (1, 2), (1, 3)], [(0, 0)], [(2, 0), (2, 1)]],
1: [[(2, 2), (2, 3)], [(0, 1), (0, 2), (1, 0), (1, 1)]]
}

Sorting 3d arrays in python as per grid

I have grid points in 3d and I would like to sort them based (x,y,z) using python (if possible avoiding loops)..
For example if the input is,
(1,2,1), (0,8,1), (1,0,0) ..
then output should be
(0,8,1), (1,0,0), (1,2,1)..
Sorry for this side track but I am actually doing is reading from a file which has data in following way:
x y z f(x) f(y) f(z)..
what I was doing was following:
def fill_array(output_array,source_array,nx,ny,nz,position):
for i in range(nx):
for j in range(ny):
for k in range(nz):
output_array[i][j][k] = source_array[i][j][k][position]
nx = 8
ny = 8
nz = 8
ndim = 6
x = np.zeros((nx,ny,nz))
y = np.zeros((nx,ny,nz))
z = np.zeros((nx,ny,nz))
bx = np.zeros((nx,ny,nz))
by = np.zeros((nx,ny,nz))
bz = np.zeros((nx,ny,nz))
data_file = np.loadtxt('datafile')
f = np.reshape(data_file, (nx,ny,nz,ndim))
fill_array(x,f,nx,ny,nz,0))
fill_array(y,f,nx,ny,nz,1)
fill_array(z,f,nx,ny,nz,2)
fill_array(fx,f,nx,ny,nz,3)
fill_array(fy,f,nx,ny,nz,4)
fill_array(fz,f,nx,ny,nz,5)
This was working fine when data was arranged (as explained previously) but with file written not in order it is creating problems with plot later on. Is there are better way to do this ? Of course I only want to arrange x,y,z and then associate functional value f(x),f(y),f(z) to its right position (x,y,z)
two updates
1) i am getting following error when I use sorted with either x,y,z,fx,fy,fz or f.
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
2) i need it in that specific way because I am using mayavi then for contour3d
The built-in function sorted does what you want:
>>> a = [(1, 2, 1), (0, 8, 1), (1, 0, 0)]
>>> sorted(a)
[(0, 8, 1), (1, 0, 0), (1, 2, 1)]
Use [sorted][1].
In [71]: sorted(a)
Out[71]: [(0, 8, 1), (1, 0, 0), (1, 2, 1)]
more precisely
In [70]: sorted(a, key=lambda x: (x[0], x[1], x[2]))
Out[70]: [(0, 8, 1), (1, 0, 0), (1, 2, 1)]
key=lambda x: (x[0], x[1], x[2])
at this step we are sorting list at 0th 1st and 2nd element of tuple

Iterating over N dimensions in Python

I have a map, let's call it M, which contains data mapped through N dimensions.
# If it was a 2d map, I could iterate it thusly:
start, size = (10, 10), (3, 3)
for x in range(start[0], start[0]+size[0]):
for y in range(start[1], start[1]+size[1]):
M.get((x, y))
# A 3d map would add a for z in ... and access it thusly
M.get((x, y, z))
# And so on.
My question is: How do I make an iterator that can yield the correct iteration sequence? That is, given start, size = (10, 10), (3, 3) it would yield the 2-tuple sequence (10, 10), (10, 11), (10, 12), (11, 10), (11, 11) etc. And given start, size = (10, 10, 10), (3, 3, 3) it would yield the correct 3-tuple sequence.
Yeah, I tried myself, but my head exploded. Or I can't justify spending time figuring it out, even though it's fun. Take your pick :)
In Python 2.6+:
itertools.product(*[xrange(i, i+j) for i,j in zip(start, size)])
With do it your self generator expreessions:
start, size = (10, 10), (3, 3)
values2=((x+xd,y+yd)
for x,y in (start,)
for xr,yr in (size,)
for xd in range(xr)
for yd in range(yr))
for x,y in values2:
print x,y
start, size = (10, 10, 10), (3, 3, 3)
values3=((x+xd,y+yd, z+zd)
for x,y,z in (start,)
for xr,yr,zr in (size,)
for xd in range(xr)
for yd in range(yr)
for zd in range(zr))
for x,y,z in values3:
print x,y,z

Categories