Itertools combinations to produce product list - python

Create combinations of item attributes in order.
Hello, I am trying to create all the combinations of a product in a particular order. The actual problem is quite large, so I have simplified it here but with the same concept. First time using itertools.
Say I am selling a t-shirt. It comes in 3 sizes (s,m,l), 3 colours (w,b,g) and 2 fits (slim,baggy). Put together these make the product code, but have to be in the order of size-colour-fit.
So the combinations are:
S-W-SLIM
S-W-BAGGY
S-B-SLIM
S-B-BAGGY
etc.
My program would need to output all the codes, which should be 3x3x2 = 18 combinations.
import itertools
size = ['S','M','L']
colour = ['W','B','G']
fit = ['slim','baggy']
options = itertools.product(size, colour,fit,[3])
print (options)
<itertools.product object at 0x03718E68>
[Finished in 0.1s]
I have had a look at combinations() and permutations() but they only take 2 input arguments?
Unsure where to go from here, TIA.

Convert it to a list:
print(list(options))
Then it will output:
[('S', 'W', 'slim'), ('S', 'W', 'baggy'), ('S', 'B', 'slim'), ('S', 'B', 'baggy'), ('S', 'G', 'slim'), ('S', 'G', 'baggy'), ('M', 'W', 'slim'), ('M', 'W', 'baggy'), ('M', 'B', 'slim'), ('M', 'B', 'baggy'), ('M', 'G', 'slim'), ('M', 'G', 'baggy'), ('L', 'W', 'slim'), ('L', 'W', 'baggy'), ('L', 'B', 'slim'), ('L', 'B', 'baggy'), ('L', 'G', 'slim'), ('L', 'G', 'baggy')]
Also, you can skip the last argument of [3]:
options = itertools.product(size, colour, fit)
It will output the same, you can check the length and it would be 18.
If you need dashed between them:
options = ['-'.join(i) for i in itertools.product(size, colour, fit)]
And the output of:
print(options)
Would be:
['S-W-slim', 'S-W-baggy', 'S-B-slim', 'S-B-baggy', 'S-G-slim', 'S-G-baggy', 'M-W-slim', 'M-W-baggy', 'M-B-slim', 'M-B-baggy', 'M-G-slim', 'M-G-baggy', 'L-W-slim', 'L-W-baggy', 'L-B-slim', 'L-B-baggy', 'L-G-slim', 'L-G-baggy']

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

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]))

How to select a chunk of list between two values in python

I have a list of values like this:
vect_i = ['a','X','c','g','X','t','o','X','q','w','e','r','t','y','u','i','o','p','Y','x','c','v','b','Y','b','n','m','Y','q','a','d','Y',]
my goal would be to select in a smart way only the values that are within the last X and the first Y (notice capital X and Y).
The output should be something like this:
vect_f = ['q','w','e','r','t','y','u','i','o','p','Y','x','c','v','b']
Is there a smart way to do this selection. I have figured out a way to do it by counting the number of Xs and Ys and collect only what is comprehended between the corresponding indexes but this is very bad coding in my opinion.
Could you suggest me a nice way to achieve my goal?
Most of the other answers are either calling index multiple times, or mutating and/or copying the list in some form. I think you can simply use enumerate and get the slice indices for the desired positions in O(n):
ix = iy = 0
for i, v in enumerate(vect_i):
if v == 'X':
ix = i
elif v == 'Y':
iy = i
break
print(vect_i[ix+1:iy])
# ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']
One line:
vect_i[(vect_i.reverse(), len(vect_i) - vect_i.index('X'), vect_i.reverse())[1] : vect_i.index('Y')]
or
vect_i[len(vect_i) - vect_i[::-1].index('X') : vect_i.index('Y')]
vect_i[len(vect_i)-vect_i[::-1].index('X'): vect_i.index('Y')]
retrurn the desired output
that would be my way:
indexOfLastX = len(vect_i) - 1 - vect_i[::-1].index("X")
indexOfFirstY = vect_i.index("Y")
print(vect_i[indexOfLastX + 1:indexOfFirstY])
resulting in "['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']". In your example result is a "Y", what is suprising, when you want to stop at the first "Y"...
Something like this?
def sliceBetween(arr, ch1, ch2):
if ch1 not in arr or ch2 not in arr:
return []
left = len(arr) - arr[::-1].index(ch1) - 1
right = arr.index(ch2)
if left >= right:
return []
return [left+1:right]
Just try with this code:
endIndex = vect1.index('Y')
dum = ''.join(vect1)
startIndex = dum.rfind('X')
print vect1[startIndex+1:endIndex]
get all diffs between every X > Y, "last X and the first Y" are the first item in vect_f
vect_f = []
x = [x for x,o in enumerate(reversed(vect_i)) if o == "X"]
for diff in x:
sub_vect_f = []
for sub_diff in range(len(vect_i)- diff, len(vect_i)):
val = vect_i[sub_diff]
if val!="Y":
sub_vect_f.append(val)
else:
vect_f.append(sub_vect_f)
break
for vect in vect_f:
print (vect)
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']
['t', 'o', 'X', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']
['c', 'g', 'X', 't', 'o', 'X', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']
Bisect search in temporary sorted list will be fast (O(2 log n)). But there is disadvantages - you need to create additional sorted list with original indexes. You can use it if selection need to be made more than once with different chars and original list is not very big:
from bisect import bisect_left
sorted_list_with_idx = sorted((v,i) for i,v in enumerate(vect_i))
# -1 because we need to insert before index of first 'Y' in list
sorted_y_idx = bisect_left(sorted_list_with_idx, ('Y', -1))
# len(vect_i) used as max index to insert to right of last 'X'
# -1 because last 'X' will be before inserted 'new X'
sorted_x_idx = bisect_left(sorted_list_with_idx, ('X', len(vect_i))) - 1
y_idx = sorted_list_with_idx[sorted_y_idx][1]
x_idx = sorted_list_with_idx[sorted_x_idx][1]
>>> sorted_list_with_idx
[('X', 1), ('X', 4), ('X', 7), ('Y', 18), ('Y', 23), ('Y', 27), ('Y', 31),
('a', 0), ('a', 29), ('b', 22), ('b', 24), ('c', 2), ('c', 20), ('d', 30),
('e', 10), ('g', 3), ('i', 15), ('m', 26), ('n', 25), ('o', 6), ('o', 16),
('p', 17), ('q', 8), ('q', 28), ('r', 11), ('t', 5), ('t', 12), ('u', 14),
('v', 21), ('w', 9), ('x', 19), ('y', 13)]
>>> y_idx
18
>>> x_idx
7
>>> vect_i[x_idx+1:y_idx]
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']

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'))]

In Python, is it possible to unpack a list of strings and put a single character from each string into a generator?

I'm using Python 2.7.
Let's say I have a list like so:
string_list = ['hello', 'apple', 'green', 'paint', 'sting']
Where each string in the list is the same length.
I want to create a generator that would be doing something like the following code:
for i in xrange(len(string_list)):
my_gen = (ch for a_string[i] in string_list)
So the first run, my_gen would have 'h', 'a', 'g', 'p', s'. The next run it would have 'e', 'p', 'r', 'a', 't'.
Just use the built-in function zip -
like in
for letters in zip('hello', 'apple', 'green', 'paint', 'sting'):
print letters
zip is a built-in that does just that: combine one element of each iterable in a tuple, for each iteration.
Running the above example, you have:
>>> for letters in zip('hello', 'apple', 'green', 'paint', 'sting'):
... print letters
...
('h', 'a', 'g', 'p', 's')
('e', 'p', 'r', 'a', 't')
('l', 'p', 'e', 'i', 'i')
('l', 'l', 'e', 'n', 'n')
('o', 'e', 'n', 't', 'g')
izip does exactly what you want:
from itertools import izip
for letters in izip(*string_list):
print letters
The * operator unpacks your string_list so that izip sees it as five sequences of characters, instead of just a single list of strings.
Output:
('h', 'a', 'g', 'p', 's')
('e', 'p', 'r', 'a', 't')
('l', 'p', 'e', 'i', 'i')
('l', 'l', 'e', 'n', 'n')
('o', 'e', 'n', 't', 'g')
The built-in zip function works too, but it's not lazy (i.e. it immediately returns a list of all the tuples, instead of generating them one at a time).
The following recipe comes from the itertools documentation:
from itertools import islice, cycle
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
pending = len(iterables)
nexts = cycle(iter(it).next for it in iterables)
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
Besides being very fast, one advantage of this approach is that it works well if the input iterables are of different lengths.
Use the zip function that takes several lists (iterables) and yields tuples of corresponding items:
zip(*string_list)
yields (successively)
[('h', 'a', 'g', 'p', 's'),
('e', 'p', 'r', 'a', 't'),
('l', 'p', 'e', 'i', 'i'),
('l', 'l', 'e', 'n', 'n'),
('o', 'e', 'n', 't', 'g')]
def foo(string_list):
for i in xrange(len(string_list)):
yield (a_string[i] for a_string in string_list)
string_list = ['hello', 'apple', 'green', 'paint', 'sting']
for nth_string_list in foo(string_list):
for ch in nth_string_list:
print ch
val = zip('hello','apple','green','paint','sting')
or zip(*string_list)
print val[0]
output = ('h', 'a', 'g', 'p', 's')
print val[1]
output = ('e', 'p', 'r', 'a', 't')

Categories

Resources