Related
I'm very new in Python and coding in general, so this question probably will sound dumb.
I want to append tuples with two elements in listay: if the first element of l2 matches with any first element of listax, then it would be appended as a tuple in listay with its second element.
If it worked my output (print(listay)) would be: ['a',4),('b',2), ('c',1)]. Instead, the output is an empty list. What am I doing wrong?
Also, I am sorry if I am not offering all the information necessary. This is my first question ever about coding in a forum.
import operator
listax= []
listay= []
l1= [('a',3), ('b',3), ('c',3), ('d',2)]
l2= [('a',4),('b',2), ('c',1), ('d',2)]
sl1= sorted(l1, key= lambda t: t[1])
sl2= sorted(l2, key= lambda t: t[1])
tup1l1= sl1[len(sl1)-1]
k1l1= tup1l1[0]
v1l1= tup1l1[1]
tup2l1= sl1[len(sl1)-2]
k2l1= tup2l1[0]
v2l1= tup2l1[1]
tup3l1= sl1[len(sl1)-3]
k3l1= tup3l1[0]
v3l1= tup3l1[1]
tup1l2= sl2[len(sl2)-1]
k1l2= tup1l2[0]
v1l2= tup1l2[1]
tup2l2= sl2[len(sl2)-2]
k2l2= tup2l2[0]
v2l2= tup2l2[1]
tup3l2= sl2[len(sl2)-3]
k3l2= tup3l2[0]
v3l2= tup3l2[1]
listax.append((k2l1, v2l1))
if v2l1== v1l1:
listax.append((k1l1, v1l1))
if v2l1== v3l1:
listax.append((k3l1, v3l1))
for i,n in l2:
if i in listax:
listay.append((i,n))
print(listay)
I'll play the debugger role here, because I'm not sure what are you trying to achieve, but you could do it yourself - try out breakpoint() build-in function and python debugger commands - it helps immensely ;)
Side note - I'm not sure why you import operator, but I assume it's not related to question.
You sort lists by the second element, ascending, python sort is stable, so you get:
sl1 = [('d', 2), ('a', 3), ('b', 3), ('c', 3)]
sl2 = [('c', 1), ('b', 2), ('d', 2), ('a', 4)]
k1l1 = 'c'
v1l1 = 3
k2l1 = 'b'
v2k1 = 3
k3l1 = 'a'
v3l1 = 3
k1l2 = 'a'
v1l2 = 4
k2l2 = 'd'
v2k2 = 2
k3l2 = 'b'
v3l2 = 2
after append
listax = [('b', 3)]
v2l1 == v1l1 is True 3 == 3, so
listax = [('b', 3), ('c', 3)]
v2l1 == v3l1 is True 3 == 3, so
listax = [('b', 3), ('c', 3), ('a', 3)]
I think it gets tricky here:
for i,n in l2:
with
l2 = [('a', 4), ('b', 2), ('c', 1), ('d', 2)]
we get
i = 'a'
n = 4
maybe you wanted enumerate(l2)?
'a' in listax ([('b', 3), ('c', 3), ('a', 3)]) is False
listax doesn't contain an element equal to 'a' - it contains an element, which contains the element 'a'. Maybe that's the mistake?
i = 'b'
n = 3
just like before
nothing interesting happens later ;)
Hope this helps :D
a = (('we', 23), ('b', 2))
b = (('we', 3), ('e', 3), ('b', 4))
#wanted_result = (('we', 3), ('b', 4), ('we', 23), ('b', 2))
How can I receive the tuple that contains the same string in both a and b
like the result I have written below the code?
I would prefer using list comprehensions using filters btw... would that be available?
You can use set intersection:
keys = dict(a).keys() & dict(b)
tuple(t for t in a + b if t[0] in keys)
You can make a set of the intersection between the first part of the tuples in both lists. Then use a list comprehension to extract the tuples that match this common set:
a = (('we', 23), ('b', 2))
b = (('we', 3), ('e', 3), ('b', 4))
common = set(next(zip(*a))) & set(next(zip(*b)))
result = [t for t in a+b if t[0] in common]
[('we', 23), ('b', 2), ('we', 3), ('b', 4)]
You can also do something similar using the Counter class from collections (by filtering tuples on string counts greater than 1:
from collections import Counter
common = Counter(next(zip(*a,*b)))
result = [(s,n) for (s,n) in a+b if common[s]>1]
If you want a single list comprehension, given that your tuples have exactly two values, you can pair each one with a dictionary formed form the other and use the dictionary as a filter mechanism:
result = [t for d,tl in [(dict(b),a),(dict(a),b)] for t in tl if t[0] in d]
Adding two list comprehensions (i.e. concatenating lists):
print([bi for bi in b if any(bi[0]==i[0] for i in a)] +
[ai for ai in a if any(ai[0]==i[0] for i in b)])
# Output: [('we', 3), ('b', 4), ('we', 23), ('b', 2)]
Explanation
[bi for bi in b if any(bi[0]==i[0] for i in a)] # ->>
# Take tuples from b whose first element equals one of the
# first elements of a
[ai for ai in a if ai[0] in [i[0] for i in b]]
# Similarly take tuples from a whose first elements equals one of the
# first elements of b
another variation with sets
filtered_keys=set(k for k,v in a)&set(k for k,v in b)
res=tuple((k, v) for k, v in [*a, *b] if k in filtered_keys)
>>> (('we', 23), ('b', 2), ('we', 3), ('b', 4))
I have two lists of the form:
lst1 = [(1.2, 4), (5, 8), (19, 21), (24.5, 26)]
lst2 = [(1, 3), (6.55, 14.871), (22, 23)]
The output I am looking to get is:
output = [(1.2, 3), (6.55, 8)]
Basically, I want the intersections between the ranges defined by the tuples across the two lists.
You can assume-
the indices to be ordered within a given list. For example, in lst2:
1 < 6.55 < 22
the ranges to be valid (within a tuple, the startVal<=endEndVal). In lst2:
1 < 3 and 6.55 < 14.871 and 22 < 23
What is an efficient way to achieve this?
I think the best way to do this is with a list comprehension, given that both lists are the same length.
in two lists for readability:
# get the min max ranges
a = [(max(i[0], j[0]),min(i[1],j[1])) for i,j in zip(lst1, lst2)]
# check that min is smaller than max
a = [(i,j) for (i,j) in a if i < j]
or in one list:
a = [(i,j) for (i,j) in [(max(i[0], j[0]),min(i[1],j[1])) for i,j in zip(lst1, lst2)] if i < j]
Using itertools and heapq.merge:
lst1 = [(1.2, 4), (5, 8), (19, 21), (24.5, 26)]
lst2 = [(1, 3), (6.55, 14.871), (22, 25)]
from heapq import merge
from itertools import tee, groupby
m1, m2 = tee(merge(lst1, lst2, key=lambda k: k[0]))
next(m2, None)
out = []
for v, g in groupby(zip(m1, m2), lambda k: k[0][1] < k[1][0]):
if not v:
l = [*g][0]
out.append((max(i[0] for i in l), min(i[1] for i in l)))
print(out)
Prints:
[(1.2, 3), (6.55, 8), (24.5, 25)]
A solution using iterators. I use a while loop which stays active until both iterators running on the lists are exausthed.
lst1 = [(1.2, 4), (5, 8), (19, 21), (24.5, 26)]
lst2 = [(1, 3), (6.55, 14.871), (22, 23)]
itr1 = iter(lst1)
itr2 = iter(lst2)
on1 = True
on2 = True
rng1 = next(itr1)
rng2 = next(itr2)
res = []
while on1 or on2:
ll = max(rng1[0], rng2[0])
rr = min(rng1[1], rng2[1])
if ll < rr:
res.append((ll, rr))
if on1 and on2:
if rng1[0] < rng2[0]:
try:
rng1 = next(itr1)
except StopIteration:
on1 = False
else:
try:
rng2 = next(itr2)
except StopIteration:
on2 = False
elif on1:
try:
rng1 = next(itr1)
except StopIteration:
on1 = False
elif on2:
try:
rng2 = next(itr2)
except StopIteration:
on2 = False
if len(res) > 1 and res[-1] == res[-2]:
res.pop(-1)
print(res)
Using your sample input, this prints: [(1.2, 3), (6.55, 8)]
make a simple function to get an intersection range for two range.
def get_intersect(r1, r2):
left = max(r1[0], r2[0])
right = min(r1[1], r2[1])
if left>right:
return None
return (left,right)
and get all intersections though the double loop
for i1 in lst1:
for i2 in lst2:
ia = get_intersect(i1, i2)
if ia!=None:
print(ia)
It would be faster, if you add some conditions in the loop.
Given this list:
[(1, 's'), (2, 'e'), (2, 's'), (3, 'e')]
This is a representation of potentially overlapping intervals, e.g. 1 --> 2 and 2 --> 3, I've brought it into this representation for easier processing (see this answer for context)
I'd like to remove the pair (2, 'e') -- (2, 's') because the end (e) of the one interval is at the same number (2) as start (s) of the next interval. So the result should be
[(1, 's'), (3, 'e')]
And would represent 1 --> 3.
Edit: It's also possible that the intervals are overlapping, e.g. 1-->4 and 2-->3. That would be represented in this list of tuples (Note that the list is already sorted): [(1, 's'), (2, 's'), (3, 'e'), (4, 'e')]. In this case nothing needs to be done as no two tuples share the same number.
I've come up with this reduce:
import functools
functools.reduce(lambda l,i: l[:-1] if i[0] == l[-1][0] and i[1] != l[-1][1] else l + [i], a[1:], [a[0]])
Are there nicer ways to achieve that?
You can use itertools.groupby for a slightly longer (two lines), although more readable solution:
import itertools
def get_combinations(s):
new_data = [(a, list(b)) for a, b in itertools.groupby(s, key=lambda x:x[0])]
return [b[-1] for i, [a, b] in enumerate(new_data) if len(b) == 1 or len(b) > 1 and i == len(new_data) - 1]
print(get_combinations([(1, 's'), (2, 'e'), (2, 's'), (2, 'e')]))
print(get_combinations([(1, 's'), (2, 'e'), (2, 's'), (3, 'e')]))
Output:
[(1, 's'), (2, 'e')]
[(1, 's'), (3, 'e')]
I've been toying with functional languages a lot lately, so this may read less Pythonic than some, but I would use a (modified) itertools's pairwise recipe to iterate through by pairs
def pairwise(iterable):
a, b = itertools.tee(iterable)
next(b, None) # advance the second iterator
return itertools.zip_longest(a, b, fillvalue=(None, None))
then filter by which pairs don't match each other:
def my_filter(a, b):
a_idx, a_type = a
b_idx, b_type = b
if a_idx == b_idx and a_type == "e" and b_type == "s":
return False
return True
Filter them yourself (because a naive filter will allow the "start" value to live since it pairs with the element ahead of it)
def filter_them(some_list):
pairs = pairwise(some_list)
acc = []
while True:
try:
a, b = next(pairs)
if my_filter(a, b):
acc.append(a)
else:
next(pairs) # skip the next pair
except StopIteration:
break
return acc
I was tinkering about a "double continue" approach, and came up with this generator solution:
def remove_adjacent(l):
iterator = enumerate(l[:-1])
for i, el in iterator:
if el[0] == l[i+1][0] and el[1] != l[i+1][1]:
next(iterator)
continue
yield el
yield l[-1]
l1=[(2,1),(3,2),(4,5)]
l2=[(2,3),(3,6),(11,3)]
I need:
result=[(2,4),(3,8),(4,5),(11,3)]
using for loop I was able to add tuples having the same first element.
>>> result = []
l1 = [(2, 1), (3, 2), (4, 5)]
l2 = [(2, 3), (3, 6), (11, 3)]
for x, y in l1:
for p, q in l2:
if x == p:
result.append((x, (y + q)))
>>> result
[(2, 4), (3, 8)]
How do I further add (4,5),(11,3)
So many ways to achieve the same goal... I like it!
Here is my solution.
l1=[(2,1),(3,2),(4,5)]
l2=[(2,3),(3,6),(11,3)]
d1 = dict(l1)
d2 = dict(l2)
result = []
for k in d1:
if k in d2:
result.append((k,d1[k]+d2[k]))
else:
result.append((k,d1[k]))
for k in d2:
if k not in d1:
result.append((k,d2[k]))
>>>print result
[(2, 4), (3, 8), (4, 5), (11, 3)]
There's no need to go through the elements of l2 for all elements of l1 if I understand what you're going for. Just use zip (or izip_longest if the lists have an uneven size) to use a tuple from each list and then append the first item and the sum if the first item matches and extend both tuples if they don't:
for tup1, tup2 in zip(l1, l2):
if tup1[0] == tup2[0]:
result.append((tup1[0], (tup1[1] + tup2[1])))
else:
result.extend([tup1, tup2])
With your input, this returns the required output:
>>> result
... [(2, 4), (3, 8), (4, 5), (11, 3)]
It can be easily done by using dict formed with l1 and iterating over l2, see working code snippet below:
l1=[(2,1),(3,2),(4,5)]
l2=[(2,3),(3,6),(11,3)]
result = dict(l1)
for y in l2:
result[y[0]] = result.get(y[0],0) + y[1]
result = result.items()
print result #[(11, 3), (2, 4), (3, 8), (4, 5)]
Or simply build results by iterating over concatenation of l1 and l2:
l1=[(2,1),(3,2),(4,5)]
l2=[(2,3),(3,6),(11,3)]
result = {}
for item in l1 + l2:
result[item[0]] = result.get(item[0],0) + item[1]
Good luck!
Seems natural to use a dictionary to keep track of which keys are already used. You can reduce the code size by using defaultdict:
import collections
l1=[(2,1),(3,2),(4,5)]
l2=[(2,3),(3,6),(11,3)]
d = collections.defaultdict(int)
for x,y in l1 + l2:
d[x] += y
print sorted(d.items())