zip() isnt working the way it is supposed to - python

guys see the code and the output i got
import itertools as it
ranks = ['A', 'K', 'Q', 'J', '10', '9', '8', '7', '6', '5', '4', '3', '2']
suits = ['H', 'D', 'C', 'S']
cards = it.product(ranks,suits)
l = []
for i in range(5):
l.append(it.islice(cards,2))
print(list(zip(*l)))
[(('A', 'H'), ('A', 'D'), ('A', 'C'), ('A', 'S'), ('K', 'H')), (('K', 'D'), ('K', 'C'), ('K', 'S'), ('Q', 'H'), ('Q', 'D'))]
This is the output i got
but shouldnt the output be like below
[(('A', 'H'), ('A','C') ,('K', 'H'),('K', 'C'),('Q', 'H')) , (('A', 'D'), ('A', 'S'), ('K', 'D'), ('K', 'S') , ('Q', 'D'))] this is the expected output
i dont know why zip function is working like this here , can someone help please

To get the desired result, you need to convert the islice object to list l.append(list(it.islice(cards,2))):
import itertools as it
ranks = ['A', 'K', 'Q', 'J', '10', '9', '8', '7', '6', '5', '4', '3', '2']
suits = ['H', 'D', 'C', 'S']
cards = it.product(ranks,suits)
l = []
for i in range(5):
l.append(list(it.islice(cards,2))) # here
print(list(zip(*l)))
Prints:
[(('A', 'H'), ('A', 'C'), ('K', 'H'), ('K', 'C'), ('Q', 'H')), (('A', 'D'), ('A', 'S'), ('K', 'D'), ('K', 'S'), ('Q', 'D'))]

This is rather difficult topic but the key idea is that islice and zip function are 'lazy', which means they are efficient. (These functions access elements one-by-one and cannot go backwards. In comparison, constructing a whole list in advance is expensive operation)
You have made "l" a list of 5 iterators.
It doesn't imply that first element of "l" would represent the first 2 tuples(1st&2nd) of the product, and second element of "l" would represent the next 2 tuples(3rd&4th) etc. They are just 5 iterators on the product 'cards'.
Now zip function is executed on 5 elements of list "l".
To compute the first zipped element, lazy 5 iterators are sequentially executed.
First iterator(=l[0]) takes the first element of 'cards' (=AH)
Second iterator(=l[1]) takes the second element of 'cards' (=AD)
.. and so on.
That explains your output.
Now here's another interesting idea.
what if you put a print("cards: ", list(card)) line
after the initialization of variable 'cards'? would it change the output of the code?
It's just a print statement so intuitively it shouldn't change the behavior of program apart from printing some line. But it does.
import itertools as it
ranks = ['A', 'K', 'Q', 'J', '10', '9', '8', '7', '6', '5', '4', '3', '2']
suits = ['H', 'D', 'C', 'S']
cards = it.product(ranks,suits)
print("cards: ", list(cards)) ''' comment this line to get your original output,
uncomment to make list(zip(*l)) return [] '''
l = []
for i in range(5):
l.append(it.islice(cards,2))
print(l)
print("zip: ", list(zip(*l)))
why is list(zip(*l)) empty list now?
While printing 'list(cards)' we used the iterator on 'cards' until it reached the StopIteration. This is because we made the whole list of it.
since we used it, the next(iter(cards)) will raise StopIteration error.
Therefore, when elements of list "l" try to iterate over 'cards', they can't because it's already at the end of iteration. (StopIteration)
What we should learn from this is that iterators are very tricky to use, but they are powerful when used correctly. Just like pointers and memory allocations are dangerous but powerful, same goes for iterator and generators. When you reuse iterator of an object over and over(in this case iterator on cards was reused a lot), it is good to make a solid list of the results and save it, rather than leave it as a floating, dangerous iterators.

Related

creating a three permutation list with two value

is there a way to automatically generate a list of permutations with three value when you only give it two ?
E = {'M', 'F'}
permute_k = list(itertools.permutations(E, 3))
like the code above will give me a empty list like this []
but I want a list that contain the two value as three element like this [’MMM’, ’MMF’, ’MFM’, ...]
any suggestion
In this case you want a cartesian product, with a repeat arg
>>> list(itertools.product(E, repeat=3))
[('F', 'F', 'F'), ('F', 'F', 'M'), ('F', 'M', 'F'), ('F', 'M', 'M'), ('M', 'F', 'F'), ('M', 'F', 'M'), ('M', 'M', 'F'), ('M', 'M', 'M')]
and if you want the elements as strings instead of tuples you can str.join
>>> [''.join(i) for i in itertools.product(E, repeat=3)]
['FFF', 'FFM', 'FMF', 'FMM', 'MFF', 'MFM', 'MMF', 'MMM']
It is because itertools.permutations returns the subsequences of the input iterable. If you specify r > len(iterable) you will have an empty list. Your iterable should be 'MMMFFF' here. By the way a set is not an iterable so you have to manipulate list or a string instead.
E = 'MMMFFF' # or ['M', 'M', 'M', 'F', 'F', 'F']
permute_k = list(itertools.permutations(E, 3))
permute_k = [''.join(perm) for perm in permute_k] # to have a list of strings

product of list of list of letter [[a,b],[c,d],[a,n]]

I have list of posiple letter for each letter in a word..and I want to find all possiple words.
for example the input is [[l,b],[e,d],[s,t]] this represent a word of 3 letter wher first letter could be l or b, second letter could be e or d and third letter is s or t. I wont the out put to be the product of these lists [les,let,bet,...and so on]. the list could be any length not only three.
I tried
res = list(map(prod, zip(test_list)))
but I get
[<itertools.product object at 0x0000024F65AEC600>, <itertools.product object at 0x0000024F65AEC640>, <itertools.product object at 0x0000024F65AEC680>, <itertools.product object at 0x0000024F65AEC6C0>]
I tried
word1=list(product(letter[0],letter[1],letter[2]))
it works perfectly but I want the code to accept any length pf list
You don't want to zip the test_list, just pass each element of it as an argument to product using the * operator:
>>> test_list = [['l','b'],['e','d'],['s','t']]
>>> import itertools
>>> list(itertools.product(*test_list))
[('l', 'e', 's'), ('l', 'e', 't'), ('l', 'd', 's'), ('l', 'd', 't'), ('b', 'e', 's'), ('b', 'e', 't'), ('b', 'd', 's'), ('b', 'd', 't')]
If you want the result to be in string form, use join:
>>> [''.join(p) for p in itertools.product(*test_list)]
['les', 'let', 'lds', 'ldt', 'bes', 'bet', 'bds', 'bdt']

Python - sort a list of tuples in decreasing order with respect to a tuple element and in increasing order with respect to another element [duplicate]

First, I have noticed that there are many questions posted on sorting lists of tuples, however after looking over a few posts I did not see any of the questions with this particular format for the list. Apologies in advance then for a potential repeat question, however I think this should be simple.
Let the list of tuples be this:
my_list = [(('G', 'J', 'I'), 1.0), (('E', 'H', 'F'), 1.0), (('F', 'H', 'G'), 0.8889), (('I', 'K', 'J'), 0.8889), (('H', 'I', 'G'), 0.8889), (('H', 'J', 'I'), 0.875)]
Note that each tuple in the list consists of 1: another tuple of length 3 with 3 letters, and 2: a floating point number. My sorting objective is simple then: 1st sort the list of tuples by the floating point number, 2nd break any ties in the floating point number by then sorting by the first letter in the length-3 tuple, 3rd break any ties in that letter by sorting by the 2nd letter in the length-3 tuple. Desired output would then be:
sorted_list = [(('E', 'H', 'F'), 1.0), (('G', 'J', 'I'), 1.0), (('F', 'H', 'G'), 0.8889), (('H', 'I', 'G'), 0.8889), (('I', 'K', 'J'), 0.8889), (('H', 'J', 'I'), 0.875)]
in this particular example, sorting on the 2nd letter to break ties on the first letter did not appear, although it does in my larger dataset.
Thanks!
Here's one way to do it: the first sort is done in reverse on the float, while the ties are broken by sorting on the inner tuple:
srt_lst = sorted(my_list, key=lambda (x, y): (-y, x)) #python 2
print(srt_lst)
# [(('E', 'H', 'F'), 1.0), (('G', 'J', 'I'), 1.0), (('F', 'H', 'G'), 0.8889), (('H', 'I', 'G'), 0.8889), (('I', 'K', 'J'), 0.8889), (('H', 'J', 'I'), 0.875)]
In Python 3, you'll have to index the lambda's single parameter to access the items and use them for sorting:
srt_lst = sorted(my_list, key=lambda tup: (-tup[1], tup[0]))

Looping through a list of strings and tuples and appending unique combinations

I have been working on this looping problem for a bit now. How do I loop through a list containing a single string and tuple, while appending the tuple to the preceding string? For example:
gen = ['A', ('x', 'y'), ('t', 'u'), 'B', ('y', 't'), 'B', ('a', 'z')]
fam = ['A', 'B']
Fortunately fam also contains the single strings imbeded within gen. In the end I would like the following.
result = [('A',('x','y')), ('A', ('t', 'u')), ('B', ('y', 't')), ('B', ('a', 'z'))
Notice that the tuples following a single string (e.g. 'A') are appending to it.
How do I loop through gen so that the tuples are appending with single strings preceeding it? So far, I have something like the following. Which appends all the combinations in the gen, and then some. I foolishly created a duplicate gen, i.e. gen2 to help with looping, to no avail.
gen = ['A', ('x', 'y'), ('t', 'u'), 'B', ('y', 't'), 'B', ('a', 'z')]
fam = ['A', 'B']
gen2 = ['A', ('x', 'y'), ('t', 'u'), 'B', ('y', 't'), 'B', ('a', 'z')]
result = []
for f in fam:
for g in gen:
if len(g) == 2:
for g2 in gen2:
if g2 == f:
result.append((g2,f))
print result
I apologize if my ramble is too confusing. I appreciate any insight.
You can do it in a single loop and without using fam if you keep track of the last string you came across. It only works properly if the first element of gen is a string, though.
gen = ['A', ('x', 'y'), ('t', 'u'), 'B', ('y', 't'), 'B', ('a', 'z')]
result = []
lastStringSeen = None
for i in gen:
if isinstance(i, str):
lastStringSeen = i
else: #must be a tuple
result.append((lastStringSeen, i))
print result
output:
[('A', ('x', 'y')), ('A', ('t', 'u')), ('B', ('y', 't')), ('B', ('a', 'z'))]

permutations of a lists python

So I posted this question here.
permutations of lists python
And the solution works.. but i should have been more careful.
Please take a look at the link above.
What if I dont have a lists explicitly as a,b,c,d
but I have a list of lists.. something like
lists.append(a)
lists.append(b)
and so on.
And in the end all i have is "lists"
This
for item in itertools.product(lists):
print(item)
doesnt work in this case??
Unpacking everything from a list using *:
>>> import itertools
>>> a = ["1"]
>>> b = ["0"]
>>> c = ["a","b","c"]
>>> d = ["d","e","f"]
>>> lists = [a,b,c,d]
>>> for item in itertools.product(*lists):
print item
('1', '0', 'a', 'd')
('1', '0', 'a', 'e')
('1', '0', 'a', 'f')
('1', '0', 'b', 'd')
('1', '0', 'b', 'e')
('1', '0', 'b', 'f')
('1', '0', 'c', 'd')
('1', '0', 'c', 'e')
('1', '0', 'c', 'f')
This just unpacks the list into its elements so it is the same as calling itertools.product(a,b,c,d). If you don't do this the itertools.product interperets it as one item, which is a list of lists, [a,b,c,d] when you want to be finding the product of the four elements inside the list.
#sberry posted this useful link: http://docs.python.org/tutorial/controlflow.html#unpacking-argument-lists

Categories

Resources