Remove duplicate unordered tuples from list - python

In a list of tuples, I want to have just one copy of a tuple where it may be (x, y) or (y, x).
So, in:
# pairs = list(itertools.product(range(3), range(3)))
pairs = [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
the result should be:
result = [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)] # updated pairs
This list of tuples is generated using itertools.product() but I want to remove the duplicates.
My working solution:
pairs = [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
result = []
for pair in pairs:
a, b = pair
# reordering in increasing order
temp = (a, b) if a < b else (b, a)
result.append(temp)
print(list(set(result))) # I could use sorted() but the order doesn't matter
How can this be improved?

You could use combinations_with_replacement
The code for combinations_with_replacement() can be also expressed as a subsequence of product() after filtering entries where the elements are not in sorted order (according to their position in the input pool)
import itertools
pairs = list(itertools.combinations_with_replacement(range(3), 2))
print(pairs)
>>> [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)]

edit I just realized, your solution matches my solution. What you are doing is just fine. If you need to do this for a very large list, then there are some other options you may want to look into, like a key value store.
If you need to remove dupes more programatically, then you can use a function like this:
def set_reduce(pairs):
new_pairs = set([])
for x,y in pairs:
if x < y:
new_pairs.add((x,y))
else:
new_pairs.add((y,x))
return new_pairs
running this results in
>>>set_reduce(pairs)
set([(0, 1), (1, 2), (0, 0), (0, 2), (2, 2), (1, 1)])

This is one solution which relies on sparse matrices. This works for the following reasons:
An entry in a matrix cannot contain two values. Therefore, uniqueness is guaranteed.
Selecting the upper triangle ensures that (0, 1) is preferred above (1, 0), and inclusion of both is not possible.
import numpy as np
from scipy.sparse import csr_matrix, triu
lst = [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1),
(1, 2), (2, 0), (2, 1), (2, 2)]
# get row coords & col coords
d1, d2 = list(zip(*lst))
# set up sparse matrix inputs
row, col, data = np.array(d1), np.array(d2), np.array([1]*len(lst))
# get upper triangle of matrix including diagonal
m = triu(csr_matrix((data, (row, col))), 0)
# output coordinates
result = list(zip(*(m.row, m.col)))
# [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)]

Related

How to handle return from recursion

I'm trying to make a polyomino generator of level N. I successfully made a function that connects a tile with a root in every possible way and returns all combinations. Now I need to extend this to level N. I've done my best, but still can't handle recursion the right way.
Here's my function:
def connect_n(tiles,root,n=1):
if not isinstance(tiles[0], list): tiles = [tiles]
result = []
if n == 1:
for tile in tiles:
result += connect(tile, root)
return result
else:
return connect_n(tiles, root,n-1)
This function successfully creates N nested functions and executes base case at n==1. But then with obtained result, it just goes up and up and exits with that result without any other iterations. I'm sure I'm missing something. I tried to move conditions and loops around without success.
I have following input:
root = (0,0)
N = 3 #for example, can be any number > 0
Function connect(root,root) returns:
[[(0, 0), (1, 0)]]
Then functionconnect([[(0, 0), (1, 0)]],root) returns
[[(0, 0), (1, 0), (2, 0), (3, 0)],
[(0, 0), (0, 1), (1, 0), (2, 0)],
[(0, 0), (0, 1), (1, 0), (1, 1)],
[(0, 0), (1, 0), (1, 1), (2, 1)]]
And so on.
Function connect_n output should be
[[(0, 0), (1, 0)]] for N=1
[[(0, 0), (1, 0), (2, 0), (3, 0)],
[(0, 0), (0, 1), (1, 0), (2, 0)],
[(0, 0), (0, 1), (1, 0), (1, 1)],
[(0, 0), (1, 0), (1, 1), (2, 1)]] for N=2
And so on.
I'm not sure I understand the algorithm, but I think this is what you want:
def connect_n(tiles, root, n=1):
if n == 1:
return connect(tiles, root)
else:
return connect_n(connect_n(tiles, root, n-1)), root)

Indices of all values in an array

I have a matrix A. I would like to generate the indices of all the values in this matrix.
A=np.array([[1,2,3],[4,5,6],[7,8,9]])
The desired output should look like:
[(0,0),(0,1),(0,2),(1,0),(1,1),(2,1),(2,0),(2,1),(2,2)]
You can use:
from itertools import product
list(product(*map(range, A.shape)))
This outputs:
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
Explanation:
A.shape gives the dimensions of the array. For each dimension, we create a range() that generates all of the numbers between 0 and the length of a given dimension. We use map() to perform this for each dimension of the array. Finally, we unpack all of these ranges into the arguments of itertools.product() to create the Cartesian product among all these ranges.
Notably, the use of list unpacking and map() means that this approach can handle ndarrays with an arbitrary number of dimensions. At the time of posting this answer, all of the other answers cannot be immediately extended to a non-2D array.
This should work.
indices = []
for i in range(len(A)):
for j in range(len(A[i])):
indices.append((i,j))
Heres a way of doing by using itertools combinations
from itertools import combinations
sorted(set(combinations(tuple(range(A.shape[0])) * 2, 2)))
combinations chooses two elements from the list and pairs them, which results in duplication, so converting it to set to remove duplications and then sorting it.
This line of list comprehension works. It probably isn't as fast as using itertools, but it does work.
[(i,j) for i in range(len(A)) for j in range(len(A[i]))]
Using numpy only you can take advantage of ndindex
list(np.ndindex(A.shape))
or unravel_index:
list(zip(*np.unravel_index(np.arange(A.size), A.shape)))
Output:
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
NB. The second option enables you to pass a order='C' (row-major) or order='F' (column-major) parameter to get a different order of the coordinates
Example on A = np.array([[1,2,3],[4,5,6]])
order='C' (default):
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
order='F':
[(0, 0), (1, 0), (0, 1), (1, 1), (0, 2), (1, 2)]

Loop through list of tuples and unpack elements of each tuple

I have this list of two-value tuples
stake_odds=[(0, 1), (0, 2), (0, 5), (0, 10), (2, 1), (2, 2), **(2, 5)**, (2, 10), (5, 1), (5, 2), (5, 5), (5, 10), (10, 1), (10, 2), (10, 5), (10, 10)]
I have the following function where I want to put the tuple into an object method where it calculates the product (or minus product depending on the instance) of the two numbers in the tuple. If the product is positive, I want to append the tuple used to another list, pos_cases.
def function():
pos_cases=[]
x,y=stake_odds[9]
b1=bet1.payout(x,y)
if b1 > 0:
return b1, "b is greater than zero!"
pos_cases.append(stake_odds[9])
print(pos_cases)
print(function())
As you can see below I have to unpack the tuple into two variables before computing. I can do it by specifying the element of the list (stake_odds[9]), however I am looking for a way to generalize and loop through the list (stake_odds[i]) rather than going one by one.
The list in this example would be shortened to the following:
pos_cases =[(2, 1), (2, 2), (2, 5), (2, 10), (5, 1), (5, 2), (5, 5), (5, 10), (10, 1), (10, 2), (10, 5), (10, 10)]
How could I do this? The only thing I can think of is some nested for loop like:
for i in stake_odds:
for x,y in i:
return(x,y)
But this results in error >TypeError: cannot unpack non-iterable int object>.
Doesn't this work?:
def function():
pos_cases=[]
for x,y in stake_odds:
b1=bet1.payout(x,y)
if b1 > 0:
return b1, "b is greater than zero!"
pos_cases.append((x,y))
return pos_cases
print(function())

How to construct an array consisting of all paths of a given length in discretised 2D-space

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.

format and Print a list in a file

i have one list with some coordinates in it, when i am printing it like that:
for i in range(0,len(List)):
print("".join(["(%d, %d) \n" % (y[i], y[i+1]) for y in (List)]))
the output is this:
(0, 3)
(0, 2)
(0, 1)
(1, 1)
(1, 2)
(2, 2)
(2, 1)
(3, 1)
(3, 0)
(2, 0)
(1, 0)
(0, 0)
i want to save the output in a .txt, but that is not a problem, my problem is that the .txt must be formmated like this:
(0, 3), (0, 2)
(0, 2), (0, 1)
(0, 1),(1, 1)
(1, 1),(1, 2)
(1, 2),(2, 2)
.....
i've tried many things but nothing worked..
it must be easy, but i am new to python
thank you in advance
This does the trick:
l = [(0, 3), (0, 2), (0, 1), (1, 1), (1, 2), (2, 2)]
for i in range(0, len(l), 2):
print(', '.join([str(l[i]), str(l[i+1])]))
# (0, 3), (0, 2)
# (0, 1), (1, 1)
# (1, 2), (2, 2)
You can use zip.
mylist = [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
# That doesn't have to be the numbers though, you can use your own.
for a, b in zip(mylist, mylist[1:]):
print("{}, {}".format(a, b), file = myfile) # Will print to myfile.
That's assuming that you are printing to an open file. Leave the file argument out if you don't want to print anywhere else, but the default screen.
in myfile.txt:
(0, 0), (0, 1)
(0, 1), (0, 2)
(0, 2), (1, 0)
(1, 0), (1, 1)
(1, 1), (1, 2)
(1, 2), (2, 0)
(2, 0), (2, 1)
(2, 1), (2, 2)
The output is in tuples, not lists.
Well, besides other answers playing with indices, you can also use zip:
for a, b, c, d in zip(List, List[1:], List[2:], List[3:]):
print('({}, {}), ({}, {})'.format(a, b, c, d))
Regarding expected output:
l = [(0, 3), (0, 2), (0, 1), (1, 1), (1, 2), (2, 2)]
for i,j in zip(l,l[1:]):
print str(i) + "," + str(j) # or print ",".join([str(i),str(j)])
#output
(0, 3),(0, 2)
(0, 2),(0, 1)
(0, 1),(1, 1)
(1, 1),(1, 2)
(1, 2),(2, 2)
If you want list chunking with 2 elements,Try this
def chunks(l, n):
for i in xrange(0, len(l), n):
yield l[i:i+n]
for i,j in list(chunks(l,2)):
print ",".join([str(i),str(j)])
#output
(0, 3),(0, 2)
(0, 1),(1, 1)
(1, 2),(2, 2)
Your code:
for i in range(0,len(List)):
print("".join(["(%d, %d) \n" % (y[i], y[i+1]) for y in (List)]))
Turning this into a list comp.
t = ["".join(["(%d, %d) \n" % (y[i], y[i+1]) for y in (List)]) for i in range(0,len(List))]
(equivalent to)
s = []
for i in range(0,len(List)):
s.append("".join(["(%d, %d) \n" % (y[i], y[i+1]) for y in (List)]))
Then:
first = True
other = None
r = ""
for i in t:
if not first:
r += other+", "+i+"\n"
first = True
else:
other = i
first = False
f = open("out.txt","w")
f.write(t)
f.close() #go to notepad

Categories

Resources