How to separate elements of tuples into occurrences of pairs in Python? - python

I have a tuple that looks like:
t=(('a','b'),('a','c','d','e'),('c','d','e'))
I need to rearrange it so I have a new tuple that will look like:
t2=(('a','b'),('a','c'),('c','d'),('d','e'),('c','d'),('d','e'))
Basically the new tuple takes pairs (of 2) from each element of the old tuple. But I am not sure how to get started. Thanks for your help.

Use a generator expression with zip to pair and convert to a tuple at the end:
>>> t = (('a','b'),('a','c','d','e'),('c','d','e'))
>>> tuple((x) for tupl in t for x in zip(tupl, tupl[1:]))
(('a', 'b'), ('a', 'c'), ('c', 'd'), ('d', 'e'), ('c', 'd'), ('d', 'e'))

Try this out :
tuple([(t[i][j],t[i][j+1]) for i in range(len(t)) for j in range(len(t[i])-1)])
#[('a', 'b'), ('a', 'c'), ('c', 'd'), ('d', 'e'), ('c', 'd'), ('d', 'e')]
You can also try another way. If the problem is reduced to do this for one tuple alone :
def pairs(my_tuple):
return [(my_tuple[i],my_tuple[i+1]) for i in range(len(my_tuple)-1)]
Then this can be mapped for all the tuples
tuple(sum(list(map(pairs,t)),[]))
#(('a', 'b'), ('a', 'c'), ('c', 'd'), ('d', 'e'), ('c', 'd'), ('d', 'e'))
Explanation :
map(pairs,t) : maps the function pairs for every element in tuple t
list(map(pairs,t)) : output of the above
But as a nested list
[[[('a', 'b')], [('a', 'c'), ('c', 'd'), ('d', 'e')],...]
sum(list(...),[]) : Flattens out this nested list for the desired output

Here's what I came up with really quick
def transform(t):
out = []
for tup in t:
for i in range(0, len(tup) - 1):
out.append((tup[i], tup[i+1]))
return tuple(out)

You can use this easy to understand code:
t = (('a','b'),('a','c','d','e'),('c','d','e'))
t2 = []
for i in t:
for j in range(len(i)-1):
t2.append((i[j], i[j+1]))
t2 = tuple(t2)
Obviously it isn't very optimized like other answers but for an easy understanding it will be perfect.
That is something equivalent to:
t2 = tuple((i[j], i[j+1]) for i in t for j in range(len(i)-1))
That is a generator expression, something quite similar to list comprehension (it use brackets instead of square brackets) and they basically do similar things, or at least in basic codes like this one. I still don't understand very well their differences but the generators are one-time fasters while the list comprehension are slower but reusable...
Nevermind: the generator means:
t2 = tuple(...) # Make with the result array a tuple, otherwise it will be a list.
for i in t # Iterate over each item of t, they will by called i.
for i in t for j in range(len(i)) # Iterate over each item of t --called--> i and then iterate over the range(len(i)) --called--> j.
(i[j], i[j+1]) for i in t for j in range(len(i)) # The same as before but each time we get a new j (each time the second loop iterate) do --> (i[j], i[j+1])
I know, make two generator/list expression/comprehension on the same line is strange. I always look at an answer like this one to remember how to do that.
My old answer was:
t = (('a','b'),('a','c','d','e'),('c','d','e'))
t2 = []
for i in t:
for j in range(len(i)):
if j < len(i) - 1:
t2.append((i[j], i[j+1]))
t2 = tuple(t2)
But I notice that adding a -1 to the len() of the loop I can avoid that line, because I won't never get an out of index error.

Related

PYTHON: how to make inner function return one value at a time and depending on outer function's conditional decide whether to run or stop?

I'm trying to come up with a way to write efficient permutations for a problem I'm trying to solve. I've looked all through both NumPy's and SymPy's documentation and can't find anything that helps with what I know.
What I need: I want to create a selective permutation function that, rather than returning all possible permutations, simply returns a single permutation, one by one.
General idea of the process: outer function calls my selective permutation function and this function returns ONE value at a time for the outer function to evaluate. If it passes the outer function's conditional statement then we are done, otherwise, it computes the inner function again for the next value and so on.
I've read on implementing yield instead but I'm not sure how to do it in this case.
If anyone has any advice on how to return one value at a time and depending on outer conditional decide whether to run or stop I'd really appreciate it.
Python's itertools.permutations already does this -
from itertools import permutations
for p in permutations("abcd", 2):
print(p)
if p == ("b","c"):
break
print("done")
('a', 'b')
('a', 'c')
('a', 'd')
('b', 'a')
('b', 'c')
done
This is because itertools.permutations returns a sort of lazy generator rather than computing all permutations up front.
print(permutations("abcd", 2))
# <itertools.permutations object at 0x7f26ebd0bf40>
If you want all permutations, you would have to collect them in a list -
print(list(permutations("abcd", 2)))
# [('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'a'), ('b', 'c'), ('b', 'd'), ('c', 'a'), ('c', 'b'), ('c', 'd'), ('d', 'a'), ('d', 'b'), ('d', 'c')]
Here's what that might look like in a program
def solve (values, size, check):
for p in permutations(values, size):
if (check(p)):
return p
answer = \
solve \
( [ 5, 3, 9, -4, 7 ] # nums to check
, 3 # permutation size
, lambda p: sum(p) == 10 # find permutation that adds to 10
)
print(answer)
# (5, 9, -4)
And the answer checks out: 5 + 9 + -4 is equal to 10!
This should be fairly straight forward to do:
def innerFunction(your_inputs):
#Generate Permutations & combinations
for permutation in all_permutations:
yield permutation
def OuterFunction():
for permutation in innerFunction():
if condition:
break;

Convert list of tuples such that [(a,b,c)] converts to [(a,b),(a,c)]

Thoughts on how I would do this? I want the first value in the tuple to pair with each successive value. This way each resulting tuple would be a pair starting with the first value.
I need to do this: [(a,b,c)] --> [(a,b),(a,c)]
You can try this.
(t,)=[('a','b','c')]
[(t[0],i) for i in t[1:]]
# [('a', 'b'), ('a', 'c')]
Using itertools.product
it=iter(('a','b','c'))
list(itertools.product(next(it),it))
# [('a', 'b'), ('a', 'c')]
Using itertools.repeat
it=iter(('a','b','c'))
list(zip(itertools.repeat(next(it)),it))
# [('a', 'b'), ('a', 'c')]
a = [('a','b','c')]
a = a[0]
a = [tuple([a[0], a[index]]) for index in range(1, len(a))]
Try this !
A solution that uses itertools's combinations module.
from itertools import combinations
arr = (['a','b','c'])
for i in list(combinations(arr, 2)):
if(i[0]==arr[0]):
print(i ,end = " ")
This would give a solution ('a', 'b') ('a', 'c')
You can just append pairs of tuples to a list:
original = [(1,2,3)]
def makePairs(lis):
ret = []
for t in lis:
ret.append((t[0],t[1]))
ret.append((t[0],t[2]))
return ret
print(makePairs(original))
Output:
[(1, 2), (1, 3)]
If your tuples are arbitrary length you can write a simple generator:
def make_pairs(iterable):
iterator = iter(iterable)
first = next(iterator)
for item in iterator:
yield first, item
example result:
my_tuple = ('a', 'b', 'c', 'd')
list(make_pairs(my_tuple))
Out[170]: [('a', 'b'), ('a', 'c'), ('a', 'd')]
This is a memory-efficient solution.

Python: Filter in tuples

I have two lists of tuples. I want a new list with every member of l2 and every member of l1 that does not begin with the same element from l2.
I used a for loop and my output is ok.
My question is: How can I use the filter function or a list comprehension?
def ov(l1, l2):
l3=l1.copy()
for i in l2:
for j in l1:
if i[0]==j[0]:
l3.pop(l3.index(j))
print (l3+l2)
ov([('c','d'),('c','e'),('a','b'),('a', 'd')], [('a','c'),('b','d')])
The output is:
[('c', 'd'), ('c', 'e'), ('a', 'c'), ('b', 'd')]
If I understand correctly, this should be the straight forward solution:
>>> l1 = [('c','d'),('c','e'),('a','b'),('a', 'd')]
>>> l2 = [('a','c'),('b','d')]
>>>
>>> starters = set(x for x, _ in l2)
>>> [(x, y) for x, y in l1 if x not in starters] + l2
[('c', 'd'), ('c', 'e'), ('a', 'c'), ('b', 'd')]
This can be generalized to work with longer tuples with extended iterable unpacking.
>>> starters = set(head for head, *_ in l2)
>>> [(head, *tail) for head, *tail in l1 if head not in starters] + l2
[('c', 'd'), ('c', 'e'), ('a', 'c'), ('b', 'd')]
Here is an approach using filter:
from operator import itemgetter
f = itemgetter(0)
zval = set(map(itemgetter(0), l2))
list(filter(lambda tup: f(tup) not in zval, l1)) + l2
[('c', 'd'), ('c', 'e'), ('a', 'c'), ('b', 'd')]
Or:
def parser(tup):
return f(tup) not in zval
list(filter(parser, l1)) + l2
[('c', 'd'), ('c', 'e'), ('a', 'c'), ('b', 'd')]
Filter is a function which returns a list for all True returns of a function, being used as filter(function(), iterator).
def compare(one, two):
for i in two:
if i[0]==one[0]:
print("yes:", one,two)
return False
return True
l1 = [('c','d'),('c','e'),('a','b'),('a', 'd')]
l2 = [('a','c'),('b','d')]
one_liner = lambda n: compare(l1[n], l2) # where n is the tuple in the first list
lets_filter = list(filter(one_liner, range(len(l1))))
final_list = l2.copy()
for i in lets_filter:
final_list.append(l1[i])
print(final_list)
I made this as a way to do it. Lambda might be a bit confusing, alert if you don't understand it, and I'll remake it.
List comprehension is a "ternary operator", if you're familiar with those, in order to make a list in a one-liner.
l1 = [('c','d'),('c','e'),('a','b'),('a', 'd')]
l2 = [('a','c'),('b','d')]
l3 = [l1[n] for n in range(len(l1)) if l1[n][0] not in [l2[i][0] for i in range(len(l2))]]+l2
print(l3)
This code does the trick, but is overwhelming at first. Let me explain what it does.
l1[n] for n in range(len(l1) goes through all the pairs in l1, in order to see if we can add them. This is done when the if returns True.
l1[n][0] not in takes the first item, and returns True if doesn't exist in any of the elements of the following list.
[l2[i][0] for i in range(len(l2))] makes a list from all the first elements of l2.
+l2 is added, as requested.
As a bonus, I'm going to explain how to use else in the same scenario, in case you wanted another result.
l1 = [('c','d'),('a','b'),('c','e'),('a', 'd')]
l2 = [('a','c'),('b','d')]
l3 = [l1[n] if l1[n][0] not in [l2[i][0] for i in range(len(l2))] else ("not", "you") for n in range(len(l1))]+l2
print(l3)
As you can see, I had to switch the order of the operators, but works as it should, adding them in the correct order of l1 (which I changed for the sake of showing).

Python - strategy to construct triplet tuples from pair tuples

I don't think the title does a great job acting as a high level explanation of the problem, but I do think this is an interesting problem to try to solve:
Given a python list of tuples of length 2:
pairs = [('G', 'H'), ('C', 'D'), ('B', 'D'), ('A', 'B'), ('B', 'C')]
I would like to create a new list containing tuples of length 3, on the condition that the tuple ('X', 'Y', 'Z') is created only if the pairs ('X', 'Y'), ('Y', 'Z'), and ('X', 'Z') all appear as tuples in the pairs list. In the case of my pairs list, only the triplet ('B', 'C', 'D') would be created (preferably alphabetically).
I haven't used python in several months, so am a bit rusty and would prefer to solve this using mostly base python packages, but open to any suggestions. Thanks in advance for any help!
I'd use itertools to check if all the pairs exist.
from itertools import combinations
doubles = [('G', 'H'), ('C', 'D'), ('B', 'D'), ('A', 'B'), ('B', 'C')]
keys = set([x for double in doubles for x in double])
options = combinations(keys, 3)
triples = list()
for option in options:
x, y, z = sorted(option)
first, second, third = (x, y), (x, z), (y, z)
if first in doubles and second in doubles and third in doubles:
triples.append(option)
This assumes that all the tuples in your list are already sorted though.
vals = set([i for (i, j) in pairs] + [j for (i, j) in pairs])
triples = [(i, j, k) for i in vals
for j in vals
for k in vals
if (((i, j) in pairs) and
((j, k) in pairs) and
((i, k) in pairs))]
Now, this only works if the order of the tuples matter. If not, you'd want to include the reverse-order tuples in pairs as well

Checking that the geometry for a triangle is contained in a list of lines

I have a list of lines Lines=([('B', 'C'), ('D', 'A'), ('D', 'C'), ('A', 'B'), ('D', 'B')]) and geometry = ('B', 'C', 'D') is a list of points that set up the triangle (B,C,D).
I want to check whether geometry can be set up from list of lines in Lines. How can I create a function to check that status? True or False.
Sample Functionality with input Lines:
>> Lines=([('B', 'C'), ('D', 'A'), ('D', 'C'), ('A', 'B'), ('D', 'B'),])
>> geometry1 = ('B', 'C', 'D')
>> check_geometry(Lines, geometry1)
True
>> geometry2 = ('A', 'B', 'E')
>> check_geometry(Lines, geometry2)
False
This is my code, but the result is wrong:
import itertools
def check_geometry(line, geometry):
dataE = [set(x) for x in itertools.combinations(geometry, 2)]
for data in dataE:
if data not in line:
return False
return True
Lines = [('B', 'C'), ('D', 'A'), ('D', 'C'), ('A', 'B'), ('D', 'B'),]
geometry1 = ('B', 'C', 'D')
print check_geometry(Lines, geometry1)
Output:
False
For triangles:
You could use the built-in all to do this, making sure to first sort the list contents since their order might differ than that generated from itertools.combinations:
sLines = [tuple(sorted(l)) for l in Lines]
dataE = itertools.combinations('BCD', 2)
Now you can call all which will check that every value in dataE is present in sLines:
all(l1 in sLines for l1 in dataE)
Which will return True.
So, your check_geometry function could look something like:
def check_geometry(line, geometry):
sLines = [tuple(sorted(l)) for l in line]
dataE = itertools.combinations(geometry, 2)
return all(l1 in sLines for l1 in dataE)
Calls made will now check if the Lines contain the geometry:
check_geometry(Lines, 'BCD')
# returns True
check_geometry(Lines, 'ABE')
# returns False
A bit more general:
To generalize this a bit, we can drop itertools.combinations and instead utilize zip. The following makes some appropriate changes to the function in order to acommodate zip but performs similar stuff:
def check_geometry(line, geometry):
sLines = [sorted(l) for l in line]
dataE = [sorted(x) for x in zip(geometry, geometry[1:] + geometry[:1])]
return all(l1 in sLines for l1 in dataE)
The key difference here is:
dataE is now a list of lists containing the result of zip(geometry, geometry[1:] + geometry[:1]). What zip does in this case is it takes a string like "BCDA" and the same string with the first element added to the end geometry[1:] + geometry[:1] (i.e "CDAB") and creates entries signifying the sides of a shape:
>>> s = "BCDA"
>>> s[1:] + s[:1]
>>> 'CDAB'
>>> list(zip(s, s[1:] + s[:1]))
[('B', 'C'), ('C', 'D'), ('D', 'A'), ('A', 'B')]
Now we can check that a geometry with points "BCDA" can be constructed by the lines in Lines:
check_geometry(Lines, "BCD")
# True
check_geometry(Lines, "BCDA")
# True
check_geometry(Lines, "BCDF")
# False
Note 1: Lines can be written as:
Lines=[('B', 'C'), ('D', 'A'), ('D', 'C'), ('A', 'B'), ('D', 'B')]
The parenthesis () and comma , have no additional effect here, you can drop them :-) .
Note 2: The geometry parameter for check_geometry can be any iterable (tuples, lists, strings):
check_geometry(lines, "BCD") == check_geometry(lines, ('B', 'C', 'D'))
Creating and passing a tuple to it seems somewhat odd in this case (alas, you might have a good reason to do so). Unless reasons require it, I would suggest going with strings as the value for parameter geometry.
I think A,B,C can be string or whatever which define a point that set up a line
Okay, I'll be using strings for my answer then, you should be able to adjust the code to your needs.
def check_for_triangle(tri, lines):
lines_needed = zip(tri, (tri[1], tri[2], tri[0]))
return all(line in lines or line[::-1] in lines for line in lines_needed)
lines=[('B', 'C'), ('D', 'A'), ('D', 'C'), ('A', 'B'), ('D', 'B')]
tri1 = ('B', 'C', 'D')
tri2 = ('A', 'B', 'E')
print(check_for_triangle(tri1, lines)) # True
print(check_for_triangle(tri2, lines)) # False
The idea is to generate all lines (represented by a pair of points) we need to find in lines for a given triangle with zip. After that, we check whether all these lines can be found in lines.
Checking for line[::-1] as well is needed because the line ('A', 'B') is the same line as ('B', 'A').

Categories

Resources