how to zip two lists of tuples in python - python

I have two lists of tuples, for example:
a = [(1,2,3),(4,5,6),(7,8,9)]
b = [(1,'a'),(4,'b'),(7,'c')]
The first element of each tuple in a and b are matched, I want to get a list like this:
merged = [(1,2,3,'a'),(4,5,6,'b'),(7,8,9,'c')]
Perhaps I will have another list like:
c = [(1,'xx'),(4,'yy'),(7,'zz')]
and merge it to "merged" list later, I tried "zip" and "map" which are not right for this case.

>>> a = [(1,2,3),(4,5,6),(7,8,9)]
>>> b = [(1,'a'),(4,'b'),(7,'c')]
>>>
>>> [x + (z,) for x, (y, z) in zip(a, b)]
[(1, 2, 3, 'a'), (4, 5, 6, 'b'), (7, 8, 9, 'c')]
to check if first elements actually match,
>>> [x + y[1:] for x, y in zip(a, b) if x[0] == y[0]]

def merge(a,b):
for ax, (first, bx) in zip(a,b):
if ax[0] != first:
raise ValueError("Items don't match")
yield ax + (bx,)
print list(merge(a,b))
print list(merge(merge(a,b),c))

>>> [a[i]+(k,) for i,(j, k) in enumerate(b)]
[(1, 2, 3, 'a'), (4, 5, 6, 'b'), (7, 8, 9, 'c')]
Using timeit this is the fastest of the posted solutions to return a merged list.

[ (x,y,z,b[i][1]) for i,(x,y,z) in enumerate(a) if x == b[i][0] ]
This makes sure that the values are matched and then merged.

Related

Indices of intersection of lists

Given two lists of equal length:
_list = [1, 4, 8, 7, 3, 15, 5, 0, 6]
_list2 = [7, 4, 0, 1, 5, 5, 7, 2, 2]
How do I try getting an output like this:
output = [(0,3), (1,1), (3,0), (6,4), (6,5), (7,2)]
Here the intersection of two lists are obtained and the common elements' indices are arranged in the list:
output = list of (index of an element in _list, where it appears in _list2)
Trying intersection with sets is not an option since the set removes the repeating elements.
Basic-Intermediate: As a generator:
def find_matching_indices(a, b):
for i, x in enumerate(a):
for j, y in enumerate(b):
if x == y:
yield i, j
list(find_matching_indices(list1_, list2_))
# [(0, 3), (1, 1), (3, 0), (3, 6), (6, 4), (6, 5), (7, 2)]
Basic-Intermediate: As a list comprehension:
[(i, j) for i, x in enumerate(list1_) for j, y in enumerate(list2_) if x == y]
# [(0, 3), (1, 1), (3, 0), (3, 6), (6, 4), (6, 5), (7, 2)]
These solutions involve two loops.
Intermediate-Advanced: For fun, a dictionary is another data structure you might consider:
import collections as ct
import more_itertools as mit
def locate_indices(a, b):
"""Return a dictionary of `a` index keys found at `b` indices."""
dd = ct.defaultdict(list)
for i, y in enumerate(a):
idxs = list(mit.locate(b, lambda z: z == y))
if idxs: dd[i].extend(idxs)
return dd
locate_indices(list1_, list2_)
# defaultdict(list, {0: [3], 1: [1], 3: [0, 6], 6: [4, 5], 7: [2]})
Note the index of list a is the key in the dictionary. All indices in list b that share the same value are appended.
A defaultdict was used since it is helpful in building dictionaries with list values. See more on the third-party tool more_itertools.locate(), which simply yields all indices that satisfy the lambda condition - an item in list a is also found in b.
from itertools import product
from collections import defaultdict
def mathcing_indices(*lists):
d = defaultdict(lambda: tuple([] for _ in range(len(lists))))
for l_idx, l in enumerate(lists):
for i, elem in enumerate(l):
d[elem][l_idx].append(i)
return sorted([tup for _, v in d.items() for tup in product(*v)])
This solution builds a dictionary that tracks the indices that values appear at in the input lists. So if the value 5 appears at indices 0 and 2 of the first list and index 3 of the second, the value for 5 in the dictionary would be ([0, 2], [3])
It then uses itertools.product to build all the combinations of those indices.
This looks more complicated than the other answers here, but because it is O(nlogn) and not O(n**2) it is significantly faster, especially for large inputs. Two length 1000 lists of random numbers 0-1000 complete 100 tests in ~.4 seconds using the above algorithm and 6-13 seconds using some of the others here
Here is a solution that runs in O(n log n):
ind1 = numpy.argsort(_list)
ind2 = numpy.argsort(_list2)
pairs = []
i = 0
j = 0
while i<ind1.size and j<ind2.size:
e1 = _list[ind1[i]]
e2 = _list2[ind2[j]]
if e1==e2:
pairs.append((ind1[i],ind2[j]))
i = i + 1
j = j + 1
elif e1<e2:
i = i +1
elif e2<e1:
j = j + 1
print(pairs)

python: combine 2 ordered lists into list of tuples

I have 2 lists that I want to combine into a single list of tuples, so that order is maintained and the result[i] is (first[i], second[i]). Assume that the two lists will always be of the same size. Is there a way to do this using list comprehension? So for example:
>>> first = [1,2,3]
>>> second = [4,5,6]
>>> combine(first, second)
[(1,4), (2,5), (3,6)]
I've tried a few things
[(i,j) for i in first, j in second]
[(i for i in first, j for j in second)]
[(i,j) for i,j in first, second]
None of these work. I'm just wondering if this is possible or if I have to do it using a loop.
Use zip:
list(zip(first, second))
Out[384]: [(1, 4), (2, 5), (3, 6)]
Python has a function for that:
>>> zip(first, second)
[(1, 4), (2, 5), (3, 6)]
zippedy-doo-dah
You can use the built-in zip function:
>>> first = [1,2,3]
>>> second = [4,5,6]
>>> list(zip(first, second))
[(1,4), (2,5), (3,6)]
>>> first = [1,2,3]
>>> second = [4,5,6]
>>> list =zip(first,second)
>>> list
[(1, 4), (2, 5), (3, 6)]
or also for lists instead of tuple, using numpy
>>> lista = [first,second]
>>> import numpy as np
>>> np.array(lista)
array([[1, 2, 3],
[4, 5, 6]])
>>> np.array(lista)[:,0]
array([1, 4])
>>> np.array(lista)[:,1]
array([2, 5])
>>> np.array(lista)[:,2]
array([3, 6])
Use izip:
>>> first = [1,2,3]
>>> second = [4,5,6]
>>> from itertools import izip
>>> gen = izip(first, second)
>>> [(i, j) for i, j in gen]
[(1, 4), (2, 5), (3, 6)]

Find product of any subset of elements from a list

what is the best way to find the product of any number of elements from a list?
e.g if I have [a,b,c] as the input, i should get [a,b,c,a*b,a*c,b*c,a*b*c] as the output (order of elements for output doesn't matter.)
PS: Can we do it recursively? (e.g you just need the product of a*b and c to get the product a*b*c.
Any idea or suggestion is welcome. Thanks in advance!
Here you go:
from itertools import combinations
l = [2, 3, 5]
result = []
for i in range(1, len(l) + 1):
result += list(combinations(l, i))
multiplied_result = [reduce(lambda x, y: x*y, lst) for lst in result]
Now if we print the result, we get
>>> print listmap
[2, 3, 5, 6, 10, 15, 30]
You can use itertools.combinations within a list comprehension :
>>> def find_mul(li):
... return [[reduce(lambda x,y:x*y,j) for j in combinations(li,i)] for i in xrange(2,len(li)+1)]
...
DEMO:
>>> [list(combinations([2,3,4],i)) for i in xrange(2,len([2,3,4])+1)]
[[(2, 3), (2, 4), (3, 4)], [(2, 3, 4)]]
>>> l=[2,3,4]
>>> find_mul(l)
[[6, 8, 12], [24]]

In Python, how can I create lists that contain a certain index of other lists?

Say I have several lists
A = [1,2,3]
B = [4,5,6]
C = [7,8,9]
How can I create new lists so that they contain matching indexes, as in:
D = [1,4,7]
E = [2,5,8]
F = [3,6,9]
The original lists will always contain the same number of elements, and I need this to work for any number of elements and any number of lists, not just three. I figure I need to loop over a range, but I'm not sure how to go about it.
If I understand you correctly, you may be looking for zip():
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = [7, 8, 9]
>>> zipped = zip(a, b, c)
>>> zipped
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
If instead of several lists, you have a list of the lists that you want to zip, then you can use * for unpacking the sublists, as follows:
>>> myListOfLists = [[1,2,3],[4,5,6],[7,8,9]]
>>> zipped = zip(*myListOfLists)
>>> zipped
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

searching through a nested-list in python

I have a nested list of tuples of 97510 values like this:
a = [ (1,2,3), (3,4,5), (5,4,2)]
every first value (index=0) is unique and I need to find other index=0 items that have the same index=1 items
In the example , I need to find the second and third tuples where the the second item '4' is common .
How do I do it ?
If you want to find all matches:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for inner in a:
... d[inner[1]].append(inner)
...
>>> d
defaultdict(<type 'list'>, {2: [(1, 2, 3)], 4: [(3, 4, 5), (5, 4, 2)]})
>>> d[4]
[(3, 4, 5), (5, 4, 2)]
If you want to pick out all matches for a particular second value:
>>> filter(lambda inner: inner[1] == 4, a)
[(3, 4, 5), (5, 4, 2)]
Edit: As pointed out in the comments, a list comprehension is preferable as it is more efficient for such work:
>>> [inner for inner in a if inner[1] == 4]
[(3, 4, 5), (5, 4, 2)]
Using timeit shows the list comprehension is about 2.5 times faster (on my machine anyway):
>>> timeit.timeit('[inner for inner in a if inner[1] == 4]', 'a=[(1,2,3), (3,4,5), (5, 4, 2)]')
2.5041549205780029
>>> timeit.timeit('filter(lambda inner: inner[1] == 4, a)', 'a=[(1,2,3), (3,4,5), (5, 4, 2)]')
6.328679084777832
Here is one way to do it:
>>> result = defaultdict(list)
>>> for item in a:
>>> result[item[1]].append(item)
>>> result
defaultdict(<type 'list'>, {2: [(1, 2, 3)], 4: [(3, 4, 5), (5, 4, 2)]})
This will result in a dictionary of lists where all items with the same second value are in one list, with that value as the key.
Another alternative:
from operator import itemgetter
from itertools import groupby
a = [ (1,2,3), (3,4,5), (5,4,2)]
b = groupby(sorted(a), itemgetter(1))
for val, group in b:
print val, list(group)
# 2 [(1, 2, 3)]
# 4 [(3, 4, 5), (5, 4, 2)]
Note that you can also use groupby:
from itertools import groupby
data = [ (1,2,3), (3,4,5), (5,4,2)]
res = groupby(sorted(data), key=lambda x: x[1])
Edited as per comment
Played around with the problem and found one more solution - however not the best one, but:
inputVals = [(1,2,3), (3,4,5), (5,4,2), (2,2,3), (7,3,1)]
for val in set(x[1] for x in inputVals):
print val, list(set(sval for sval in inputVals if sval[1] == val))

Categories

Resources