Generating and merging all combinations of multiple lists python - python

Similiar questions to this one have been asked before, but none exactly like it an and I'm kind of lost.
If I have 2 sets of lists (or a lists of lists)
listOLists = [[1,2,3],[1,3,2]]
listOLists2 = [[4,5,6],[4,6,5]]
And I want 'merge' the two lists to make
mergedLists = [[1,2,3,4,5,6],[1,3,2,4,5,6],[1,2,3,4,6,5],[1,3,2,4,6,5]]
How would I do this?

list1s=[[1,2,3],[3,2,1],[2,2,2]]
list2s=[[3,3,3],[4,4,4],[5,5,5]]
for indis1 in list1s:
for indis2 in list2s:
print(indis1 + indis2)
try and;
[1, 2, 3, 3, 3, 3]
[1, 2, 3, 4, 4, 4]
[1, 2, 3, 5, 5, 5]
[3, 2, 1, 3, 3, 3]
[3, 2, 1, 4, 4, 4]
[3, 2, 1, 5, 5, 5]
[2, 2, 2, 3, 3, 3]
[2, 2, 2, 4, 4, 4]
[2, 2, 2, 5, 5, 5]

You may use generator to simplify your code, like this:
a = [[1, 2, 3], [1, 3, 2], [2, 1, 3]]
b = [[4, 5, 6], [4, 6, 5], [5, 4, 6]]
c = [i + j for i in a for j in b]
print c
Output:
[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 6, 5], [1, 2, 3, 5, 4, 6], [1, 3, 2, 4, 5, 6], [1, 3, 2, 4, 6, 5], [1, 3, 2, 5, 4, 6], [2, 1, 3, 4, 5, 6], [2, 1, 3, 4, 6, 5], [2, 1, 3, 5, 4, 6]]

list1 = [[1,2,3],[1,3,2]]
list2 = [[4,5,6],[4,6,5]]
mergedLists = []
for list1_inner in list1:
for list2_inner in list2:
mergedLists.append(list1_inner + list2_inner)
print(mergedLists)

A comparison of methods:
import itertools
import random
l1 = [[random.randint(1,100) for _ in range(100)]for _ in range(100)]
l2 = [[random.randint(1,100) for _ in range(100)]for _ in range(100)]
With itertools:
def itert(l1, l2):
[list(itertools.chain(*x)) for x in itertools.product(l1, l2)]
With for loops:
def forloops(list1, list2):
mergedLists = []
for list1_inner in list1:
for list2_inner in list2:
mergedLists.append(list1_inner + list2_inner)
With a simple Comprehension:
def comp(l1, l2):
[i + j for i in l1 for j in l2]
Speed
%time itert(l1, l2)
Wall time: 99.8 ms
%time comp(l1, l2)
Wall time: 31.3 ms
%time forloops(l1, l2)
Wall time: 46.9 ms

Related

Iteratively multiply elements of two lists in python

I have two lists (X and Y). I want to first scale the first element of X with all the elements of Y before moving to the second element of X which is then again scaled by all the elements of Y. How can I do this? Ideally, I would also like to append it to different lists (L) when a new element of X is started, but I am not sure if that is possible.
Y = [2, 3, 4, 5]
X = [1, 2, 3, 4]
L = []
for i in range(len(X)):
for j in range(len(Y)):
X_scale = X[i] * Y[j]
L.append(X_scale)
Preferred outcome:
# First element in X
X_scale = [2, 2, 3, 4]
X_scale = [3, 2, 3, 4]
X_scale = [4, 2, 3, 4]
X_scale = [5, 2, 3, 4]
# Second element in X
X_scale = [1, 4, 3, 4]
X_scale = [1, 6, 3, 4]
#etc
This seems to follow your pattern:
Y = [2, 3, 4, 5]
X = [1, 2, 3, 4]
L = []
for i,x in enumerate(X):
for y in Y:
X_scale = X.copy()
X_scale[i] = x * y
L.append(X_scale)
for row in L:
print(row)
Output:
[2, 2, 3, 4]
[3, 2, 3, 4]
[4, 2, 3, 4]
[5, 2, 3, 4]
[1, 4, 3, 4]
[1, 6, 3, 4]
[1, 8, 3, 4]
[1, 10, 3, 4]
[1, 2, 6, 4]
[1, 2, 9, 4]
[1, 2, 12, 4]
[1, 2, 15, 4]
[1, 2, 3, 8]
[1, 2, 3, 12]
[1, 2, 3, 16]
[1, 2, 3, 20]
Per OP's comment to group the indices:
Y = [2, 3, 4, 5]
X = [1, 2, 3, 4]
L = []
for i,x in enumerate(X):
L2 = []
for y in Y:
X_scale = X.copy()
X_scale[i] = x * y
L2.append(X_scale)
L.append(L2)
for row in L:
print(row)
Output:
[[2, 2, 3, 4], [3, 2, 3, 4], [4, 2, 3, 4], [5, 2, 3, 4]]
[[1, 4, 3, 4], [1, 6, 3, 4], [1, 8, 3, 4], [1, 10, 3, 4]]
[[1, 2, 6, 4], [1, 2, 9, 4], [1, 2, 12, 4], [1, 2, 15, 4]]
[[1, 2, 3, 8], [1, 2, 3, 12], [1, 2, 3, 16], [1, 2, 3, 20]]
First you can simply you loops by accessing the items directly, without an index. Then you can transform the inner loop into a comprehension list to make it more compact:
Y = [2, 3, 4, 5]
X = [1, 2, 3, 4]
L = []
for x_item in X:
L += [x_item * y_item for y_item in Y]

List of list, compare the last item with python

I need to compare the elements of a list of list. My code is for two items inside of the list of list but when I have more than two I don't know how proceed.
My inputs have the same len ever. And I need to compare d[][:1] and if it is repeated check the d[][:-1] and print the d[] with the less d[][:-1]
The print I need: d = [[1, 2, 3, 4, 4], [3, 2, 4, 2, 1]]
Code:
d = [[1, 2, 3, 4, 5],
[1, 2, 3, 4, 6],
[1, 2, 3, 4, 4],
[3, 2, 4, 2, 5],
[3, 2, 4, 2, 1]]
if d[0][:-1] == d[1][:-1]:
if d[0][-1] < d[1][-1]:
d.remove(d[1])
else:
d.remove(d[0])
>>> print d
[[1, 2, 3, 4, 5], [1, 2, 3, 4, 4], [3, 2, 4, 2, 5], [3, 2, 4, 2, 1]]
Edited:
from operator import itemgetter
from itertools import groupby
d = [['4027221', 'MX', '0.4', 3],
['4027221', 'MX', '30', 1],
['4027222', 'MX', '0.4', 3],
['4027222', 'MX', '30', 1]]
d.sort()
d = [min(g, key=lambda s: s[-2]) for _, g in groupby(d, key=lambda s: s[:-2])]
[['4027221', 'MX', '0.4', 3], ['4027222', 'MX', '0.4', 3]]
You can use itertools.groupby to group the list by all but the last item first, and then sort the sub-lists by the last item with min:
from operator import itemgetter
from itertools import groupby
d = [[1, 2, 3, 4, 5],
[1, 2, 3, 4, 6],
[1, 2, 3, 4, 4],
[3, 2, 4, 2, 5],
[3, 2, 4, 2, 1]]
print([min(g, key=itemgetter(-1)) for _, g in groupby(d, key=lambda s: s[:-1])])
This outputs:
[[1, 2, 3, 4, 4], [3, 2, 4, 2, 1]]
if I understood well what you want, this should do the trick:
d = [[1, 2, 3, 4, 5],
[1, 2, 3, 4, 6],
[1, 2, 3, 4, 4],
[3, 2, 4, 2, 5],
[3, 2, 4, 2, 1]]
mins = {}
for a_list in d:
list_key = ','.join(map(str, a_list[:-1]))
list_orderer = a_list[-1]
if list_key not in mins or mins[list_key] > list_orderer:
mins[list_key] = a_list
print(sorted(mins.values())) # [[1, 2, 3, 4, 4], [3, 2, 4, 2, 1]]
It works in Python 2 and 3, it does not require the input to be sorted and it does not require any dependency (which is not a real argument).
You can do it this way too:
d = [[1, 2, 3, 4, 5],
[1, 2, 3, 4, 6],
[1, 2, 3, 4, 4],
[3, 2, 4, 2, 5],
[3, 2, 4, 2, 1]]
sublists = list(set(tuple(i[:-1]) for i in d))
mins = [min([elem for elem in d if elem[:-1]==list(s)])for s in sublists]
print(mins)
Output:
[[3, 2, 4, 2, 1], [1, 2, 3, 4, 4]]
Scaling up blhsing's solution. For bigger data and skipping the need to sort.
import pandas as pd
cols = ['v1', 'v2', 'v3', 'v4', 'v5']
df = pd.DataFrame(d, columns=cols)
ndf = df.groupby(cols[:-1], as_index=False).min()
out = ndf.values.tolist()
print(out)
[[1, 2, 3, 4, 4], [3, 2, 4, 2, 1]]
You can use a dictionary, taking advantage of the fact that, as you iterate, only the last value will be attached to any given key. The solution does not require sorting.
d2 = {tuple(key): val for *key, val in d}
res = [list(k) + [v] for k, v in d2.items()]
print(res)
[[1, 2, 3, 4, 4],
[3, 2, 4, 2, 1]]
Note tuple conversion is required since lists are not hashable, so they cannot be used as dictionary keys.
Edit: as #JonClements suggests, you can write this more simply as:
res = list({tuple(el[:-1]): el for el in d}.values())

I am trying to make a big list by using a present list but it's not working, can anybody explain why is this happening?

list1=[0,1,2]
mega_list=[]
for x in range(4):
print(list1)
mega_list.append(list1)
list1.pop(0)
list1.insert(2,list1[1]+1)
print(mega_list)
This is because you are adding a reference to list1 to mega_list. That is to say, at the end of the for loop you really have this:
mega_list = [list1, list1, list1, list1]
But, after the loop, list1 = [4, 5, 6], so that's why you get the output:
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
To fix this, make a copy of the list before iterating further:
list1=[0,1,2]
mega_list=[]
for x in range(4):
print(list1)
mega_list.append(list1.copy())
list1.pop(0)
list1.insert(2, list1[1]+1)
print(mega_list)
Which outputs:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]
list1=[0,1,2]
mega_list=[]
for x in range(4):
# Adds list1 as an element
mega_list.append(list1)
# Increments each element of list1
list1 = [x+1 for x in list1]
print(mega_list)
>>>[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]
You need to create a copy of the list, rather use the same instance in each append, e.g.:
list1=[0,1,2]
mega_list=[]
for x in range(5):
mega_list.append(list1[:])
list1.pop(0)
list1.insert(2,list1[1]+1)
print(mega_list) # [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
An alternative, is just use indexes into a full list of the input values, e.g.:
list1=list(range(7)) # [0,1,2,3,4,5,6]
mega_list=[]
for i in range(len(list1)-3+1):
mega_list.append(list1[i:i+3])
print(mega_list) # [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
One alternative is to create a generalized nwise() function using itertools:
import itertools as it
def nwise(iterable, n):
ts = it.tee(list1, n)
for c, t in enumerate(ts):
next(it.islice(t, c, c), None)
return zip(*ts)
list1 = list(range(7))
mega_list = [x for x in nwise(list1, 3)]
print(mega_list) # [(0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]

Display a List with order modulo N in Python

I want to display a list with order modulo N, for exemple:
With N =6, I have a list l[k]=[1, 2, 3, 4, 5, 6], so I can display its revers l[-k]=[6,5,4,3,2,1] by the instruction l[::-1].
But now I want to display l[(-k)mod N] which is [1,6,5,4,3,2] and then l[(1-k)mod N] which is [2,1,6,5,4,3] and so on.
Is there any instruction in python for display a list like that?
>>> l = [1,2,3,4,5,6]
>>> N = len(l)
>>> revL = l[::-1]
>>> revL
[6, 5, 4, 3, 2, 1]
>>> for i in range(1,N):
... print revL[-i:] + revL[:(N-i)]
...
[1, 6, 5, 4, 3, 2]
[2, 1, 6, 5, 4, 3]
[3, 2, 1, 6, 5, 4]
[4, 3, 2, 1, 6, 5]
[5, 4, 3, 2, 1, 6]

combining elements of a list 3 at the time

I'm struggling to understand how to combine elements from a list.
I have:
a = [[1, 2], [3, 4, 5], [6], [7, 8, 9, 10]]
I want to create triplets by taking elements from each list members
but always including the first element. This piece of code does half the
job:
r=[[]]
for x in a[0:3]:
t = []
for y in x:
for i in r:
t.append(i+[y])
r = t
r
[[1, 3, 6], [2, 3, 6], [1, 4, 6], [2, 4, 6], [1, 5, 6], [2, 5, 6]]
but I also want:
[[1,6,7], [1,6,8], [1,6,9] etc.]
Could someone please suggest a good method to do that?
Using itertools:
import itertools
a = [[1, 2], [3, 4, 5], [6], [7, 8, 9, 10]]
for xss in itertools.combinations(a, 3): # pick 3 lists.
# xss => ([1, 2], [3, 4, 5], [6])
# ([1, 2], [3, 4, 5], [7, 8, 9, 10])
# ...
indexed = [enumerate(x) for x in xss]
# indexed => [[(0, 1), (1, 2)], [(0, 3), (1, 4), (2, 5)], [(0, 6)]]
# ^^^^^^^^^^^^^^^^ list(enumerate([1, 2]))
for xs in itertools.product(*indexed):
# xs => ((0, 1), (0, 3), (0, 6))
# ((0, 1), (0, 3), (0, 7))
# ((0, 1), (0, 6), (0, 7))
# ...
if all(i > 0 for i, x in xs): # exclude no first item is selected.
continue
print [x for i, x in xs]
UPDATE response to the comment.
import itertools
a = [[1, 2], [3, 4, 5], [6], [7, 8, 9, 10]]
for xss in itertools.combinations(a[1:], 2):
xss = (a[0],) + xss
indexed = [enumerate(x) for x in xss]
for xs in itertools.product(*indexed):
if all(i > 0 for i, x in xs):
continue
print [x for i, x in xs]
output:
[1, 3, 6]
[1, 4, 6]
[1, 5, 6]
[2, 3, 6]
[2, 4, 6]
[2, 5, 6]
[1, 3, 7]
[1, 3, 8]
[1, 3, 9]
[1, 3, 10]
[1, 4, 7]
[1, 4, 8]
[1, 4, 9]
[1, 4, 10]
[1, 5, 7]
[1, 5, 8]
[1, 5, 9]
[1, 5, 10]
[2, 3, 7]
[2, 3, 8]
[2, 3, 9]
[2, 3, 10]
[2, 4, 7]
[2, 5, 7]
[1, 6, 7]
[1, 6, 8]
[1, 6, 9]
[1, 6, 10]
[2, 6, 7]
[2, 6, 8]
[2, 6, 9]
[2, 6, 10]

Categories