More Pythonic way to build random clusters in Python - 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)

Related

How to fill a distance between two meshed as filled cells/mesh in pyvista?

So I look at this sample and I want to create a mesh/surface grid that fills that distance. How to do such a thing in PyVista?
What I try and seem not to be able to bridge with Andras Deak beautiful answer:
import pyvista as pv
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import KDTree
import PVGeo
from PVGeo import interface
from PVGeo.filters import BuildSurfaceFromPoints
b = pv.read('./top.vtk') # PolyData
t = pv.read('./bottom.vtk') # PolyData
dim = (int(b.bounds[1]-b.bounds[0]), int(b.bounds[3]-b.bounds[2]), 1)
z_range = np.arange(b.bounds[4], b.bounds[5] )
bottom = BuildSurfaceFromPoints().apply(b)
top = BuildSurfaceFromPoints().apply(t)
grid_2d = top.points.reshape(dim[:-1] + (3,), order='F')[..., :-1]
That sadly fails on line with grid_2d with
ValueError: cannot reshape array of size 1942464 into shape
(30150,26750,3)
I don't know if there's a built-in way to interpolate between two surfaces, but it's not very hard to do so using just numpy.
Here's an example that uses Perlin noise to generate two sheets of data on the same grid in two different heights. The actual code for the answer comes after.
import numpy as np
import pyvista as pv
# generate two sheets of input data
noise = pv.perlin_noise(2, (0.2, 0.2, 0.2), (0, 0, 0))
bounds_2d = (-10, 10, -10, 10)
dim = (40, 50, 1)
bottom, top = [
pv.sample_function(noise, dim=dim, bounds=bounds_2d + (z, z)).warp_by_scalar()
for z in [-5, 5]
]
# actual answer starts here
# the top and bottom sheets are named `top` and `bottom`
# and they share the same 2d grid
# rebuild grid points
grid_2d = top.points.reshape(dim[:-1] + (3,), order='F')[..., :-1]
values_x = grid_2d[:, 0, 0]
values_y = grid_2d[0, :, 1]
# generate full grid with equidistant interpolation in each (x, y)
nz = 10
scale = np.linspace(0, 1, nz)
scale_z = scale[:, None] * [0, 0, 1] # shape (nz, 3)
scale_z_inv = (1 - scale[:, None]) * [0, 0, 1] # shape (nz, 3)
z_bottom = bottom.points.reshape(dim[:-1] + (3,), order='F')[..., -1] # shape (nx, ny)
z_top = top.points.reshape(dim[:-1] + (3,), order='F')[..., -1] # shape (nx, ny)
interpolated_z = scale * z_bottom[..., None] + (1 - scale) * z_top[..., None] # shape (nx, ny, nz)
grid_2d_in_3d = np.pad(grid_2d, [(0, 0), (0, 0), (0, 1)]) # shape (nx, ny, 3)
final_grid = grid_2d_in_3d[..., None, :] + interpolated_z[..., None] * [0, 0, 1] # shape (nx, ny, nz, 3)
mesh = pv.StructuredGrid(*final_grid.transpose())
# plot the two sheets and the interpolated grid
pv.set_plot_theme('document')
plotter = pv.Plotter()
plotter.add_mesh(bottom, show_scalar_bar=False)
plotter.add_mesh(top, show_scalar_bar=False)
plotter.add_mesh(mesh, style='wireframe')
plotter.show()

Finding the corrdinates between points in a meshgrid [duplicate]

This question already has answers here:
Python: Draw line between two coordinates in a matrix
(5 answers)
Closed 2 years ago.
Say that I have a simple meshgrid:
xx,yy = np.meshgrid(10,10)
and two data points within that meshgrid, e.g. (2,3) and (8,8). What's the easiest way to get an array of all the points in the meshgrid between the two data points forming the shortest distance (i.e. approximating a straight line within the meshgrid)?
Check this, you can also control tolerance value
import numpy as np
xx, yy = np.meshgrid(range(1, 11), range(1, 11))
# points
p1 = [2, 3]
p2 = [8, 8]
# slope
m = (p2[1] - p1[1])/(p2[0] - p1[0])
# 2D line equation
through_line = (yy - m*xx) - (p1[1] - m*p1[0])
# to get only the exact points
mask = np.isclose(through_line, 0)
print(list(zip(xx[mask], yy[mask])))
# to get approximate points
tol = 0.2
mask = np.less(np.abs(through_line), tol)
print(list(zip(xx[mask], yy[mask])))
[(2, 3), (8, 8)]
[(1, 2), (2, 3), (3, 4), (7, 7), (8, 8), (9, 9)]

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

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

Calculate cross-product of every three consecutive points in a list and check whether all cross products have same signs or not

I created a list of vertices of a polygon, using a 'Point' class and 'Polygon' class.. Now, to check the type of the polygon, that whether it is a 'concave; or 'convex' polygon, I want to calculate the cross-products of every three consecutive points present in the list of vertices. As an example, consider a list : vertices = [p1, p2, p3, p4, p5, p6]. Considering this list, the sequence should be p1,p2,p3...p2,p3,p4...p3,p4,p5...p4,p5,p6...and the p5,p6,p1. I mean, the last cross product will be between last 2 elements and 1st element of the list because polygon is a closed figure.
Then after calculating, the program should check whether all cross products are -ive (negative) or all are +ive (positive) because these are the conditions for the polygon to be convex.
class Polygon:
def __init__(self,*vertices):
self.vertices=[Polygon.Point(v[0],v[1]) for v in vertices]
def CrossProduct(self, A, B, C):
return (B.x - A.x) * (C.y - B.y) -(B.y - A.y) * (C.x - B.x)
#property
def shape(self): #Method for determining the type of polygon i.e. Convex or concave
# if (all cross product >=0 or all cross products <=0):
#return 'Convex'
# return 'Concave'
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
### MAIN PROGRAM ###
poly1 = Polygon((3,4), (5,11), (12,8), (9,5), (5,6)) #Concave Polygon
poly2 = Polygon((5.09,5.80), (1.68,4.90), (1.48,1.38), (4.76,0.10), (7.00,2.83)) #Convex Polygon
print(poly1.shape)
print(poly2.shape)
Create all needed 3-tuples using zip(..) and check them with all(..):
class Polygon:
def __init__(self,*vertices):
self.vertices=[Polygon.Point(v[0],v[1]) for v in vertices]
def CrossProduct(self, A, B, C):
return (B.x - A.x) * (C.y - B.y) -(B.y - A.y) * (C.x - B.x)
#property
def shape(self): #Method for determining the type of polygon i.e. Convex or concave
p0 = self.vertices[0:1]
# debugging printout of points that are going to be checked
points = list(zip(self.vertices, self.vertices[1:], self.vertices[2:] + p0))
print(*points)
print ([self.CrossProduct(*p) for p in points])
if all(self.CrossProduct(*p) >= 0 for p in points) or all(
self.CrossProduct(*p) < 0 for p in points):
return "Convex"
return "Concave"
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __str__(self):
return f"({self.x}, {self.y})"
def __repr__(self):
return str(self)
poly1 = Polygon((3,4), (5,11), (12,8), (9,5), (5,6)) # Concave
poly2 = Polygon((5.09,5.80), (1.68,4.90), (1.48,1.38), (4.76,0.10), (7.00,2.83))
print(poly1.shape) # Concave
print(poly2.shape) # Convex
Output:
((3, 4), (5, 11), (12, 8)) ((5, 11), (12, 8), (9, 5)) ((12, 8), (9, 5), (5, 6)) ((9, 5), (5, 6), (3, 4))
[-55, -30, -15, 10]
Concave
((5.09, 5.8), (1.68, 4.9), (1.48, 1.38)) ((1.68, 4.9), (1.48, 1.38), (4.76, 0.1)) ((1.48, 1.38), (4.76, 0.1), (7.0, 2.83)) ((4.76, 0.1), (7.0, 2.83), (5.09, 5.8))
[11.823200000000002, 11.8016, 11.8216, 11.8671]
Convex
After isolating the problem a bit, something like that came to my mind:
pp = [(3,4), (5,11), (12,8), (9,5), (5,6)]
pp = pp + pp[:(len(pp) % 3 - 1)]
print(pp)
c = list(zip(pp, pp[1:], pp[2:]))
def cross_product(p):
print(p)
pass
for pt in c:
cross_product(pt)
Which yields:
[(3, 4), (5, 11), (12, 8), (9, 5), (5, 6), (3, 4)]
((3, 4), (5, 11), (12, 8))
((5, 11), (12, 8), (9, 5))
((12, 8), (9, 5), (5, 6))
((9, 5), (5, 6), (3, 4))
So, first of all you have to 'pad' the initial list a bit so that it wraps properly - the length has to be divisible by 3 so that we can pack it into groups of 3 points.
After that, it's a matter of simple zip between 3 consecutive elements and a cross-product calculation.

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