Combinations with limited repeats in Python - python

I know how to get ALL combinations of a list in Python with itertools, but what if I want to limit the amount of repeats?
So, if I have [1, 2, 3, 4, 5]
But I want to limit combinations to only 3 repeats of each item (with a fixed length of the final list, say 10):
[1, 1, 1, 2, 3, 3, 5, 5, 5, 4]
[1, 2, 3, 3, 3, 4, 5, 5, 4, 4]
[4, 4, 1, 1, 1, 5, 2, 2, 2, 3]
and so on. How do I do this?

This would work:
import random
L = [1, 2, 3, 4, 5]
L3 = L * 3
random.shuffle(L3)
L3[:10]

I don't know if there is a more elegant way but that seems to work:
from itertools import combinations_with_replacement
a = [1, 2, 3, 4, 5] # can contain letters as well
all_combinations = combinations_with_replacement(a, 10)
filtered_results = []
for current in all_combinations:
throw_away = False
for item in current:
if current.count(item) > 3:
throw_away = True
break
if not throw_away:
filtered_results.append( current )
for j in filtered_results:
print(j)

You could do it the following way: Use [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5] and use itertools.combination(the list from above, 10). That would mean that each element occurs at most three times in a resulting list.
Example with smaller list:
Edited to ensure every combination occurs exactly once, the combinations occur in random order and, finally, every combination has a random order.
Also incorporated the idea of Mike Müller to just multiply the list by 3.
import itertools
import random
combinations = set()
for c in itertools.combinations(sorted([1, 2, 3] * 3), 5):
combinations.add(c)
shuffledCombinations = list(combinations)
random.shuffle(shuffledCombinations)
for combination in shuffledCombinations:
copiedCombination = list(combination)
random.shuffle(copiedCombination)
print copiedCombination
which will give:
[2, 1, 3, 2, 2]
[2, 2, 1, 1, 2]
[3, 3, 3, 1, 2]
[2, 2, 3, 2, 3]
[1, 2, 3, 1, 2]
[1, 1, 1, 2, 3]
[2, 1, 1, 1, 2]
[3, 3, 1, 1, 1]
[1, 3, 3, 3, 1]
[3, 2, 3, 2, 3]
[3, 2, 1, 2, 3]
[1, 1, 2, 3, 3]

Related

How can I select a specific number in a list with duplicates without removing the previous same values?

I have the following list in python3:
a = [1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 1, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3]
I want to have something like this as an output:
b = [1, 2, 3, 1, 2, 4, 3]
How can I do that? I have tried a = set(a) and other methods for getting rid of duplicates. But they remove all duplicates.
If you are willing to use a module, you can use itertools.groupby:
from itertools import groupby
a = [1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 1, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3]
output = [k for k, _ in groupby(a)]
print(output) # [1, 2, 3, 1, 2, 4, 3]
This would work:
a = [1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 1, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3]
b = [a[0]]
for k in a:
if k!=b[-1]:
b.append(k)
print(b)
If you comfortable with C, you may like this style:
a = [1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 1, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3]
b=len(a)
i=0
last_one=None
while i<b:
if a[i]==last_one:
del a[i]
b-=1
continue
else:
last_one=a[i]
i+=1
print(a)

Detect all duplicates from second list based on the numbers in the first list

I have 2 lists, for example:
list1 = [1, 2, 2, 3, 3, 4]
list2 = [1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5]
I want to get a list 3 which will include all numbers from list2 matching the numbers from list1:
list3 = [1, 1, 2, 2, 3, 3, 4, 4, 4]
I was trying to use 2 while loops to go through list1 and list2 and match numbers, but it gives me wrong number of values:
listO = [1, 2, 2, 3, 3, 4]
listX = [1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5]
x_temp = []
a = 0
b = 0
while a < len(listO):
while b < len(listX):
if listO[a] == listX[b]:
x_temp.append(listX[b])
print("Outlier a:" , listO[a] )
listX.remove(listX[b])
b+=1
b=0
a+=1
list3 = [1, 2, 2, 3, 3, 4, 4]
Is there any way to do it using loops?
Please do NOT provide solution using set() it doesn`t work for my lists!
With a for loop:
listO = [1, 2, 2, 3, 3, 4]
listX = [1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5]
x_temp = []
for x in listX:
if x in listO:
x_temp.append(x)
# [1, 1, 2, 2, 3, 3, 4, 4, 4]
You can use a list comprehension for this.
list1 = [1, 2, 2, 3, 3, 4]
list2 = [1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5]
list3 = [i for i in list2 if i in list1]
# [1, 1, 2, 2, 3, 3, 4, 4, 4]

Form groups in a list based on condition

(Edited based on feedbacks)
I've got a list like this:
my_list = [1,2,3,1,2,4,1,3,5,1,4,6,1,4,7]
That I'm struggling to turn into that:
result = [[1,2,3,1,2,4],[1,3,5],[1,4,6,1,4,7]]
I want to group my_list elements in sublists of 3 elements unless my_list[i] = my_list[i+3] in this case I want to merge those in bigger sublists.
Here is what I've tried:
result = []
for i in range(1,len(my_list),3):
try:
print(my_list[i],my_list[i+3])
if my_list[i] == my_list[i+3]:
result.extend(my_list[i-1:i+5])
else:
result.append(my_list[i-1:i+2])
FWIW, the description of your logic isn't quite clear. However, if I understand your code correctly, I think this is at least something in the correct direction:
def stepper(my_list, step, bigger_step):
res = []
idx = 0
while idx <= len(my_list)-1:
if idx + step > len(my_list)-1:
# Remove this append if you don't want the "leftovers"
res.append(my_list[idx:])
break
if my_list[idx] != my_list[idx+step]:
res.append(my_list[idx:idx+step])
idx += step
else:
res.append(my_list[idx:idx+bigger_step])
idx += bigger_step
return res
my_list = [1,2,3,1,2,4,1,3,5,1,3,6,1,2,7]
print(stepper(my_list, step=3, bigger_step=6)) # Output: [[1, 2, 3, 1, 2, 4], [1, 3, 5, 1, 3, 6], [1, 2, 7]]
Note that the above output is different from your given example, because of your given logic that you've provided makes the second sub-list extended as well as the first.
Using the above code, we can check the results if we change bigger_step easily with a for-loop:
for big in range(4, 10):
print(f"Step: 3, Bigger_Step: {big}, Result:{stepper(my_list, step=3, bigger_step=big)}")
Output:
Step: 3, Bigger_Step: 4, Result:[[1, 2, 3, 1], [2, 4, 1], [3, 5, 1, 3], [6, 1, 2], [7]]
Step: 3, Bigger_Step: 5, Result:[[1, 2, 3, 1, 2], [4, 1, 3], [5, 1, 3], [6, 1, 2], [7]]
Step: 3, Bigger_Step: 6, Result:[[1, 2, 3, 1, 2, 4], [1, 3, 5, 1, 3, 6], [1, 2, 7]]
Step: 3, Bigger_Step: 7, Result:[[1, 2, 3, 1, 2, 4, 1], [3, 5, 1, 3, 6, 1, 2], [7]]
Step: 3, Bigger_Step: 8, Result:[[1, 2, 3, 1, 2, 4, 1, 3], [5, 1, 3], [6, 1, 2], [7]]
Step: 3, Bigger_Step: 9, Result:[[1, 2, 3, 1, 2, 4, 1, 3, 5], [1, 3, 6, 1, 2, 7]]

recursively split list into sub lists based on first element length

I am breaking my head around this small issue which I am sure can (and should) be solved recursively.
# split list in sublists based on length of first element.
list = [3, 1, 2, 3, 4, 1, 2, 3, 4]
#* #*
# *number of elements of the sublist
It is better shown than explained, the above should result to:
[[1, 2, 3], [1, 2, 3, 4]]
The lists I am processing always respect this logic, the first element is always the length of the following n elements.
EDIT:
Based on some of the suggestions, I simply added a yield to get it done lazily.
def split(ls):
"""
func that given a list extracts sub lists with the length indicated by the first element
[2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4] => [[1, 2], [1, 2, 3], [1, 2, 3, 4]]
"""
res = []
while ls:
dim = ls[0]
yield ls[1:dim + 1]
ls = ls[dim + 1:]
return res
>>> list(split([2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4]))
[[1, 2], [1, 2, 3], [1, 2, 3, 4]]
Simple slicing will do:
>>> a = [3, 1, 2, 3, 4, 1, 2, 3, 4]
>>> c = []
>>> while len(a) :
... b = a[0]
... c.append( a[1:b+1] )
... a = a[b+1:]
...
>>> c
[[1, 2, 3], [1, 2, 3, 4]]
Here is a way to achieve what you want:
numbers = [3, 1, 2, 3, 4, 1, 2, 3, 4]
result = []
def split_list(list_):
first = list_.pop(0)
result.append(list_[:first])
if len(list_[first:]) > 0:
split_list(list_[first:])
split_list(numbers)
print(result)
You can use itertools.islice here.
>>> from itertools import islice
>>> lst = [3, 1, 2, 3, 4, 1, 2, 3, 4]
>>> def chunks(lst):
... t=iter(lst)
... c=next(t,None)
... while c:
... yield list(islice(t,None,c))
... c=next(t,None)
...
>>> list(chunks(lst))
[[1, 2, 3], [1, 2, 3, 4]]
I've edited my answer as inspired by someone else's in this thread. This doesn't consume the original array and uses recursion instead of looping.
numbers = [3, 1, 2, 3, 4, 1, 2, 3, 4, 3,1,1,1,1]
def do_take(numbers: []):
result = []
to_take = numbers[0]
result.append(numbers[1:to_take + 1])
if len(numbers) > to_take:
result.append(do_take(numbers[to_take + 1:]))
return result
print(do_take(numbers))
print(numbers)
Results in the following output:
# python /tmp/breakup.py
[[1, 2, 3], [[1, 2, 3, 4], [[1, 1, 1], [[]]]]]
[3, 1, 2, 3, 4, 1, 2, 3, 4, 3, 1, 1, 1, 1]

function that replaces consecutive duplicate elements of list with single element

What I tried :
def compress(l):
i = 0
while i < len(l)-1:
if l[i] == l[i+1]:
del l[i]
else:
i = i+1
l = [1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5]
compress(l)
I do not know many functions in python yet as I have just started so I would like to do this the basic way i.e using for and while loops and some list methods.
What am I doing wrong?
Any other methods
Another one I tried what's wrong in this :
def compress(l):
for i in l:
if l[i] == l[i+1] and i != (len(l) - 1):
l.pop(l[i])
print(l)
l = [1,1,1,1,2,2,2,2,2,2,3,3,3,4,5,6,7,8]
compress(l)
which gives me the output :
[1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8]
[1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8]
[1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8]
[1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8]
[1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8]
[1, 1, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8]
[1, 1, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8]
[1, 1, 2, 3, 3, 3, 4, 5, 6, 7, 8]
[1, 1, 2, 3, 3, 3, 4, 5, 6, 7, 8]
[1, 1, 2, 3, 3, 3, 4, 5, 6, 7, 8]
[1, 1, 2, 3, 3, 3, 4, 5, 6, 7, 8]
You don't appear to be doing anything wrong(a) with your first attempt, other than the fact you're not printing the compressed list. That can be fixed by adding this as the last line:
print(l)
At that point, you should see:
[1, 2, 3, 4, 5]
like I do.
Your second attempt is problematic - it's a common occurrence when you modify a list that you're iterating over. Because the iteration effectively examines based on the index, inserting items before the current point can result in items being processed twice. Additionally, deleting items at or before the current point can result in items not being processed at all.
That latter case is what's happening in your second attempt.
(a) You could probably choose more descriptive names for your variables than l but that's just a preference of mine.
Since you are asking for other methods, one should point out that repeated removal from a list has bad performance, as all the tail elements have to be shifted for each removal. Building the compressed list from scratch is less expensive and slice assignment allows you to mutate the original list. Using basic loops and list methods, I would do:
def compress(l):
new_l = l[:1]
for x in l:
if x != new_l[-1]:
new_l.append(x)
l[:] = new_l
For a one-line alternative using some more advanced means (itertools.groupby), you can do:
from itertools import groupby
def compress(l):
l[:] = [k for k, _ in groupby(l)]
You don't need to try so hard to delete duplicate elements in a list:
print(list(set(l)))
this will delete all the duplicate elements in the list.

Categories

Resources