Related
I have a list of data in Python of the form [(x0, f0), ..., (xn, fn)], where the tuple (xi, fi) represents the location and magnitude, respectively, of the ith element. For example:
point_forces = [(0, 4), (3.5, 2), (0, -3.1), (4, 6), (2, 0), (3.5, -4)]
What is a good way to remove tuples where fi = 0 and combine fi + fj of tuples where xi = xj, returning a result in the form [(x0, f0), ..., (xm, fm)]? Continuing the example, this is what I want to get:
result = [(0, 0.9), (3.5, -2), (4, 6)]
The order in which these operations are applied or the order in which tuples appear in the resultant list does not matter to me as long as xi ≠ xj and fi ≠ 0 for all i, j in [0, m].
Try:
out = {}
for x, f in point_forces:
if f != 0.0:
out[x] = out.get(x, 0) + f
out = [(x, round(f, 2)) for x, f in out.items() if f != 0.0] # if you want to keep the resulting tuples where f=0 then remove the if... part
print(out)
Prints:
[(0, 0.9), (3.5, -2), (4, 6)]
I have some Python code that I would like to run in Matlab. Suppose you have two lists of the same length:
x = [0, 2, 2, 5, 8, 10]
y = [0,2, 4, 7, 3, 3]
P = np.copy(y)
P.sort()
P = np.unique(P, axis=0) # P = [0 2 3 4 7] Takes the list y, sorts it and removes repeated elements
s = list(zip(x,y)) #match list x with y: s = [(0, 0), (2, 2), (2, 4), (5, 7), (8, 3), (10, 3)]
for y_1,y_2 in zip(P,P[1:]): # runs over (0, 2), (2, 3), (3, 4), (4, 7)
for v_1,v_2 in zip(s, s[1:]):
-- process --
Where in this case:
list(zip(s, s[1:])) = [((0, 0), (2, 2)), ((2, 2), (2, 4)), ((2, 4), (5, 7)), ((5, 7), (8, 3)), ((8, 3), (10, 3))]
I would like to translate this in Matlab but I don't know how to replicate the zip function. Any ideas on how I can do this?
MATLAB doesn’t have a zip, but you can accomplish the same thing in various ways. I think that the simplest translation of
for y_1,y_2 in zip(P,P[1:]):
...
is
for ii = 1:numel(P)-1
y_1 = P(ii);
y_2 = P(ii+1);
...
And in general, iterating over zip(x,y) is accomplished by iterating over indices 1:numel(x), then using the loop index to index into the arrays x and y.
Here's an implementation of Python's zip function in Matlab.
function out = zip(varargin)
% Emulate Python's zip() function.
%
% Don't actually use this! It will be slow. Matlab code works differently.
args = varargin;
nArgs = numel(args);
n = numel(args{1});
out = cell(1, n);
for i = 1:n
blah = cell(1, nArgs);
for j = 1:nArgs
if iscell(args{j})
blah(j) = args{j}(i);
else
blah{j} = args{j}(i);
end
end
out{i} = blah;
end
end
But don't use it; the performance will be lousy. In Matlab, you want to keep things in parallel primitive arrays instead, and use vectorized operations on those. Or, if you have to, iterate over array indexes.
I'm using Shapely's polygons for human-generated data. Multiple humans were asked to draw polygons around certain features in images. For each image, we thus have n MultiPolygon's, where n equals the number of participants per image.
I can plot each of these Multipolygon's.
fig, ax = plt.subplots()
for ii, multi_poly in enumerate(multi_polys):
for poly in multi_poly.geoms:
x,y = poly.exterior.xy
plt.plot(x,y, c=colors[ii])
We can see that at some locations, the Multipolygon's overlap, while at others there is no overlap.
I wish to get the overlap, or intersections, of these polygons.
This should be trivial, as I can do something like:
intersection = multi_a.intersection(multi_b) \
.intersection(multi_c) \
.intersection(multi_d) \
.inters...
I can plot this intersection on the previous plot and we see:
This looks pretty good. However, this method only returns the regions where all Multipoloygon's overlap. Is there a way to get the intersection where 75% of the polygons overlap? Or where 50% overlap?
A code example: The following dummy data gives this figure:
P1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
P2 = Polygon([(2.5, 2), (3, 2), (3, 3), (2.5, 3)])
multi_a = MultiPolygon([P1, P2])
P1 = Polygon([(-1, -1), (-1, 2),(2, 2), (2, -1)])
P2 = Polygon([(3, 3), (4, 2), (4, 4), (3, 4)])
multi_b = MultiPolygon([P1,P2])
P1 = Polygon([(-2, 4), (2.2, 4),(2.2, -2), (-2, -2)])
P2 = Polygon([(-1.5, 3), (-1.1, 3), (-1.5, -1), (-1.1, -1)])
multi_c = MultiPolygon([P1,P2])
P1 = Polygon([(2.5, -1), (3.2, -1),(3.2, -2), (-2, -2)])
P2 = Polygon([(3, 0), (4, 0), (3, 1), (4, 1)])
multi_d = MultiPolygon([P1,P2])
On these four multipolygons, the intersection method would returns no intersection as there is not a single spot that all four multipolygons occupy. However, the blue square, accentuated with yellow marker, is occupied by the blue, orange and green polygon. Thus 75% of the multipolygons overlap at this location.
Is there a way (preferably using Shapely) to get the locations where 75% of polygons overlap?
The accepted answer appeared to break down in certain conditions.
I found the problem: the boundaries of the contain shape sometimes overlap the inter polygons. I can replace if geom.contains(polygon): with if geom.contains(polygon.buffer(-1)): and it works exactly as intended.
One approach is to split all geometries to get a flat list of non-intersecting regions on the XY-plane, and then see how many of the original geometries contain each region. Any region that is contained by at least some threshold number of the original geometries can be added to the result. This is easier to explain using a combination of code and illustrations.
To start with, we'll need to fix one issue. The example you put together has a couple invalid geometries, which will cause Shapely to throw an error when attempting to query spatial relationships (e.g., calling contains or intersects). You can check that using the is_valid property, and get a more detailed information by calling explain_validity:
from shapely.geometry import Polygon
from shapely.validation import explain_validity
P2 = Polygon([(-1.5, 3), (-1.1, 3), (-1.5, -1), (-1.1, -1)])
>>> P2.is_valid
False
>>> explain_validity(P2)
'Self-intersection[-1.3 1]'
Basically, it's not happy about shapes like these being expressed as a Polygon when they should be multi-polygons:
So to make your example valid, some of your multi-polygons will have 3 rather than 2 constituent polygons:
P1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
P2 = Polygon([(2.5, 2), (3, 2), (3, 3), (2.5, 3)])
multi_a = MultiPolygon([P1, P2])
P1 = Polygon([(-1, -1), (-1, 2),(2, 2), (2, -1)])
P2 = Polygon([(3, 3), (4, 2), (4, 4), (3, 4)])
multi_b = MultiPolygon([P1,P2])
P1 = Polygon([(-2, 4), (2.2, 4),(2.2, -2), (-2, -2)])
P2 = Polygon([(-1.5, 3), (-1.1, 3), (-1.3, 1)])
P3 = Polygon([(-1.5, -1), (-1.3, 1), (-1.1, -1)])
multi_c = MultiPolygon([P1,P2,P3])
P1 = Polygon([(2.5, -1), (3.2, -1),(3.2, -2), (-2, -2)])
P2 = Polygon([(3, 0), (4, 0), (3.5, 0.5)])
P3 = Polygon([(3.5, 0.5), (3, 1), (4, 1)])
multi_d = MultiPolygon([P1,P2,P3])
Hopefully your real source data has valid geometries (or you have some way of converting them to become valid geometries - which incidentally is an upcoming feature in Shapely 1.8, implemented via make_valid, but it is not yet released), since otherwise the method described below will not work.
With that out of the way, the first step is to get a flat list of non-intersecting regions from your list of shapes. To do this, we start with the original list of intersecting shapes (note darker shading where multiple shapes overlap):
Convert them to lines using linemerge (in combination with unaryunion):
Then polygonize the result:
It might not be clear from the picture, but the idea is none of these geometries intersect - some of these polygons have holes in them (in cases where one shape previously contained another). So this represents the "flat list of non-intersecting regions on the XY-plane" that I was referring to in the beginning.
The code for the process so far looks like this:
from shapely.geometry import Polygon, MultiPolygon
from shapely.ops import linemerge, unary_union, polygonize
# Original shape definitions here (snipped)...
shapes = [multi_a, multi_b, multi_c, multi_d]
lines = unary_union(linemerge([geom.exterior for shape in shapes for geom in shape.geoms]))
polygons = list(polygonize(lines))
Next, we check each resulting region in the polygons list, and check how many shapes from the original list it intersected with. If it's above the threshold (defined here as 0.75 * len(shapes), then we add it to the result:
threshold = 0.75 * len(shapes)
def overlaps(polygon, shape):
for geom in shape.geoms:
if geom.contains(polygon):
return True
return False
result = []
for polygon in polygons:
containing_shapes = []
for shape in shapes:
if overlaps(polygon, shape):
containing_shapes.append(shape)
if len(containing_shapes) >= threshold:
result.append(polygon)
If you're dealing with a large dataset, checking intersections in a nested loop like that can be quite slow (O(N^2)), so you can probably speed it up using an STRtree:
from shapely.strtree import STRtree
# (Previous code here to get the flattened list of polygons...)
tree = STRtree([geom for shape in shapes for geom in shape.geoms])
result = []
for polygon in polygons:
matches = [o.wkt for o in tree.query(polygon) if o.contains(polygon)]
if len(matches) >= threshold:
result.append(polygon)
This question already has an answer here:
Stimulating Liquid Flow on Matrix
(1 answer)
Closed 2 years ago.
Simulate a liquid flow through a randomly created square matrix that contains a set of integers from 0-9 using Python. The liquid should start from the top left corner of the matrix. It could only move towards right or below adjacent matrix. The lower value of the adjacent matrix, the higher potential for it to flow. In the case of the right and below adjacent matrix have the same value, the program should be able to simulate both conditions (Refer attached example image). The movement of the liquid is considered stop at the right or below edge of the matrix. The program should be able to show the sum of all numbers that the liquid has pass through, in all possibilities and visualize the path. Visualization can be done by replacing the numbers lying in the path with asterisk (*) , vertical line/pipe symbol (|), hyphen (-) or other relevant symbols such as (~,>= etc). Other methods to visualize can be accepted. Example output should look like this:
Example output visualization
This is what i have coded so far, but it does not output all the possible outcomes and the sum of integers in which the liquid flows through;
import random
import numpy as np
import copy
a=[]
n=int(input("Enter matrix size between 8 to 20 only= "))
while True:
if n<8:
n=int(input("Input size less than 8! Enter at least 8 or more = "))
elif n>20:
n=int(input("Input size more than 20! Enter at most 20 or lesser = "))
else:
break
print()
print(f'{n} × {n} matrix generated from random numbers of 0-9 :\n')
def create_matrix(n, a):
for i in range (n):
v=[]
for j in range (n):
v.append(random.randint(0,9))
a.append(v)
my.create_matrix(n, a)
b=copy.deepcopy(a)
#c=copy.deepcopy(a)
def print_array(n, array):
for i in range(n):
for j in range(n):
print(array[i][j], end=" ")
print()
print()
my.print_array(n, a)
def move_right(b, i, j, n):
b[0][0]="."
while i+1 < n and j+1<n :
if b[i+1][j] < b[i][j+1]:
b[i+1][j]="."
i+=1
elif b[i][j+1] < b[i+1][j]:
b[i][j+1]="."
j+=1
elif b[i+1][j] == b[i][j+1]:
b[i][j]="*"
#c=copy.deepcopy(b)
#move_bottom(c, i, j, n)
b[i][j+1]="."
j+=1
else:
break
def move_bottom(array,i ,j, n):
array[i][j]="."
alt=0
while i+1 < n and j+1<n :
if array[i+1][j] < array[i][j+1]:
array[i+1][j]="."
i+=1
elif array[i][j+1] < array[i+1][j]:
array[i][j+1]="."
j+=1
elif array[i+1][j] == array[i][j+1]:
array[i][j]="*"
bb=copy.deepcopy(array)
move_right(bb,i,j,n)
array[i+1][j]="."
i+=1
alt+=1
else:
break
print_array(n, array)
my.move_bottom(b, 0, 0, n)
I really need help with my coding so that i can output all the possible outcomes that the liquid can flow and the sum of integers in which the liquid flows through. If there's any other way to easily code this program using python given the conditions, please let me know!
Here is how you could do this for all possible ways.
A map of
456
867
978
would be represented as dictionary of dictionaries:
{ (0,0): {(1,0): {(2,0): {},
(1,1): { (2,1): {},
(1,2): {}}}}
and can be used to generate all paths from it.
Then you need a copy of the original numbers and can add the ascii art instead of the numbers at the correct positions. The total map has to check if you add another way to a formerly already set position, and if so replace it with a "both-ways" marker.
Some utility-methods:
import random
import copy
random.seed(42) # fixed for repeatability
class Consts:
"""Some constants to use throughout"""
VALUES = range(10)
SIZE = 8
START = (0,0)
OUT_OF_BOUNDS = 99
def generate():
"""Generates an Consts.SIZE * Consts.SIZE list of lists of numbers 0-9"""
n = Consts.SIZE
data = random.choices(Consts.VALUES, k = n*n)
return [data[i * n : i * n + n] for i in range(n)]
def v(data, x, y):
"""Returns the value in data at position x,y or
Consts.OUT_OF_BOUNDS if out of bounds."""
try:
return data[y][x]
except:
return Consts.OUT_OF_BOUNDS
def peek_east(data, pos):
"""Returs the value, position tuple of the position one to the east"""
new_pos = pos[0] + 1, pos[1]
return v(data, *new_pos), new_pos
def peek_south(data, pos):
"""Returs the value, position tuple of the position one to the south"""
new_pos = pos[0], pos[1] + 1
return v(data, *new_pos), new_pos
def done(pos):
"""Returns True if a position is at the border of the map"""
return Consts.SIZE-1 in pos
def pp(arr):
"""Pretty print a map / list of lists"""
print('\n'.join(''.join(map(str, n)) for n in arr))
the exploration part:
def explore(data, start=None, ways=None):
"""Creates/Explores all the ways. The exploration position are stored
as dictionary that contains all explored tiles as a dict of dict of ...
of possible ways to go through the map."""
size = Consts.SIZE
OUT = Consts.OUT_OF_BOUNDS
start = start or Consts.START
ways = ways or {}
pos = start
if done(pos):
ways[pos] = "DONE"
return
routes = []
# get east and south data to see where we can go from here
east, east_pos = peek_east(data, pos)
south, south_pos = peek_south(data, pos)
# where to move to
if east <= south:
routes.append(east_pos)
if east >= south:
routes.append(south_pos)
# add the visited tiles and the empty dicts for them to ways
for way in routes:
if pos not in ways:
ways[pos] = {}
if way not in ways:
ways[way] = {}
ways[pos][way] = ways[way]
# explore further
for way in routes:
explore(data, way, ways)
# simplify dict, only return the (0,0) element
return {Consts.START: ways[Consts.START]}
How to use it & ascii art:
array = generate()
pp(array)
exp = explore(array)
# used to create all possible paths from the dict we generated
def get_paths(tree, cur=()):
""" Source: https://stackoverflow.com/a/11570745/7505395 """
if not tree or tree == "DONE":
yield cur
else:
for n, s in tree.items():
for path in get_paths(s, cur+(n,)):
yield path
p = list(get_paths(exp))
# copy the original map for a "all in one" map
d_all = copy.deepcopy(array)
for path in p:
# copy the original map for this paths map
d = copy.deepcopy(array)
# get from,to pairs from this runway
for (_from, _to) in zip(path, path[1:]):
_, pe = peek_east(array, _from)
_, ps = peek_south(array, _from)
# ASCII Art the map
if _to == pe:
d[_from[1]][_from[0]] = "-"
d_all[_from[1]][_from[0]] = "-" if isinstance(d_all[_from[1]][_from[0]], int) else ("-" if d_all[_from[1]][_from[0]] == "-" else "+")
else:
d[_from[1]][_from[0]] = "|"
d_all[_from[1]][_from[0]] = "|" if isinstance(d_all[_from[1]][_from[0]], int) else ("|" if d_all[_from[1]][_from[0]] == "|" else "+")
# ASCII Art the last one
d[_to[1]][_to[0]] = "°"
d_all[_to[1]][_to[0]] = "°"
# print this map
print("\nPath: ", path)
pp(d)
# and total map
print("\nTotal mapping")
pp(d_all)
Output:
60227680
40250165
25808631
93008687
59358685
70220212
63322966
17139656
Path: ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1),
(6, 2), (7, 2))
-|227680
4-----|5
258086-°
93008687
59358685
70220212
63322966
17139656
Path: ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (5, 2),
(6, 2), (7, 2))
-|227680
4----|65
25808--°
93008687
59358685
70220212
63322966
17139656
Path: ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3), (3, 4),
(3, 5), (4, 5), (5, 5), (6, 5), (7, 5))
-|227680
4--|0165
258|8631
930|8687
593|8685
702----°
63322966
17139656
Path: ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3), (3, 4),
(3, 5), (4, 5), (4, 6), (5, 6), (6, 6), (6, 7))
-|227680
4--|0165
258|8631
930|8687
593|8685
702-|212
6332--|6
171396°6
Path: ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3), (3, 4),
(3, 5), (4, 5), (4, 6), (5, 6), (5, 7))
-|227680
4--|0165
258|8631
930|8687
593|8685
702-|212
6332-|66
17139°56
Path: ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3), (3, 4),
(3, 5), (4, 5), (4, 6), (4, 7))
-|227680
4--|0165
258|8631
930|8687
593|8685
702-|212
6332|966
1713°656
Total mapping
-|227680
4--+-+|5
258|8--°
930|8687
593|8685
702-+--°
6332++|6
1713°°°6
Starting at (0,0) in the plane, given a positive integer n, I want to generate all paths consisting of n-1 steps away from (0,0). A step can be either one step to the right or one step up. For example, if n=4, then a path would be (0,0), (1,0), (1,1), (1,2). I'm currently using python
I've tried letting some parameter count the number of steps I'm taking and then using a while loop to restrict the number of steps, and for looping through my starting array [[[0,0]]].
def f(n):
A=[[[0,0]]]
s=0
while (int(s+1)<int(n)):
for i in A:
i.append([i[-1][0]+1,i[-1][1]])
A.append(i+[i[-1][0],i[-1][1]+1])
s+=1
return A
print f(2)
I'm getting an error 'int' object has no attribute 'getitem' on line 8. I also have a feeling that there are various other problems with the above code but am not too sure the best way to go about this
Welcome to Stackoverflow. This problem is ideally suited to recursive techniques.
If you have a path of length N at point P = (x, y) then you know it forms two
possible paths of length N+1, one to point (x+1, y) and one to point (x, y+1).
The only other thing you know is that there is one path of length zero at the starting point. Given that, you can compute the paths of length 1, the paths of length 2, and
so on. To separate the logic of path generation from the business of consuming the
paths I'd suggest using a generator function, allowing your logic to yield a
new path whenever is finds one. You can then iterate over this generator
to enumerate the paths. This appears to work:
def paths(in_path, N=1):
if N==1:
yield in_path
else:
x, y = in_path[-1]
yield from paths(in_path+[(x+1, y)], N-1)
yield from paths(in_path+[(x, y+1)], N-1)
for path in paths([(0, 0)], 4):
print(path)
The output I see is
[(0, 0), (1, 0), (2, 0), (3, 0)]
[(0, 0), (1, 0), (2, 0), (2, 1)]
[(0, 0), (1, 0), (1, 1), (2, 1)]
[(0, 0), (1, 0), (1, 1), (1, 2)]
[(0, 0), (0, 1), (1, 1), (2, 1)]
[(0, 0), (0, 1), (1, 1), (1, 2)]
[(0, 0), (0, 1), (0, 2), (1, 2)]
[(0, 0), (0, 1), (0, 2), (0, 3)]
which, gratifyingly, appears to include the example you gave.