List multiplication [duplicate] - python

This question already has answers here:
Operation on every pair of element in a list
(5 answers)
Closed 8 months ago.
I have a list L = [a, b, c] and I want to generate a list of tuples :
[(a,a), (a,b), (a,c), (b,a), (b,b), (b,c)...]
I tried doing L * L but it didn't work. Can someone tell me how to get this in python.

You can do it with a list comprehension:
[ (x,y) for x in L for y in L]
edit
You can also use itertools.product as others have suggested, but only if you are using 2.6 onwards. The list comprehension will work will all versions of Python from 2.0. If you do use itertools.product bear in mind that it returns a generator instead of a list, so you may need to convert it (depending on what you want to do with it).

The itertools module contains a number of helpful functions for this sort of thing. It looks like you may be looking for product:
>>> import itertools
>>> L = [1,2,3]
>>> itertools.product(L,L)
<itertools.product object at 0x83788>
>>> list(_)
[(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]

Take a look at the itertools module, which provides a product member.
L =[1,2,3]
import itertools
res = list(itertools.product(L,L))
print(res)
Gives:
[(1,1),(1,2),(1,3),(2,1), .... and so on]

Two main alternatives:
>>> L = ['a', 'b', 'c']
>>> import itertools
>>> list(itertools.product(L, L))
[('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'b'), ('b', 'c'), ('c', 'a'), ('c', 'b'), ('c', 'c')]
>>> [(one, two) for one in L for two in L]
[('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'b'), ('b', 'c'), ('c', 'a'), ('c', 'b'), ('c', 'c')]
>>>
the former one needs Python 2.6 or better -- the latter works in just about any Python version you might be tied to.

x = [a,b,c]
y = []
for item in x:
for item2 in x:
y.append((item, item2))
Maybe not the Pythonic way but working

Ok I tried :
L2 = [(x,y) for x in L for x in L] and this got L square.
Is this the best pythonic way to do this? I would expect L * L to work in python.

The most old fashioned way to do it would be:
def perm(L):
result = []
for i in L:
for j in L:
result.append((i,j))
return result
This has a runtime of O(n^2) and is therefore quite slow, but you could consider it to be "vintage" style code.

Related

Why tuples in a set won't convert to any other type in a loop?

I'm trying to remove a certain item from a set of tuples. to do so I must convert the tuples to a list or a set (i.e. a mutable object). I'm trying to do in a for loop but the tuples won't convert and my item is yet to be removed.
a = [('A', 'C'), ('B', 'C'), ('B', 'C')]
for i in a:
i = list(i)
if 'C' in i:
i.remove('C')
print(a)
This is the output:
[('A', 'C'), ('B', 'C'), ('B', 'C')]
You got the right intuition. As your tuples are immutable, you need to create new ones.
However, in your code, you create lists, modify them, but fail to save them back in the original list.
You could use a list comprehension.
[tuple(e for e in t if e != 'C') for t in a]
Output:
[('A',), ('B',), ('B',)]
You are modifying the list but are not creating a new list.
Try this:
a = [('A', 'C'), ('B', 'C'), ('B', 'C')]
b = []
for i in a:
i = list(i)
if 'C' in i:
i.remove('C')
b.append(i)
print(b)

how to pair each 2 elements of a list?

I have a list like this
attach=['a','b','c','d','e','f','g','k']
I wanna pair each two elements that followed by each other:
lis2 = [('a', 'b'), ('c', 'd'), ('e', 'f'), ('g', 'k')]
I did the following:
Category=[]
for i in range(len(attach)):
if i+1< len(attach):
Category.append(f'{attach[i]},{attach[i+1]}')
but then I have to remove half of rows because it also give 'b' ,'c' and so on. I thought maybe there is a better way
You can use zip() to achieve this as:
my_list = ['a','b','c','d','e','f','g','k']
new_list = list(zip(my_list[::2], my_list[1::2]))
where new_list will hold:
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('g', 'k')]
This will work to get only the pairs, i.e. if number of the elements in the list are odd, you'll loose the last element which is not as part of any pair.
If you want to preserve the last odd element from list as single element tuple in the final list, then you can use itertools.zip_longest() (in Python 3.x, or itertools.izip_longest() in Python 2.x) with list comprehension as:
from itertools import zip_longest # In Python 3.x
# from itertools import izip_longest ## In Python 2.x
my_list = ['a','b','c','d','e','f','g','h', 'k']
new_list = [(i, j) if j is not None else (i,) for i, j in zip_longest(my_list[::2], my_list[1::2])]
where new_list will hold:
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('g', 'h'), ('k',)]
# last odd number as single element in the tuple ^
You have to increment iterator i.e by i by 2 when moving forward
Category=[]
for i in range(0, len(attach), 2):
Category.append(f'{attach[i]},{attach[i+1]}')
Also, you don't need the if condition, if the len(list) is always even
lis2 = [(lis[i],lis[i+1]) for i in range(0,len(lis),2)]
lis2
You can use list comprehension

How to convert a dict of lists to a list of tuples of key and value in python?

I have a dict of lists like this:
y = {'a':[1,2,3], 'b':[4,5], 'c':[6]}
I want to convert the dict into a list of tuples, each element of which is a tuple containing one key of the dict and one element in the value list:
x = [
('a',1),('a',2),('a',3),
('b',4),('b',5),
('c',6)
]
My code is like this:
x = reduce(lambda p,q:p+q, map(lambda (u,v):[(u,t) for t in v], y.iteritems()))
Such a code seems hard to read, so I wonder if there is any pythonic way, or more precisely, a way in list comprehension to do such thing?
You could do something like this,
>>> y = {'a':[1,2,3], 'b':[4,5], 'c':[6]}
>>> [(i,x) for i in y for x in y[i]]
[('a', 1), ('a', 2), ('a', 3), ('c', 6), ('b', 4), ('b', 5)]
Another approach, but not necessarily more readable or pythonic:
>>> from itertools import izip_longest
>>> y = {'a':[1,2,3], 'b':[4,5], 'c':[6]}
>>> [tuple(izip_longest(k, v, fillvalue=k)) for k, v in y.items()]
[(('a', 1), ('a', 2), ('a', 3)), (('c', 6),), (('b', 4), ('b', 5))]

filtering a list of tuples in python

Am looking for a clean pythonic way of doing the following
I have a list of tuples say :
[(1,'a'), (1,'b'), (1,'c'), (2, 'd'), (5, 'e'), (5, 'f')]
I want to make a new list which discards tuples whose first key has been seen before. So the o/p for the above would be:
[(1,'c'), (2,'d'), (5, 'f')]
Thanks!
A simple way would be creating a dictionary, since it will only keep the last element with the same key:
In [1]: l = [(1,'a'), (1,'b'), (1,'c'), (2, 'd'), (5, 'e'), (5, 'f')]
In [2]: dict(l).items()
Out[2]: [(1, 'c'), (2, 'd'), (5, 'f')]
Update: As #Tadeck mentions in his comment, since the order of dictionary items is not guaranteed, you probably want to use an ordered dictionary:
from collections import OrderedDict
newl = OrderedDict(l).items()
If you actually want to keep the first tuple with the same key (and not the last, your question is ambiguous), then you could reverse the list first, add it do the dictionary and reverse the output of .items() again.
Though in that case there are probably better ways to accomplish this.
Using unique_everseen from itertools docs
from itertools import ifilterfalse
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
a = [(1,'a'), (1,'b'), (1,'c'), (2, 'd'), (5, 'e'), (5, 'f')]
print list(unique_everseen(a,key=lambda x: x[0]))
Yielding
[(1, 'a'), (2, 'd'), (5, 'e')]
a nifty trick for one liner fetishists that keeps the order in place (I admit it's not very readable but you know...)
>>> s = [(1,'a'), (1,'b'), (1,'c'), (2, 'd'), (5, 'e'), (5, 'f')]
>>> seen = set()
>>> [seen.add(x[0]) or x for x in s if x[0] not in seen]
[(1, 'a'), (2, 'd'), (5, 'e')]

Performance_Python get union of 2 lists of tuple according to 2 out of the 3 elements of the tuple

My program is not doing a great job. In a loop, data from each processor (list of tuple) are gathered into the master processor that needs to clean it by removing similar element.
I found a lot of interesting clue on internet and especially in this site about union of list. However, i have not managed to apply it to my problem.
My aim is to get rid of tuple whose its two last element are similar to another tuple in the list . for example:
list1=[[a,b,c],[d,e,f],[g,h,i]]
list2=[[b,b,c],[d,e,a],[k,h,i]]
the result should be:
final=[[a,b,c],[d,e,f],[g,h,i],[d,e,a]]
Right now I'm using loops and break but I'm hoping to make this process faster.
here is what my code looks like (result and temp are the lists I want to get union from)
on python2.6.
for k in xrange(len(temp)):
u=0
#index= next((j for j in xrange(lenres) if temp[k][1:3] == result[j][1:3]),None)
for j in xrange(len(result)):
if temp[k][1:3] == result[j][1:3]:
u=1
break
if u==0:
#if index is None:
result.append([temp[k][0],temp[k][1],temp[k][2]])
Thanks for your help
Herve
Below is our uniques function. It takes arguments l (list) and f (function), returns list with duplicates removed (in the same order). Duplicates are defined by: b is duplicate of a iff f(b) == f(a).
def uniques(l, f = lambda x: x):
return [x for i, x in enumerate(l) if f(x) not in [f(y) for y in l[:i]]]
We define lastTwo as follows:
lastTwo = lambda x: x[-2:]
For your problem we use it as follows:
>>> list1
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i')]
>>> list2
[('b', 'b', 'c'), ('d', 'e', 'a'), ('k', 'h', 'i')]
>>> uniques(list1+list2, lastTwo)
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ('d', 'e', 'a')]
If the usecase you describe comes up a lot you may want to define
def hervesMerge(l1, l2):
return uniques(l1+l2, lambda x: x[-2:])
Identity is our default f but it can be anything (so long as it is defined for all elements of the list, since they can be of any type).
f can be sum of a list, odd elements of a list, prime factors of an integer, anything. (Just remember that if its injective theres no point! Add by constant, linear functions, etc will work no differently than identity bc its f(x) == f(y) w/ x != y that makes the difference)
>>> list1
[(1, 2, 3, 4), (2, 5), (6, 2, 2), (3, 4), (8, 3), (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)]
>>> uniques(list1, sum)
[(1, 2, 3, 4), (2, 5), (8, 3)]
>>> uniques(list1, lambda x: reduce(operator.mul, x)) #product
[(1, 2, 3, 4), (2, 5), (3, 4), (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)]
>>> uniques([1,2,3,4,1,2]) #defaults to identity
[1, 2, 3, 4]
You seemed concerned about speed, but my answer really focused on shortness/flexibility without significant (or any?) speed improvment. For bigger lists where speed is z concern, you want to take advantage of hashable checks and the fact that list1 and list2 are known to have no duplicates
>>> s = frozenset(i[-2:] for i in list1)
>>> ans = list(list1) #copy list1
>>> for i in list2:
if i[-2:] not in s: ans.append(i)
>>> ans
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ('d', 'e', 'a')]
OR allowing disordering
>>> d = dict()
>>> for i in list2 + list1:
d[i[-2:]] = i
>>> d.values()
[('d', 'e', 'f'), ('a', 'b', 'c'), ('g', 'h', 'i'), ('d', 'e', 'a')]
--Edit--
You should always be able to avoid un-pythonic looping like you post in your question. Here is your exact code with the loops changed:
for k in temp:
u=0
for j in result:
if k[1:3] == j[1:3]:
u=1
break
if u==0:
#if index is None:
result.append([k[0],k[1],k[2]]) // k
result and temp are iterable, and for anything iterable you can put it directly in the for loop without eanges. If for some reason you explicitly need the index (this is not such a case, but I have one above) you can use enumerate.
Here's a simple solution using a set:
list1=[('a','b','c'),('d','e','f'),('g','h','i')]
list2=[('b','b','c'),('d','e','a'),('k','h','i')]
set1 = set([A[1:3] for A in list1])
final = list1 + [A for A in list2 if A[1:3] not in set1]
However, if your list1 and list2 aren't actually made of tuples, then you will have to put tuple() around A[1:3].

Categories