Generalising different for-loop method in Python - python

I have this following code where I am trying to form an array 'opt'. Here, I am taking three possible values of 'pos_set' = [1, 2, 3] and in a similar manner, I can extend this. But, I just want a generalized code for any possible integer value of pos_set.
opt = []
if pos_set == 1:
for j in range(1, n):
opt.append([j])
elif pos_set == 2:
for j in range(1, n):
for k in range(j+1, n):
opt.append([j, k])
elif pos_set == 3:
for j in range(1, n):
for k in range(j+1, n):
for l in range(k+1, n):
opt.append([j, k, l])
For more clarity, I am doing this with the objective of collecting all possibilities if you roll an n-sided die and keep doing this as long as you keep rolling larger values.
For instance, if you roll a sequence 1-2-6-4, in this case after getting a 4 following a larger no. 6, you stop rolling. Similarly, if you roll a sequence 1-2-6-6, in this case you get a repeated 6 so you stop because it is not larger than your previous roll. I am considering cases before the smaller or same number occurs i.e., [1, 2, 6] in both cases.
If you guys can help me, I would be grateful.

You can use the following recursive function:
def f(p, n, i=1):
if p == 0:
return [[]]
return [[j, *l] for j in range(i, n) for l in f(p - 1, n, j + 1)]
so that:
print(f(1, 7))
print(f(2, 7))
print(f(3, 7))
outputs:
[[1], [2], [3], [4], [5], [6]]
[[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [2, 3], [2, 4], [2, 5], [2, 6], [3, 4], [3, 5], [3, 6], [4, 5], [4, 6], [5, 6]]
[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 2, 6], [1, 3, 4], [1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [1, 5, 6], [2, 3, 4], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6], [2, 5, 6], [3, 4, 5], [3, 4, 6], [3, 5, 6], [4, 5, 6]]

Was not sure by how you went about your question were you just concerned for constructing your qpos_set, or did you need help with the code to produce the actual output that will meet your objective.
Here is a code I wrote that will roll n- sided die (I just set it at 100 for demonstration) but this will continue to roll another die and append its value until that number is equal or less and then the previous roll.
I'm going to hold off on breaking down the code unless you needed this portion as well. Let me know!
import random
die = list(range(1, 100))
temp = [0, 1, 2]
winners = []
while temp[1] > temp[0]:
temp[0] = random.randint(1, len(die))
temp[1] = random.randint(1, len(die))
temp[0] = temp[2]
if temp[1] > temp[0]:
winners.append(temp[1])
temp[2] = temp[1]
else:
winners.append(temp[1])
break
print(winners)

There is a built-in solution for this, itertools.combinations:
In [5]: import itertools
In [6]: list(itertools.combinations(range(1,7), 3))
Out[6]:
[(1, 2, 3),
(1, 2, 4),
(1, 2, 5),
(1, 2, 6),
(1, 3, 4),
(1, 3, 5),
(1, 3, 6),
(1, 4, 5),
(1, 4, 6),
(1, 5, 6),
(2, 3, 4),
(2, 3, 5),
(2, 3, 6),
(2, 4, 5),
(2, 4, 6),
(2, 5, 6),
(3, 4, 5),
(3, 4, 6),
(3, 5, 6),
(4, 5, 6)]

Related

Iterating sublists without using the list length

I have a list of things, and I need to apply some processing on successive (and overlapping) groups of 3 elements:
I can do it with:
for i in range(len(things)-2):
process(things[i:i+3])
So for instance:
things=[0, 1, 2, 3, 4, 5, 6, 7]
And I want to process:
[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7]
But is there a clever (but readable) way to do it without explicitly using len(things)?
Yes, what you're looking for is called a sliding/moving window. There are different ways to achieve this but the easiest is to use tee() and islice() functions from itertools. Using this you can define a window() function like below with default window size of 2.
import itertools
def window(iterable, n=2):
iters = itertools.tee(iterable, n)
for i, it in enumerate(iters):
next(itertools.islice(it, i, i), None)
return zip(*iters)
Then you can use it as
>>> things=[0, 1, 2, 3, 4, 5, 6, 7]
>>> list(window(things, n = 3))
[(0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7)]
>>> for elem in window(things, n = 3):
... print(elem)
...
(0, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, 5)
(4, 5, 6)
(5, 6, 7)
Edit: For one time use a more simpler option may be
>>> list(zip(things, things[1:], things[2:]))
[(0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7)]
Another way of doing it could be:
for i in things[0:-2]:
a=things.index(i)
process(things[a:a+3])
Let's try using enumerate, Here len(things[i : i+len_]) == len_ is to drop uneven sized list that get's accumulated at the end iterations.
len_ = 3
[things[i : i+len_] for i, j in enumerate(things) if len(things[i : i+len_]) == len_]
[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7]]
len_ = 4
[things[i : i+len_] for i, j in enumerate(things) if len(things[i : i+len_]) == len_]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]

How to create a list from tuples with sub-tuples?

I have two lists a and b. Then I create all combinations taking all two-length combinations of a plus one each of b:
import itertools as it
a = [1,2,3,4]
b = [5,6]
for i in it.product(it.combinations(a, 2), b):
print (i)
# output:
((1, 2), 5)
((1, 2), 6)
((1, 3), 5)
...
# expected output:
[1, 2, 5]
[1, 2, 6]
[1, 3, 5]
...
How can the tuples be transformed at the stage of the loop operation into lists?
The following comprehensions will work:
>>> [[*x, y] for x, y in it.product(it.combinations(a, 2), b)] # Py3
>>> [list(x) + [y] for x, y in it.product(it.combinations(a, 2), b)] # all Py versions
[[1, 2, 5],
[1, 2, 6],
[1, 3, 5],
[1, 3, 6],
[1, 4, 5],
[1, 4, 6],
[2, 3, 5],
[2, 3, 6],
[2, 4, 5],
[2, 4, 6],
[3, 4, 5],
[3, 4, 6]]
Simplified approach:
a = [1,2,3,4]
b = [5,6]
l = len(a)
print(sorted([a[i], a[i_n], j] for i in range(l) for j in b
for i_n in range(i+1, l) if i < l-1))
The output:
[[1, 2, 5], [1, 2, 6], [1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6], [3, 4, 5], [3, 4, 6]]

Possible combination of a nested list in python

If I have a list of lists and want to find all the possible combination from each different indices, how could I do that?
For example:
list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
I want to find
all_possibility = [[1, 5, 9], [1, 8, 6], [4, 2, 9], [4, 8, 3], [7, 2, 6], [7, 5, 3]]
where
[1,5,9]: 1 is 1st element of [1, 2, 3], 5 is 2nd element of [4, 5, 6], 9 is 3rd element of [7, 8, 9].
[1,8,6]: 1 is 1st element of [1, 2, 3], 8 is 2nd element of [7, 8, 9], 6 is 3rd element of [4, 5, 6].
and so on.
(Edited) Note: I would like the result to be in the same order as the original element of the list. [1, 8, 6] but not [1, 6, 8] because 8 is the 2nd element of [7, 8, 9].
What you're looking for is the Cartesian product, in Python itertools.product:
>>> import itertools
>>> list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> all_possibility = list(itertools.product(*list_of_lists))
>>> print(all_possibility)
[(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 6, 7), (1, 6, 8),
(1, 6, 9), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 6, 7),
(2, 6, 8), (2, 6, 9), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 5, 7), (3, 5, 8), (3, 5, 9),
(3, 6, 7), (3, 6, 8), (3, 6, 9)]
If you want permutations based on the indices rather than the values, you can use itertools.combinations to get the possible indices, then use those indices to get the respective values from the sub-lists, like this:
>>> list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> length = 3
>>> all_indices = list(itertools.permutations(range(length), length))
>>> all_possibility = [[l[i] for l,i in zip(list_of_lists, indices)] for indices in all_indices]
>>> print(all_possibility)
[[1, 5, 9], [1, 6, 8], [2, 4, 9], [2, 6, 7], [3, 4, 8], [3, 5, 7]]
I have to consider the indices as well. For example, (1, 4, 7) is excluded because 1, and 4 both are the 1st element from the list of the lists (from [1, 2, 3] and [4, 5, 6]). And actually (1, 4, 7) all of them are from the first component of the nested list. I need the cases with all the different indices.
So you actually just want to get the possible permutations of a “list selector” for each index in the output, i.e. these are what you are trying to get:
>>> list(itertools.permutations(range(3), 3))
[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
And once you have that, you just need to translate into your list_of_lists where you access each index from the specified sublist:
>>> [[list_of_lists[k][i] for i, k in enumerate(comb)] for comb in itertools.permutations(range(3), 3)]
[[1, 5, 9], [1, 8, 6], [4, 2, 9], [4, 8, 3], [7, 2, 6], [7, 5, 3]]
In a spirit of #poke's approach, here is the cases for number of elements differ than the number of the list in the lists. (Previously, there are 3 elements in individual list where 3 sub-lists in the list).
list_of_lists = [[1, 2], [3, 4], [5, 6], [7, 8]]
We expect every (0, 1) pairs from the list of lists, or
all_possibility = [[1, 4], [1, 6], [1, 8], [3, 2], [3, 6], [3, 8], \
[5, 2], [5, 4], [5, 8], [7, 2], [7, 4], [7, 6]]
The code:
permutation_cases = list(itertools.permutations(range(2), 2))
select_from = list(itertools.combinations(range(len(list_of_lists)), 2))
all_possibility = []
for selecting_index in select_from:
selected = [list_of_lists[i] for i in selecting_index ]
cases = list([selected[k][i] for i, k in enumerate(comb)] for comb in permutation_cases)
for item in cases:
all_possibility.append(item)
print(all_possibility)

Transpose a list

Suppose i have a nested list m = [[2, 3], [4, 7]] and I want to transpose it such that I get [[2, 4], [3, 7]] OR if m can be [[1,2,3],[4,5,6],[7,8,9]] then after passing through the transpose function i should get [[1,4,7],[2,5,8],[3,6,9]]
It should also work for [[1,2,3],[4,5,6]] which returns [[1,4],[2,5],[5,6]]
def transpose(m):
t = make_copy(m) ##[[2, 3], [4, 7]]
if len(t) == 1: ## if [[1]]
return t ##[[1]]
i = 0
while i <= len(t): #0 <= 2
t[0][i] = t[i][0] #t[0][0] = t[0]t[0] ## t[0][1] = t[1][0] so now 3 = 4
i += 1 #1
return t
I thought this should work but it didn't give me the answer I want. Whats wrong ?
Use zip function
print zip(*[[2, 3], [4, 7]])
# [(2, 4), (3, 7)]
So, your transpose function becomes like this
def transpose(m):
return zip(*m)
assert transpose([[2, 3], [4, 7]]) == [(2, 4), (3, 7)]
assert transpose([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) == [
(1, 4, 7), (2, 5, 8), (3, 6, 9)]
Remember, it just returns a list of tuples. They can be used almost interchangeably, except for the facts that the syntaxes for creating them is different and tuples are immutable.
If you really want to get only list of lists, you have throw in a comprehension or a map function, like this
def transpose(m):
return [list(item) in zip(*m)]
# return map(list, zip(*m))
assert transpose([[2, 3], [4, 7]]) == [[2, 4], [3, 7]]
assert transpose([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) == [
[1, 4, 7], [2, 5, 8], [3, 6, 9]]
assert transpose([[1, 2, 3], [4, 5, 6]]) == [[1, 4], [2, 5], [3, 6]]

Zip as a list comprehension

I have a fairly last list of data like this:
data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
I'm trying to zip it so that that I get something like this:
zipped_data = [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
I know I could do that with
l = [(data[0]), (data[1]), (data[2])]
zipped_data = zip(*l)
But I would like to write a list comprehension to do that for any number of items in data. I tried this, but it didn't work.
s = [zip(i) for i in data]
s
[[(1,), (2,), (3,)], [(4,), (5,), (6,)], [(7,), (8,), (9,)]]
Can anyone identify where I've gone wrong here? Thanks.
Try the *:
In [2]: lis=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [3]: zip(*lis)
Out[3]: [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
If you really want to rewrite zip as a list comprehension, then this is how I would do it:
In [25]: data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [26]: [tuple(lis[j] for lis in data) for j in range(min(len(l) for l in data))]
Out[26]: [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
In [27]: data = [[1, 2, 3], [4, 5, 6], [7, 8]]
In [28]: [tuple(lis[j] for lis in data) for j in range(min(len(l) for l in data))]
Out[28]: [(1, 4, 7), (2, 5, 8)]
Though, zip(*data) is definitely a better way to go about this
I would do it with zip but here is done with list comprehension
def zip_lists(lists):
"""
Assuming all lists have the same length
>>> zip_lists([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
>>> zip_lists([[1, 2], [3, 4], [5, 6], [7, 8]])
[[1, 3, 5, 7], [2, 4, 6, 8]]
"""
return [[l[x] for l in lists] for x in range(len(lists[0]))]

Categories

Resources