Python : Combining the values of two arrays [duplicate] - python

This question already has answers here:
How to merge lists into a list of tuples?
(10 answers)
Closed 4 years ago.
I am struggling to combine the values of two arrays in Python. I want to get the values of two arrays as pairs.
EXAMPLE: Suppose we have two arrays a and b as below:
a = [[1, 2, 3], [4, 5, 6], [0, 3, 1]]
for i in range(len(a)):
for j in range(len(a[i])):
print(a[i][j], end=' ')
print()
b = [[4, 0, 3], [6, 3, 6], [1, 4, 1]]
for i in range(len(b)):
for j in range(len(b[i])):
print(b[i][j], end=' ')
print()
How can I combine the values of two arrays as pairs similar to:
array([[(1,4), (2,0), (3,3)],
[(4,6), (5,3), (6,6)],
[(0,1), (3,4), (1,1)]])
I am not sure if it can be done as an array or a list.

You can combine list comprehension and zip() to do that:
a = [[1, 2, 3], [4, 5, 6], [0, 3, 1]]
b = [[4, 0, 3], [6, 3, 6], [1, 4, 1]]
c = [ list(zip(a[x],b[x])) for x in range(len(a))] # works b/c len(a) = len(b)
print(c)
Output
[[(1, 4), (2, 0), (3, 3)], [(4, 6), (5, 3), (6, 6)], [(0, 1), (3, 4), (1, 1)]]
This works correctly as a and b have the same amount of inner lists and the inner lists have the same length. zip() does only create tuples for 2 lists up to the shorter length of both. For inequeal length lists you can use itertools.zip_longest(...) which uses None or a specified default to fill up the shorter list.

Related

Python inception of generators

I'm trying to make a general way to generate all the combinations of multiple ranges or lists, for example,
[range(0, 2), range(2, 5), range(4, 6), range(2, 3)],
which should return a 2x3x2x1 = 12 element list.
[[0, 2, 4, 2],
[0, 2, 5, 2],
[0, 3, 4, 2],
[0, 3, 5, 2],
[0, 4, 4, 2],
[0, 4, 5, 2],
[1, 2, 4, 2],
[1, 2, 5, 2],
[1, 3, 4, 2],
[1, 3, 5, 2],
[1, 4, 4, 2],
[1, 4, 5, 2]]
So far, everything is fine here. When I hardcode it, by doing
x = [ ( [a,b] for a in rgs[0] for b in rgs[1] ) ]
x.append( ( a + [b] for a in x[-1] for b in rgs[2]) )
x.append( ( a + [b] for a in x[-1] for b in rgs[3]) )
I get the good result. However, when I attempt to generalize it, by doing
x = [ ( [a,b] for a in rgs[0] for b in rgs[1] ) ]
for i in range(1,len(rgs)-1):
x.append( ( a + [b] for a in x[-1] for b in rgs[i+1]) )
I obtain a 6-element list:
[[0, 2, 2, 2],
[0, 3, 2, 2],
[0, 4, 2, 2],
[1, 2, 2, 2],
[1, 3, 2, 2],
[1, 4, 2, 2]]
Also, what I notice is that all the ranges generated after the first two use the range in rgs[-1] instead of the correct ones. I struggle to understand why this happens, as I beleive that those two code example are identical, except the latter is the more general form for arbitrary large number of ranges.
You can use itertools.product to output a list of tuples
Input:
import itertools
a= [range(0, 2), range(2, 5), range(4, 6), range(2, 3)]
list(itertools.product(*a))
Output:
[(0, 2, 4, 2),
(0, 2, 5, 2),
(0, 3, 4, 2),
(0, 3, 5, 2),
(0, 4, 4, 2),
(0, 4, 5, 2),
(1, 2, 4, 2),
(1, 2, 5, 2),
(1, 3, 4, 2),
(1, 3, 5, 2),
(1, 4, 4, 2),
(1, 4, 5, 2)]
I did not get the same result when running your first code. I had to change it up a bit:
x = [ ( [a,b] for a in rgs[0] for b in rgs[1] ) ]
x.append( ( a + [b] for a in x[-1] for b in rgs[2]) )
x = list( a + [b] for a in x[-1] for b in rgs[3])
Most people that don't know about itertools would have done it this way:
x=[]
for i0 in rgs[0]:
for i1 in rgs[1]:
for i2 in rgs[2]:
for i3 in rgs[3]:
x.append([i0,i1,i2,i3])
Or using a list comprehension (DON'T DO THIS, it is VERY messy looking):
[[i0,i1,i2,i3] for i3 in rgs[3] for i2 in rgs[2] for i1 in rgs[1] for i0 in rgs[0]]
Your issue has to do with creating generator expressions in a loop. Generator expressions are implemented as functions, and like functions, they can have "free" variables that they look up in the containing namespaces. Your generator expressions are accessing the i from outside their definition, and because of this, they end up seeing a different i value you expect.
Here's an example that might be easier to understand:
def gen()
print(i)
yield 10
x = []
for i in range(3):
x.append(gen()) # add several generators while `i` has several different values
for g in x:
list(g) # consume the generators, so they can print `i`
Here, rather than using the i value for something useful, I've written a generator function that just prints it out. If you run this code, you'll see that all the generators print out 2, since that's the value of i when they finally run (after the first loop ended).
Your situation is a little more subtle, as you're consuming the previous generator as you create the next one, but the general idea is the same. The generator expression loop that you expect to be over rgs[2] is actually over rgs[3] because it's actually being looked up with rgs[i+1] and i increased between the time the generator expression was declared and when it was consumed.

Change the data structure [duplicate]

This question already has answers here:
Combine two lists into one multidimensional list
(4 answers)
Closed 5 years ago.
I have 2 lists with the same size. I want to make a new list which the combination of both of them (list of lists) in a way that elements with the same indexes would be in a list and this list would have the same index.
input example:
a = [1, 2, 3]
b = [4, 5, 6]
combined = [[1, 4], [2, 5], [3, 6]]
do you know how to do that?
Using the built-in zip:
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> zip(a, b)
[(1, 4), (2, 5), (3, 6)]
This gives you the list.
combined = [[i,k] for i,k in zip(a,b)]
this will give you list of tuples
combined = list(zip(a, b))
if you really need your elements to be lists then we can write
combined = list(map(list, zip(a, b)))
Use zip
>>> list(zip(a,b))
[(1, 4), (2, 5), (3, 6)]
Or you want list instead of tuples :
>>> [[x,y] for x,y in zip(a,b)]
[[1, 4], [2, 5], [3, 6]]
You can zip them:
list(zip(a, b))
a = [1, 2, 3]
b = [4, 5, 6]
combined = list(zip(a,b))
for i in combined:
print(i)
Use zip command to combine both the list.

Permutations of a list with constraints python

I am desperately trying to get all the permutations of a list while enforcing position assignment constraints.
I have a list [1,2,3,4,5,6] (6 is just an example, I would like to find something that could work with every lenght) and I want to find all the lists of lenght 3 (also an example) with the following constraints :
position 1 can be occupied by numbers 1 and 2
position 2 can be occupied by numbers 1,2 and 3
position 3 can be occupied by numbers 2,3 and 4
repetions of a same number are not allowed
That would give these lists : [1,2,3],[1,2,4],[1,3,2],[1,3,4],[2,1,3],[2,3,4],[2,1,4]
For those interested, what I am trying to implement is what is explained pages 5 and 6 of this paper
Filter the product() of those subsets:
from itertools import product
for combo in product([1, 2], [1, 2, 3], [2, 3, 4]):
if len(set(combo)) == 3:
print(combo)
or as a list comprehension:
[combo for combo in product([1, 2], [1, 2, 3], [2, 3, 4]) if len(set(combo)) == 3]
Output:
>>> from itertools import product
>>> [combo for combo in product([1, 2], [1, 2, 3], [2, 3, 4]) if len(set(combo)) == 3]
[(1, 2, 3), (1, 2, 4), (1, 3, 2), (1, 3, 4), (2, 1, 3), (2, 1, 4), (2, 3, 4)]

How to change the major order of lists in python?

I have a list of N lists, each sub-list containing M elements.
How do I create a list of M lists with N elements each, containing the n-th elements from the initial lists?
Let's say I have this
myList = [[1, 2, 3],[4, 5, 6]]
I want to have
[[1, 4],[2, 5],[3, 6]]
Performance is also important. I have sublists of millions of elements (though, one dimension will always be small: like 1.000.000 x 8 )
This will give tuples, but it's trivial to extend this to contain lists:
zip(*myList)
i.e.
[list(i) for i in zip(*myList)]
Use
myList = [[1, 2, 3],[4, 5, 6]]
zip(*myList )
>>> lst = [[1, 2, 3], [4, 5, 6]]
>>> zip(*lst)
[(1, 4), (2, 5), (3, 6)]
zip returns a list in Python 2, but an iterator in Python 3, which you have to convert to a list if you need to (list(zip(lst)))
See also: Matrix Transpose in Python, Transpose/Unzip Function (inverse of zip)?
What zip(*lst) does is unpacks the elements of lst using the * operator into separate arguments of the zip function.
We know what happens when we put two lists in:
>>> zip([1, 2, 3], [4, 5, 6])
[(1, 4), (2, 5), (3, 6)]
So if we have a list containing those two lists, the * will unpack the list to form two separate arguments - the equivalent of the above call.
>>> zip(*[[1, 2, 3], [4, 5, 6]])
[(1, 4), (2, 5), (3, 6)]
Maybe with the help of itertools.izip()
itertools.izip(*myList)
To create lists from this iterator do the following:
map(list, itertools.izip(*myList))
Since you mentioned efficiency, I tried these with PyPy and somewhere around 100x1000 they start to be better than zip (on CPython however they are worse):
myList = [[1, 2, 3],[4, 5, 6]]
newList = [[] for _ in xrange(len(myList[0]))]
for innerList in myList:
for elIdx in xrange(len(innerList)):
newList[elIdx].append(innerList[elIdx])
print newList
And my first attempt (which is worse), but still better then zip on PyPy:
newList = [[None for i in xrange(len(myList))] for j in xrange(len(myList[0]))]
for i in xrange(len(myList)):
for j in xrange(len(myList[i])):
newList[j][i] = myList[i][j]
print newList

How to find all possible sequences of elements in a list?

I have a list [2,3,4]. How do I find all possible sequence of elements in the list?
So the output should be:
[2,3,4]
[2,4,3]
[3,2,4]
[3,4,2]
[4,2,3]
[4,3,2]
You can do this easily using itertools.permutations():
>>> from itertools import permutations
>>> list(permutations([2, 3, 4]))
[(2, 3, 4), (2, 4, 3), (3, 2, 4), (3, 4, 2), (4, 2, 3), (4, 3, 2)]
And if for some reason you need lists instead of tuples:
>>> map(list, permutations([2, 3, 4]))
[[2, 3, 4], [2, 4, 3], [3, 2, 4], [3, 4, 2], [4, 2, 3], [4, 3, 2]]
You are looking for permutations, something like this should work:
import itertools
itertools.permutations([2,3,4])
a start of a great lottery program except data would be formated as such
ist(permutations([2, 3, 4],[7,2,5],[8,1,4,9]))
the problem is that the first group is used to create numbers in first column only
the secound is for 2 column and 3rd is for 3rd
the output will be a set of 3 numbers just that the permutation is different
Just so you know:
def unique_perms(elems):
"""returns non-duplicate permutations
if duplicate elements exist in `elems`
"""
from itertools import permutations
return list(set(permutations(elems)))
But if you're doing something like this:
print len(unique_perms(elems))
Then try this:
def fac(n):
"""n!"""
if n == 1: return n
return n * fac(n -1)
def unique_perm_count(elems)
n = len(elems)
return fac(2 * n) / fac(n) ** 2

Categories