Python List Comprehension: assign to multiple variables - python-3.x

Is there a way to assign to multiple variables in a one-liner?
Let's say I have a list of 3D points and I want x, y and z lists.
polygon = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
# this works to get the expected result, but is not a single list comprehension
x = [x for x, y, z in polygon ]
y = [y for x, y, z in polygon ]
z = [z for x, y, z in polygon ]
I am thinking of something like:
x, y, z = [... for x, y, z in polygon ]

You can use zip() function:
polygon = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
x, y, z = zip(*polygon)
print(x)
print(y)
print(z)
Prints:
(0, 1, 1, 0)
(0, 0, 1, 1)
(0, 0, 0, 0)
Or if you want lists instead of tuples:
x, y, z = map(list, zip(*polygon))

Unpack the list of tuples using zip():
polygon = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
x,y,z = zip(*polygon)
print(list(x))
print(list(y))
print(list(z))
OUTPUT:
[0, 1, 1, 0]
[0, 0, 1, 1]
[0, 0, 0, 0]
EDIT:
If you want the lists:
x,y,z = [list(a) for a in zip(*polygon)]

Related

For loop through tuple A and increace tuple Ai given size N to Ni of iterable

I´m trying to iterate trough elements where list N return an iterator, which goes through all possible tuples A, where Ai changes from 0 to Ni. the elements are always and int. A solution or a path to a solution with for loops and while loops is preferred.
def f(*args):
lst = []
for i in range(args[0]):
for x in range(args[1]):
for a in range(args[2]):
for v in range(args[3]):
lst.append((i,x,a,v))
return lst
print(f(5, 3, 1, 5))
This code works, but I don't want it hard coded: let's say if I want to input another int, lets say: print(f(5, 3, 1, 5, 6)) <- it should work for that as well.
So the question is how to make a loop like the one above without the hard coding?
I have tried to use a while loop and a for loop inside the while:
def f(*args):
cnt = 0
lst = []
while len(args) > cnt:
print(cnt)
for i in range(args[cnt]):
lst.append(i)
print(lst)
cnt += 1
return lst
print(f(5, 3, 1, 5))
this is the correct output of the first code snippet and this is what I want without the hard coding:
Another way you could do it is:
def f(*args):
res = [[]]
for z in map(range, args):
res = [tuple(x)+(y,) for x in res for y in z]
return res
f(5,3,1,5)
You can use recursion for this. It replaces the unknown number of layered for loops.
def fr(tt, idx = 0):
lst = []
if idx == len(tt)-1: # last index, just loop
for x in range(tt[idx]):
lst.append(tt[:-1] + (x,))
return lst
for x in range(tt[idx]): # loop and call child loops
l2 = tt[:idx] + (x,) + tt[idx+1:] # update this index
lst.extend(fr(tuple(l2), idx+1)) # iterate next level
return lst
print(fr((5, 3, 1, 5)))
print(fr((5, 3, 1, 5, 6)))
Output
[(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3), (0, 0, 0, 4), ......., (4, 2, 0, 3), (4, 2, 0, 4)]
[(0, 0, 0, 0, 0), (0, 0, 0, 0, 1), (0, 0, 0, 0, 2), (0, 0, 0, 0, 3), ......., (4, 2, 0, 4, 4), (4, 2, 0, 4, 5)]

Algorithm for polygon triangulation

I have written the following code for the generation of all "triangulations" of a regular or convex polygon:
def getTriangles(points,i,j):
print('i={}, j={}'.format(i,j))
ee = []
if j-i<2:
return []
if j-i==2:
return [[[i,i+1,j]]]
for k in range(i+1,j):
print(' k={}'.format(k))
e1= getTriangles(points,i,k)
e2 = getTriangles(points,k,j)
for x in e1:
for y in e2:
e = [[i,k,j]]
e.extend(x)
e.extend(y)
ee.append(e)
if len(e1)==0:
for y in e2:
e = [[i,k,j]]
e.extend(y)
ee.append(e)
if len(e2)==0:
for a in e1:
e = [[i,k,j]]
e.extend(x)
ee.append(e)
print(' e1={}, e2={}, ee={}'.format(e1,e2,ee))
return ee
n=5
tr = getTriangles(range(1,n+1),0,n-1)
print()
print(tr)
print(len(tr))
For n=3,4 it is correct, and in general "navigates" through the right number of possible triangulations (that is the Catalan number) for n=3,4,5,6,7,8, but the triangulations are not unique. here the formatted output for n=5, consisting of a list of triangles (e.g. three vertices in [0,1,2,3,4]):
[[[0, 1, 4], [1, 2, 4], [2, 3, 4]],
[[0, 1, 4], [1, 3, 4], [1, 2, 3]],
[[0, 2, 4], [0, 1, 2], [2, 3, 4]],
[[0, 3, 4], [0, 2, 3], [0, 1, 2]],
[[0, 3, 4], [0, 2, 3], [0, 1, 2]]]
as you can see the last two are equal. Where is the error?
Intuitively the code is more complex than needed.
EDIT As you can see I'm not in bad company for this error: here is Robert Sedgewick, a computer science professor at Princeton University and in the background you see that the n=5 is correct but for n=6 there are double ;-)
The following code seams to work. The change is in the middle. The algorithm fix the edge [i,j] and k moves from i+1 to j-1. The triangle ijk is fixed and split the polygon in three sub-polygons: itself, the polygon i...k, and the polygon, k..j. If k=i+1 or k=j-1, one of the two polygon is degenerate (empty):
def getTriangles(points,i,j):
print('i={}, j={}'.format(i,j))
ee = []
if j-i<2:
return []
if j-i==2:
return [[[i,i+1,j]]]
for k in range(i+1,j): # k is the vertex such that the triangle ikj split the polygon 1j in 2 subspace plus the triangle ikj
print(' k={}'.format(k))
e1= getTriangles(points,i,k)
e2 = getTriangles(points,k,j)
if k==i+1:
for y in e2:
e = [[i,k,j]]
e.extend(y)
ee.append(e)
elif k==j-+1:
for x in e1:
e = [[i,k,j]]
e.extend(x)
ee.append(e)
else:
for x in e1:
for y in e2:
e = [[i,k,j]]
e.extend(x)
e.extend(y)
ee.append(e)
print(' e1={}, e2={}, ee={}'.format(e1,e2,ee))
return ee
n=5
tr = getTriangles(range(1,n+1),0,n-1)
print()
print(tr)
print(len(tr))
Here's a more Pythonic version of the above:
def genTriangles(i, j):
if j - i < 2:
yield []
return
if j - i == 2:
yield [(i, i+1, j)]
return
for k in range(i + 1, j):
for x in genTriangles(i, k):
for y in genTriangles(k, j):
yield x + y + [(i, k, j)]
n = 5
for k, tr in enumerate(genTriangles(0, n - 1), 1):
print(k, tr)
1 [(2, 3, 4), (1, 2, 4), (0, 1, 4)]
2 [(1, 2, 3), (1, 3, 4), (0, 1, 4)]
3 [(0, 1, 2), (2, 3, 4), (0, 2, 4)]
4 [(1, 2, 3), (0, 1, 3), (0, 3, 4)]
5 [(0, 1, 2), (0, 2, 3), (0, 3, 4)]

how to get all coordinates in the rectangle between two coordinates?

say i have a rectangle, and its top-left and bottom-right coordinates are A(0,0) and B(2,3) respectively. is there a method/formula that i can use to get all the coordinates inside this rectangle? I want my output to be like this if the input was these two coordinates:
input: [(0, 0), (2, 3)]
output: [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2), (0, 3,) (1, 3,) (2, 3)]
also, a python 3 implementation would be greatly appreciated, although not necessary.
thanks
EDIT: full story: i'm using python, and at first i thought i could achieve what i want by getting all the values between x1 and x2, y1 and y2. so for example i have x = 0, x = 1, x = 2 and y = 0, y = 1, y = 2, y = 3, but i honestly don't know where to go from there, or if this is correct in the first place. i thought i could get all the coordinates by somehow getting all the coordinates with y = 0 with different x values, then all the coordinates with y = 1... but i can't seem to wrap my head around a way of doing this. any help is appreciated, thanks.
One thing you could do is make a list of all x coordinates inside the rectangle [x1..x2] and all y coordinates inside the rectangle [y1..y2] and then take the Cartesian product of the two lists using itertools:
import itertools
...
input = [(0, 0), (2, 3)]
x_coords = [x for x in range(input[0][0], input[1][0] + 1)]
y_coords = [y for y in range(input[0][1], input[1][1] + 1)]
output = list(itertools.product(x_coords, y_coords))
If you don't want to use itertools to compute the product, you could also easily use a for loop or a list comprehension to do it instead, which is roughly equivalent to what itertools is doing behind the scenes anyway:
output = [(x, y) for x in x_coords for y in y_coords]

How to get list from specific input of numbers (x,y axis)?

Input= 2 2 2 1 2 0 1 0 0 0 0 1
first number is X coordinate in normal XY axis (not list), second Y coordinate, third X and so on; so from this input it will look like:
Y
2 *
1* *
0* * *
0 1 2 X
(first*: 2,2, second*: 2,1, third*:2,0 - going from right side).
I need to get output which would look like:
output=
[[0,0,1],
[1,0,1],
[1,1,1]]
So far I tried this, but don't know how to continue:
inp=[2,2,2,1,2,0,1, 0, 0, 0, 0, 1]
maxx=0
maxy=0
for i in range(1,len(inp),2): #yaxis
if inp[i]>maxy:
maxy=inp[i]
continue
else:
continue
for j in range(0,len(inp),2): #xaxis
if inp[j]>maxx:
maxx=inp[j]
continue
else:
continue
part=[]
for i in range(maxy+1):
part.append([0 for j in range (maxx+1)])
for k in range(0,len(inp),2):
for j in range(1,len(inp),2):
for i in range(len(part)):
part[i][j]=
inp = [2,2,2,1,2,0,1, 0, 0, 0, 0, 1]
tuples = [(inp[i], inp[i+1]) for i in range(0, len(inp), 2)]
print(tuples) # [(2, 2), (2, 1), (2, 0), (1, 0), (0, 0), (0, 1)]
# Define the dimensions of the matrix
max_x_value = max([i[0] for i in tuples])+1
max_y_value = max([i[1] for i in tuples])+1
# Build the matrix - fill all cells with 0 for now
res_matrix = [[0 for _ in range(max_y_value)] for _ in range(max_x_value)]
# Iterate through the tuples and fill the 1's into the matrix
for i in tuples:
res_matrix[i[0]][i[1]]=1
print(res_matrix) # [[1, 1, 0], [1, 0, 0], [1, 1, 1]]
# Rotate the matrix by 90 to get the final answer
res = list(map(list, list(zip(*res_matrix))[::-1]))
print(res) # [[0, 0, 1], [1, 0, 1], [1, 1, 1]]

Find largest value from two lists, check it against its counterpart in the other list, assign it, then do same for 2nd largest value, etc

I have two lists:
a = [3, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
b = [2, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0]
I want to find the maximum value across both lists, and compare it to its counterpart (same index) in the other list. If for example the max value is in list a and its counterpart in list b is smaller, I need to allocate the max value's index to that list's masterlist:
a_master = []
b_master = []
If its counterpart is equal to the max value, do nothing and repeat the process for the next biggest value, but ignore the index that has already been allocated to a master list.
So in this example, the initial max value is 3, and a[0] > b[0] means that we append index 0 to the a_master list. Next, we see that b[2] > a[2] therefore we append index 2 to the b_master list. a[3] = b[3] so we don't allocate anything.
Now repeat the process for the next biggest value: 2. a[2] = 2 but index 2 has already been allocated and thus should be ignored. b[0] = 2 but has also already been allocated. b[1] = 2 > a[1] thus index 1 gets allocated to b_master.
Since all remaining values are equal to their counterparts, we're done and the resulting master lists are:
a_master = [0]
b_master = [2, 1]
Kudos for the most pythonian answer!
What about sorting a beforehand into tupled pairs of (index, element), and comparing it with the second list:
from operator import itemgetter
a = [3, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
b = [2, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0]
sorted_a = sorted(enumerate(a), key=itemgetter(1), reverse=True)
# [(0, 3), (3, 3), (2, 2), (1, 1), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), (9, 0), (10, 0)]
a_master = []
b_master = []
for i, x in sorted_a:
if x > b[i]:
a_master.append(i)
elif x < b[i]:
b_master.append(i)
print(a_master)
print(b_master)
Which Outputs:
[0]
[2, 1]

Resources