Related
This question already has answers here:
Cartesian product of x and y array points into single array of 2D points
(17 answers)
Closed 1 year ago.
I have two numpy arrays: alpha=[0,1] and beta=[2,3,4].
I want to combine them in order to create a new array of tuple which is the result of all possible combinations of the two previous arrays.
x= [(0,2)(0,3)(0,4)(1,2)(1,3)(1,4)]
is there a function which in numpy package or i need to do it by my self? If i have to do it, which is the optimal way?
You can use itertools.product:
import numpy as np
import itertools
alpha = np.array([0, 1])
beta = np.array([2, 3, 4])
x = list(itertools.product(alpha, beta))
print(x)
# [(0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4)]
alpha=[0,1]
beta=[2,3,4]
d = [(a, b) for a in alpha for b in beta] # The cartesian product
print(d)
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.
While working with shapely I faced a strange issue. There're 2 points p1 and p2, where the first one belongs to polygon and the second is not. When I tried to find intersection between the LineString containing these 2 points as endpoints with boundary lines of the Polygon, I received message that no intersection was found. I wonder, how is it possible?
from shapely.geometry import Polygon as SPolygon, Point, LineString
p1 = Point(5.414213562373095, 2.585786437626905)
p2 = Point(15.17279752753168, -7.172797527531679)
l = LineString([p1, p2])
l1 = LineString([(2, 2), (2, 6)])
l2 = LineString([(2, 6), (6, 6)])
l3 = LineString([(6, 6), (6, 2)])
l4 = LineString([(6, 2), (2, 2)])
sp = SPolygon([(2, 2), (2, 6), (6, 6), (6, 2)])
print "Polygon contains p1:", sp.contains(p1)
print "Polygon contains p2:", sp.contains(p2)
for i, line in enumerate((l1, l2, l3, l4)):
res = l.intersects(line)
print "Line {0} intersects l1: {1}".format(i, res)
And here are output:
Polygon contains p1: True
Polygon contains p2: False
Line 0 intersects l1: False
Line 1 intersects l1: False
Line 2 intersects l1: False
Line 3 intersects l1: False
I changed l.intersects(line) to l.intersection(line) and I did get an intersection at
LINESTRING (6 6, 6 2) at Point (6, 2)
Not sure why .instersects() is acting differently.
Then I rounded p1 and p2
p1 = Point(round(5.414213562373095, 2), round(2.585786437626905, 2))
p2 = Point(round(15.17279752753168, 2), round(-7.172797527531679, 2))
And I got 2 intersections at
LINESTRING (6 6, 6 2) at POINT (6 2)
LINESTRING (6 2, 2 2) at POINT (6 2)
This fix also worked with .intersects() (2 True's)
Shapely can be a bit touchy with floating point precision, and I usually can fix an issue by rounding. Though this may not be acceptable for you.
This question already has answers here:
How to get the cartesian product of multiple lists
(17 answers)
Closed 8 months ago.
I'm looking to generate the cartesian product of a relatively large number of arrays to span a high-dimensional grid. Because of the high dimensionality, it won't be possible to store the result of the cartesian product computation in memory; rather it will be written to hard disk. Because of this constraint, I need access to the intermediate results as they are generated. What I've been doing so far is this:
for x in xrange(0, 10):
for y in xrange(0, 10):
for z in xrange(0, 10):
writeToHdd(x,y,z)
which, apart from being very nasty, is not scalable (i.e. it would require me writing as many loops as dimensions). I have tried to use the solution proposed here, but that is a recursive solution, which therefore makes it quite hard to obtain the results on the fly as they are being generated. Is there any 'neat' way to do this other than having a hardcoded loop per dimension?
In plain Python, you can generate the Cartesian product of a collection of iterables using itertools.product.
>>> arrays = range(0, 2), range(4, 6), range(8, 10)
>>> list(itertools.product(*arrays))
[(0, 4, 8), (0, 4, 9), (0, 5, 8), (0, 5, 9), (1, 4, 8), (1, 4, 9), (1, 5, 8), (1, 5, 9)]
In Numpy, you can combine numpy.meshgrid (passing sparse=True to avoid expanding the product in memory) with numpy.ndindex:
>>> arrays = np.arange(0, 2), np.arange(4, 6), np.arange(8, 10)
>>> grid = np.meshgrid(*arrays, sparse=True)
>>> [tuple(g[i] for g in grid) for i in np.ndindex(grid[0].shape)]
[(0, 4, 8), (0, 4, 9), (1, 4, 8), (1, 4, 9), (0, 5, 8), (0, 5, 9), (1, 5, 8), (1, 5, 9)]
I think I figured out a nice way using a memory mapped file:
def carthesian_product_mmap(vectors, filename, mode='w+'):
'''
Vectors should be a tuple of `numpy.ndarray` vectors. You could
also make it more flexible, and include some error checking
'''
# Make a meshgrid with `copy=False` to create views
grids = np.meshgrid(*vectors, copy=False, indexing='ij')
# The shape for concatenating the grids from meshgrid
shape = grid[0].shape + (len(vectors),)
# Find the "highest" dtype neccesary
dtype = np.result_type(*vectors)
# Instantiate the memory mapped file
M = np.memmap(filename, dtype, mode, shape=shape)
# Fill the memmap with the grids
for i, grid in enumerate(grids):
M[...,i] = grid
# Make sure the data is written to disk (optional?)
M.flush()
# Reshape to put it in the right format for Carthesian product
return M.reshape((-1, len(vectors)))
But I wonder if you really need to store the whole Carthesian product (there's a lot of data duplication). Is it not an option to generate the rows in the product at the moment they're needed?
It seems you just want to loop over an arbitrary number of dimensions. My generic solution for this is using an index field and increment indices plus handling overflows.
Example:
n = 3 # number of dimensions
N = 1 # highest index value per dimension
idx = [0]*n
while True:
print(idx)
# increase first dimension
idx[0] += 1
# handle overflows
for i in range(0, n-1):
if idx[i] > N:
# reset this dimension and increase next higher dimension
idx[i] = 0
idx[i+1] += 1
if idx[-1] > N:
# overflow in the last dimension, we are finished
break
Gives:
[0, 0, 0]
[1, 0, 0]
[0, 1, 0]
[1, 1, 0]
[0, 0, 1]
[1, 0, 1]
[0, 1, 1]
[1, 1, 1]
Numpy has something similar inbuilt: ndenumerate.
This should be fairly easy, but I'm getting a headache from trying to figure it out. I want to list all the coordinates between two points. Like so:
1: (1,1)
2: (1,3)
In between: (1,2)
Or
1: (1,1)
2: (5,1)
In between: (2,1), (3,1), (4,1)
It does not need to work with diagonals.
You appear to be a beginning programmer. A general technique I find useful is to do the job yourself, on paper, then look at how you did it and translate that to a program. If you can't see how, break it down into simpler steps until you can.
Depending on how you want to handle the edge cases, this seems to work:
def points_between(p1, p2):
xs = range(p1[0] + 1, p2[0]) or [p1[0]]
ys = range(p1[1] + 1, p2[1]) or [p1[1]]
return [(x,y) for x in xs for y in ys]
print points_between((1,1), (5,1))
# [(2, 1), (3, 1), (4, 1)]
print points_between((5,6), (5,12))
# [(5, 7), (5, 8), (5, 9), (5, 10), (5, 11)]