Related
I'm having difficulty adding to a list iteratively.
Here's a MWE:
# Given a nested list of values, or sets
sets = [[1, 2, 3], [1, 2, 4], [1, 2, 5]]
# add a value to each sublist giving the number of that set in the list.
n_sets = len(sets)
for s in range(n_sets):
(sets[s]).insert(0, s)
# Now repeat those sets reps times
reps = 4
expanded_sets = [item for item in sets for i in range(reps)]
# then assign a repetition number to each occurance of a set.
rep_list = list(range(reps)) * n_sets
for i in range(n_sets * reps):
(expanded_sets[i]).insert(0, rep_list[i])
expanded_sets
which returns
[[3, 2, 1, 0, 0, 1, 2, 3],
[3, 2, 1, 0, 0, 1, 2, 3],
[3, 2, 1, 0, 0, 1, 2, 3],
[3, 2, 1, 0, 0, 1, 2, 3],
[3, 2, 1, 0, 1, 1, 2, 4],
[3, 2, 1, 0, 1, 1, 2, 4],
[3, 2, 1, 0, 1, 1, 2, 4],
[3, 2, 1, 0, 1, 1, 2, 4],
[3, 2, 1, 0, 2, 1, 2, 5],
[3, 2, 1, 0, 2, 1, 2, 5],
[3, 2, 1, 0, 2, 1, 2, 5],
[3, 2, 1, 0, 2, 1, 2, 5]]
instead of the desired
[[0, 0, 1, 2, 3],
[1, 0, 1, 2, 3],
[2, 0, 1, 2, 3],
[3, 0, 1, 2, 3],
[0, 1, 1, 2, 4],
[1, 1, 1, 2, 4],
[2, 1, 1, 2, 4],
[3, 1, 1, 2, 4],
[0, 2, 1, 2, 5],
[1, 2, 1, 2, 5],
[2, 2, 1, 2, 5],
[3, 2, 1, 2, 5]]
Just for fun, the first loop returns an expected value of sets
[[0, 1, 2, 3], [1, 1, 2, 4], [2, 1, 2, 5]]
but after the second loop sets changed to
[[3, 2, 1, 0, 0, 1, 2, 3], [3, 2, 1, 0, 1, 1, 2, 4], [3, 2, 1, 0, 2, 1, 2, 5]]
I suspect the issue has something to do with copies and references. I've tried adding .copy() and slices in various places, but with the indexed sublists I haven't come across a combo that works. I'm running Python 3.10.6.
Thanks for looking!
Per suggested solution, [list(range(reps)) for _ in range(n_sets)] doesn't correctly replace the list(range(reps)) * n_sets, since it gives [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]] instead of
the desired [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]. Do I need to flatten, or is there a syntax with the _ notation that gives me a single list?
Further update . . .
replacing
rep_list = list(range(reps)) * n_sets
with
rep_list_nest = [list(range(reps)) for _ in range(n_sets)]
rep_list = [i for sublist in rep_list_nest for i in sublist]
gives the same undesired result for expanded_sets.
The problem is here:
expanded_sets = [item
for item in sets
for i in range(reps)]
This list now contains the same element of sets four times in a row, followed by the next element repeated four times, and so on.
Creating copies of item fixes the issue:
expanded_sets = [item.copy()
for item in sets
for i in range(reps)]
Try it online
If you want a more pythonic approach, then recognize that the result is a product of two ranges, and your original sets all concatenated together:
from itertools import product
sets = [[1, 2, 3], [1, 2, 4], [1, 2, 5]]
expanded_sets = [[inner_counter, outer_counter] + sets_elem
for sets_elem, outer_counter, inner_counter in product(sets, range(len(sets)), range(4))]
Try it online
I have a list that looks like :
L = [2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1]
I want to check if the sequence 2-1-2 is always respected or I have an outlier somewhere .
Is there a simple way to do this with python ?
from itertools import cycle
L = [2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2]
seq = cycle([2, 1])
for idx, el in enumerate(L):
if not el == next(seq):
raise ValueError(f"Sequence not followed at index {idx}")
What does "2-1-2 is always respected" mean, precisely?
I assume you want to check if Lis an alternating sequence of 2 and 1, starting with 2.
That's easy to check:
def check(L):
if len(L) < 3:
return False
even_indices_all_two = set(L[::2]) == {2}
odd_indices_all_one = set(L[1::2]) == {1}
return even_indices_all_two and odd_indices_all_one and L[-1] == 2
If you don't require L to end with 2, remove the and L[-1] == 2.
If you wonder about the many colons, check this post to understand slicing.
I use the set to check if a sequence contains only one distinct item.
This question already has answers here:
permutations with unique values
(20 answers)
Closed 4 years ago.
I have a problem that I'm trying to solve with itertools product. It is a permutations problem, but the values are not unique, example:
list = [1,1,2,2,3,3]
#result should be
[1,2,1,2,3,3], [2,2,1,1,3,3], .....
I tried using set(itertools.permutations(list))which is the straightforward answer, but the processing time for it is too much for different values and long lists.
I tried also x = itertools.product(set(list),repeat=len(list)) then cleaning out x from the items that don't fulfill the original list value counts (i.e. the generated lists must have two 1s, two 2s, and two 3s), this solution is more quick but this answer throws MemoryError with large numbers because it tries storing the output in the memory then doing the processing on it.
I tried also looping through the product result (i.e for i in itertools.product(set(list),repeat=len(list)) and selecting which iterations to store in and which iterations to throw away) and this solution solves the memory error problem, but is almost as slow as the first one, in which the code could run for hours.
Does anyone have any suggestions how would be a more efficient way to solve this problem?
Here's how I might approach the problem. Take each unique starting value and then all the unique combinations from the values with the starting value removed. It avoids generating any duplicates but only stores the unique values used in each position so worst case an n element list is n-squared in memory.
import itertools
def unique_permutations(values):
if not values:
yield []
return
seen = set()
for idx, first in enumerate(values):
if first in seen:
continue
seen.add(first)
rest = values[:idx] + values[idx+1:]
for perm in unique_permutations(rest):
yield [first] + perm
values = [1, 1, 2, 2, 3, 3]
for perm in unique_permutations(values):
print(perm)
Output:
[1, 1, 2, 2, 3, 3]
[1, 1, 2, 3, 2, 3]
[1, 1, 2, 3, 3, 2]
[1, 1, 3, 2, 2, 3]
[1, 1, 3, 2, 3, 2]
[1, 1, 3, 3, 2, 2]
[1, 2, 1, 2, 3, 3]
[1, 2, 1, 3, 2, 3]
[1, 2, 1, 3, 3, 2]
[1, 2, 2, 1, 3, 3]
[1, 2, 2, 3, 1, 3]
[1, 2, 2, 3, 3, 1]
[1, 2, 3, 1, 2, 3]
[1, 2, 3, 1, 3, 2]
[1, 2, 3, 2, 1, 3]
[1, 2, 3, 2, 3, 1]
[1, 2, 3, 3, 1, 2]
[1, 2, 3, 3, 2, 1]
[1, 3, 1, 2, 2, 3]
[1, 3, 1, 2, 3, 2]
[1, 3, 1, 3, 2, 2]
[1, 3, 2, 1, 2, 3]
[1, 3, 2, 1, 3, 2]
[1, 3, 2, 2, 1, 3]
[1, 3, 2, 2, 3, 1]
[1, 3, 2, 3, 1, 2]
[1, 3, 2, 3, 2, 1]
[1, 3, 3, 1, 2, 2]
[1, 3, 3, 2, 1, 2]
[1, 3, 3, 2, 2, 1]
[2, 1, 1, 2, 3, 3]
[2, 1, 1, 3, 2, 3]
[2, 1, 1, 3, 3, 2]
[2, 1, 2, 1, 3, 3]
[2, 1, 2, 3, 1, 3]
[2, 1, 2, 3, 3, 1]
[2, 1, 3, 1, 2, 3]
[2, 1, 3, 1, 3, 2]
[2, 1, 3, 2, 1, 3]
[2, 1, 3, 2, 3, 1]
[2, 1, 3, 3, 1, 2]
[2, 1, 3, 3, 2, 1]
[2, 2, 1, 1, 3, 3]
[2, 2, 1, 3, 1, 3]
[2, 2, 1, 3, 3, 1]
[2, 2, 3, 1, 1, 3]
[2, 2, 3, 1, 3, 1]
[2, 2, 3, 3, 1, 1]
[2, 3, 1, 1, 2, 3]
[2, 3, 1, 1, 3, 2]
[2, 3, 1, 2, 1, 3]
[2, 3, 1, 2, 3, 1]
[2, 3, 1, 3, 1, 2]
[2, 3, 1, 3, 2, 1]
[2, 3, 2, 1, 1, 3]
[2, 3, 2, 1, 3, 1]
[2, 3, 2, 3, 1, 1]
[2, 3, 3, 1, 1, 2]
[2, 3, 3, 1, 2, 1]
[2, 3, 3, 2, 1, 1]
[3, 1, 1, 2, 2, 3]
[3, 1, 1, 2, 3, 2]
[3, 1, 1, 3, 2, 2]
[3, 1, 2, 1, 2, 3]
[3, 1, 2, 1, 3, 2]
[3, 1, 2, 2, 1, 3]
[3, 1, 2, 2, 3, 1]
[3, 1, 2, 3, 1, 2]
[3, 1, 2, 3, 2, 1]
[3, 1, 3, 1, 2, 2]
[3, 1, 3, 2, 1, 2]
[3, 1, 3, 2, 2, 1]
[3, 2, 1, 1, 2, 3]
[3, 2, 1, 1, 3, 2]
[3, 2, 1, 2, 1, 3]
[3, 2, 1, 2, 3, 1]
[3, 2, 1, 3, 1, 2]
[3, 2, 1, 3, 2, 1]
[3, 2, 2, 1, 1, 3]
[3, 2, 2, 1, 3, 1]
[3, 2, 2, 3, 1, 1]
[3, 2, 3, 1, 1, 2]
[3, 2, 3, 1, 2, 1]
[3, 2, 3, 2, 1, 1]
[3, 3, 1, 1, 2, 2]
[3, 3, 1, 2, 1, 2]
[3, 3, 1, 2, 2, 1]
[3, 3, 2, 1, 1, 2]
[3, 3, 2, 1, 2, 1]
[3, 3, 2, 2, 1, 1]
I have a numpy array like the following:
A = np.array([[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 5, 1, 1, 1],
[1, 1, 1, 1, 3, 3, 1, 1],
[1, 1, 1, 1, 1, 1, 2, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 4, 1, 1]])
I am looking for the minimum indices in each column. I found this using numpy.argmin as follows:
I = np.zeros(A.shape[1], dtype=np.int64)
for j in range(A.shape[1]):
I[j] = np.argmin(A[:, j])
This gives me I = [0, 0, 0, 0, 0, 0, 0, 0]. Since there are ties, I could obtain the following: I = [0, 1, 2, 3, 4, 0, 5, 1], where I break the ties by the index that is least used (from the previous indices).
In more details:
For j=0, we have np.argmin(A[:, 0]) in [0, 1, 2, 3, 4, 5] and, say, we choose np.argmin(A[:, 0]) = 0.
For j=1, we have np.argmin(A[:, 1]) in [0, 1, 2, 3, 4, 5] and we have to choose the minimum index from [1, 2, 3, 4, 5] since these indices are the least used (we already choose np.argmin(A[:, 0]) = 0 for j=0). Say, we choose np.argmin(A[:, 1])=1.
For j=2, we have np.argmin(A[:, 2]) in [0, 1, 2, 3, 4, 5] and we have to choose the minimum index from [2, 3, 4, 5] since these indices are the least used.
We continue in this way...
For j=5, we have np.argmin(A[:, 5]) in [0, 1, 3, 4] and we have to choose the minimum index from [0, 1, 3, 4] since these indices are the least used. Say we choose np.argmin(A[:, 5])=0.
For j=6, we have np.argmin(A[:, 6]) in [0, 1, 2, 4, 5] and we have to choose from [5] since these indices are the least used. We choose np.argmin(A[:, 6])=5.
For j=7, we have np.argmin(A[:, 7]) in [0, 1, 2, 3, 4, 5] and we have to choose from [1, 2, 3, 4, 5] since these indices are the least used. Say we choose np.argmin(A[:, 7])=1.
I hope it is clear. My question is how to find the minimum indices and break ties by the least used indices in Python?
You could use min combined with a dictionary for keeping the counts of each index:
import numpy as np
A = np.array([[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 5, 1, 1, 1],
[1, 1, 1, 1, 3, 3, 1, 1],
[1, 1, 1, 1, 1, 1, 2, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 4, 1, 1]])
counts = {}
I = np.zeros(A.shape[1], dtype=np.int64)
for j in range(A.shape[1]):
_, _, i = min([(v, counts.get(i, 0), i) for i, v in enumerate(A[:, j])])
counts[i] = counts.get(i, 0) + 1
I[j] = i
print(I)
Output
[0 1 2 3 4 0 5 1]
The idea is to create the following key: (value, count of index, index), and then use the normal comparison of tuples, so if the values are equal the one with less counts of the corresponding index will be selected, if both counts are equal get the one with lower index will be selected.
Let's say I have a list of 5 elements x=[a,b,c,d,e] and I want to run a for loop that prints all lists where two of the entries are 1 less than the corresponding entries in the original list.
What is a simple way to do this in Python? Thanks in advance.
Edit: if x=[4,5,6,7,8] I want:
[3,4,6,7,8], [3,5,5,7,8], [3,5,6,6,8] etc.
Something like this:
>>> from itertools import combinations
>>> lis = [0,1,2,3,4]
>>> for x,y in combinations(range(len(lis)),2):
l = lis[:]
l[x] -= 1
l[y] -= 1
print l
...
[-1, 0, 2, 3, 4]
[-1, 1, 1, 3, 4]
[-1, 1, 2, 2, 4]
[-1, 1, 2, 3, 3]
[0, 0, 1, 3, 4]
[0, 0, 2, 2, 4]
[0, 0, 2, 3, 3]
[0, 1, 1, 2, 4]
[0, 1, 1, 3, 3]
[0, 1, 2, 2, 3]
Shorter version:
for x,y in combinations(range(len(lis)),2):
print [item - 1 if i in (x,y) else item for i,item in enumerate(lis)]
...
[-1, 0, 2, 3, 4]
[-1, 1, 1, 3, 4]
[-1, 1, 2, 2, 4]
[-1, 1, 2, 3, 3]
[0, 0, 1, 3, 4]
[0, 0, 2, 2, 4]
[0, 0, 2, 3, 3]
[0, 1, 1, 2, 4]
[0, 1, 1, 3, 3]
[0, 1, 2, 2, 3]
from itertools import combinations
a = [1,2,3,4]
for combination in combinations(range(len(a)),r=2):
print [c-(1 if i in combination else 0) for i,c in enumerate(a)]