No repeats in a for loop with random.sample [duplicate] - python

This question already has answers here:
Mutually exclusive random sampling from a list
(3 answers)
Closed 3 years ago.
I have the following:
list1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
for i in range(3):
print(random.sample(list1, k=1))
print(random.sample(list1, k=1))
print(random.sample(list1, k=1))
and am looking for something like:
['a', 'g', 'i']
['c', 'h', 'b']
['k', 'd', 'e']
The idea being to have no repeats anywhere.
I understand that I could slice the list if not using a loop, but am unsure of how to go about it with the loop involved.

Just shuffle it and take subsequent slices:
list1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
random.shuffle(list1)
N = 3
for i in range(0, len(list1)-N+1, N):
print(list1[i:i+N])
#['c', 'b', 'd']
#['i', 'e', 'j']
#['h', 'g', 'f']

Shuffle the list then break it apart:
import random
list1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
random.shuffle(list1)
for i in range(0, len(list1), 3):
print(list1[i:i+3])

You could also use you're original sample function and slice the list using list comprehensions:
list1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
output = random.sample(list1, k=9)
output = [output[i:i+3] for i in range(0,9,3)]

Input:
import random
list1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
for i in range(3):
print([list1.pop(random.choice(range(len(list1)))) for i in range(3)])
Output:
['j', 'g', 'b']
['f', 'c', 'h']
['e', 'k', 'i']

>>> list1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
>>> mylist = random.sample(list1, k=9)
>>> print([mylist[i:i+3] for i in range(0, 9, 3)])
output
[['e', 'b', 'i'], ['k', 'h', 'd'], ['j', 'f', 'a']]

Related

Generating distinct partly-unordered permutations in Python

Given a list of length 2n, say ["a", "b", "c", "d", "e", "f"] or ["a", "a", "b", "b", "c", "d"] (elements in the list don't necessarily have to be unique), I'd like to generate all the possible distinct permutations of that list while taking into account that the order in which the element 2k and the element 2k+1 appear doesn't matter. That means that
["a", "b", "c", "d", "e", "f"] and ["b", "a", "c", "d", "e", "f"] are the same permutation, but
["a", "b", "c", "d", "e", "f"] and ["a", "c", "b", "d", "e", "f"] are not.
For example, from the list ["a", "b", "c", "d"], the code I need would generate this sequence:
["a", "b", "c", "d"], ["a", "c", "b", "d"], ["a", "d", "b", "c"], ["b", "c", "a", "d"], ["b", "d", "a", "c"], ["c", "d", "a", "b"]
I know it's possible to do that by generating all the permutations and keeping one of each of those that are equivalent to each other, but that's not a very efficient way to proceed, especially with larger sets. Is there a more efficient way to do that?
I wrote this code, but it's highly inefficient (keep in mind that I need to use lists with a length of up to 14):
from itertools import permutations
list_letters = ["a", "b", "c", "d"]
n = int(len(list_letters) / 2)
set_distinct_perm = set()
for perm in permutations(list_letters):
perm = list(perm)
for i in range(n):
perm[2*i:2*i + 2] = sorted(perm[2*i:2*i+2])
perm = tuple(perm)
set_distinct_perm.add(perm)
print(set_distinct_perm)
How about the following, where we take permutations of each [2k, 2k+1] (inclusive) subsequence and then take the product of those permutations:
from itertools import product, permutations
def equivalents(lst):
perms = product(*({*permutations(lst[i:i+2])} for i in range(0, len(lst), 2)))
return [[x for tupl in perm for x in tupl] for perm in perms] # flattening inner part
print(*equivalents('abcdef'))
# ['a', 'b', 'c', 'd', 'f', 'e'] ['b', 'a', 'c', 'd', 'f', 'e']
# ['a', 'b', 'c', 'd', 'e', 'f'] ['a', 'b', 'd', 'c', 'f', 'e']
# ['a', 'b', 'd', 'c', 'e', 'f'] ['b', 'a', 'd', 'c', 'e', 'f']
# ['b', 'a', 'd', 'c', 'f', 'e'] ['b', 'a', 'c', 'd', 'e', 'f']
print(*equivalents('aabbef'))
# ['a', 'a', 'b', 'b', 'f', 'e'] ['a', 'a', 'b', 'b', 'e', 'f']

Python: looping through a list at certain indices (containing certain string)

So I want to loop through a list starting at string "B" to the end or until a new "B" is reached and so on till the end of the list.
l=["A", "B", "C", "D", "B", "M", "C", "T", "B", "g", "do"]
index = [idx for idx, s in enumerate(l) if 'B' in s]
print(index)
I want to loop through the list so I get these outputs but without hard coding the indices:
l[1:4]=['B', 'C', 'D']
l[4:8]=['B', 'M', 'C', 'T']
l[8:]=['B', 'g', 'do']
If just one index has that string then
l=["A", "B", "C", "D"]
index = [idx for idx, s in enumerate(l) if 'B' in s]
print(index)
l[1:]=["B", "C", "D"]
Taking your code and using a for loop:
l = ["A", "B", "C", "D", "B", "M", "C", "T", "B", "g", "do"]
index = [idx for idx, s in enumerate(l) if 'B' in s]
out_of_bounds_index = len(index)
result = []
for i in range(out_of_bounds_index):
if i+1 < out_of_bounds_index:
result.append(l[index[i]:index[i+1]])
else:
result.append(l[index[i]:])
print(result)
Or a list comprehension:
l = ["A", "B", "C", "D", "B", "M", "C", "T", "B", "g", "do"]
index = [idx for idx, s in enumerate(l) if 'B' in s]
out_of_bounds_index = len(index)
result = [l[index[i]:index[i+1]]
if i+1 < out_of_bounds_index
else l[index[i]:]
for i in range(out_of_bounds_index)]
print(result)
Output:
[['B', 'C', 'D'], ['B', 'M', 'C', 'T'], ['B', 'g', 'do']]
Try this:
def get_lists(l):
indices = [i for i, x in enumerate(l) if x == "B"]
if len(indices) == 0:
return []
elif len(indices) == 1:
return [[l[indices[0]]]]
else:
result = list()
for i in range(len(indices)):
if indices[i] == indices[-1]:
result.append(l[indices[i]:])
else:
result.append(l[indices[i]:indices[i+1]])
return result
k = ["A", "B", "C", "D", "B", "M", "C", "T", "B", "g", "do"]
print(get_lists(k))
Output:
[['B', 'C', 'D'], ['B', 'M', 'C', 'T'], ['B', 'g', 'do']]
l = ["A", "B", "C", "D", "B", "M", "C", "T", "B", "g", "do"]
#note that an extra 'B' is added to the list: l+['B']
indexOfB = [i for i, x in enumerate(l+['B']) if x == 'B']
Blists = [l[i[0]:i[1]] for i in zip(indexOfB, indexOfB[1:])]
[print(x) for x in Blists]

How to append element from a multi-dimensional array to each element of another multi-dimensional array

I am trying to append elements from a multi-dimensional list into a 2nd multi-dimensional list. Here is the code I wrote -
my_list = [
[["b", ["a"], "c"], ["d", ["a", "b"], "e"]],
[["j", ["a", "f"]], ["q"]]
]
ref_list = [[["q", "w", "t"], ["y", "u"]], [["s"], ["p", "k", "l"]]]
for a, b in zip(my_list, ref_list):
for i, subl in enumerate(a):
for ii, v in enumerate(subl):
if isinstance(v, list):
subl[ii] = b[i] + subl[ii]
break
print(my_list)
The output I am getting is -
[
[["b", ["q", "w", "t", "a"], "c"], ["d", ["y", "u", "a", "b"], "e"]],
[["j", ["s", "a", "f"]], ["p", "k", "l", "q"]]
]
It adds elements of 2nd list to the beginneing of 1st list. The output I am trying to get is of sort -
[
[["b", ["q", "a", "w", "a", "t", "a"], "c"], ["d", ["y", "a", "b", "u", "a", "b"], "e"]],
[["j", ["s", "a", "f"]], ["p", "q", "k", "q", "l", "q"]]
]
I want to add inner_most element of my_list after each element of ref_list instead of just adding it once in the end.
Slight change to your code:
for a, b in zip(my_list, ref_list):
for i, subl in enumerate(a):
for ii, v in enumerate(subl):
if isinstance(v, list):
result = list()
for element in b[i]:
result += [element]+subl[ii]
subl[ii] = result
break
else:
result = list()
for element in b[i]:
result += [element]+a[i]
a[i] = result
>>> my_list
[[['b', ['q', 'a', 'w', 'a', 't', 'a'], 'c'],
['d', ['y', 'a', 'b', 'u', 'a', 'b'], 'e']],
[['j', ['s', 'a', 'f']], ['p', 'q', 'k', 'q', 'l', 'q']]]
my_list = [
[["b", ["a"], "c"], ["d", ["a", "b"], "e"]],
[["j", ["a", "f"]], ["q"]]
]
ref_list = [[["q", "w", "t"], ["y", "u"]], [["s"], ["p", "k", "l"]]]
for a, b in zip(my_list, ref_list):
for i, subl in enumerate(a):
for ii, v in enumerate(subl):
if isinstance(v, list):
new_subl=subl[ii]
subl[ii]=[]
for j in b[i]:
subl[ii] +=[j] + new_subl
break

How to append elements from a multi-dimensional list into 2nd multi-dimensional list

I am trying to append elements from a multi-dimensional list into a 2nd multi-dimensional list. Here is the code I wrote -
my_list = [[['b', ['a'], 'c'], ['d', ['a', 'b'], 'e']],
[['j', ['a', 'f']], ['q', 't']]]
ref_list = [[['q', 'w', 't'], ['y', 'u']],
[['s', 'o'], ['p', 'k', 'l']]]
newlist = []
for num, i in enumerate(my_list):
small_list = []
for num_1, j in enumerate(i):
semilist = []
for k in j:
if isinstance(k, list):
onelist = []
for a in k:
for ii in ref_list[num][num_1]:
onelist.append(ii)
semilist.append(onelist)
else:
semilist.append(k)
small_list.append(semilist)
newlist.append(small_list)
print(newlist)
The output I am getting is -
[[['b', ['q', 'w', 't'], 'c'], ['d', ['y', 'u', 'y', 'u'], 'e']], [['j', ['s', 'o', 's', 'o']], ['q', 't']]]
The output I am looking for is of sort -
[[['b', ['q', 'w', 't'], 'c'], ['d', ['y', 'u'], 'e']], [['j', ['s', 'o']], ['p', 'k', 'l']]]
I want to replace elements of innermost list of "my_list" with the elements of the "ref_list".
Try:
ref_list_flat = [l for sublist in ref_list for l in sublist]
for i, l1 in enumerate(my_list):
for j, l2 in enumerate(l1):
#check if this is the innermost list
if not any(isinstance(l3, list) for l3 in l2):
my_list[i][j] = ref_list_flat.pop(0)
else:
for k, l3 in enumerate(l2):
if isinstance(l3, list):
my_list[i][j][k] = ref_list_flat.pop(0)
>>> my_list
[[['b', ['q', 'w', 't'], 'c'], ['d', ['y', 'u'], 'e']],
[['j', ['s', 'o']], ['p', 'k', 'l']]]
Try:
my_list = [
[["b", ["a"], "c"], ["d", ["a", "b"], "e"]],
[["j", ["a", "f"]], ["q", "t"]],
]
ref_list = [[["q", "w", "t"], ["y", "u"]], [["s", "o"], ["p", "k", "l"]]]
for a, b in zip(my_list, ref_list):
for i, subl in enumerate(a):
for ii, v in enumerate(subl):
if isinstance(v, list):
subl[ii] = b[i]
break
else:
a[i] = b[i]
print(my_list)
Prints:
[
[["b", ["q", "w", "t"], "c"], ["d", ["y", "u"], "e"]],
[["j", ["s", "o"]], ["p", "k", "l"]],
]
EDIT: To append elements from 2nd list instead replacing:
my_list = [
[["b", ["a"], "c"], ["d", ["a", "b"], "e"]],
[["j", ["a", "f"]], ["q", "t"]],
]
ref_list = [[["q", "w", "t"], ["y", "u"]], [["s", "o"], ["p", "k", "l"]]]
for a, b in zip(my_list, ref_list):
for i, subl in enumerate(a):
for ii, v in enumerate(subl):
if isinstance(v, list):
subl[ii].extend(b[i])
break
else:
a[i].extend(b[i])
print(my_list)
Prints:
[
[["b", ["a", "q", "w", "t"], "c"], ["d", ["a", "b", "y", "u"], "e"]],
[["j", ["a", "f", "s", "o"]], ["q", "t", "p", "k", "l"]],
]
EDIT2: To add elements of 2nd list at the beginning of first list:
my_list = [
[["b", ["a"], "c"], ["d", ["a", "b"], "e"]],
[["j", ["a", "f"]], ["q", "t"]],
]
ref_list = [[["q", "w", "t"], ["y", "u"]], [["s", "o"], ["p", "k", "l"]]]
for a, b in zip(my_list, ref_list):
for i, subl in enumerate(a):
for ii, v in enumerate(subl):
if isinstance(v, list):
subl[ii] = b[i] + subl[ii]
break
else:
a[i] = b[i] + a[i]
print(my_list)
Prints:
[
[["b", ["q", "w", "t", "a"], "c"], ["d", ["y", "u", "a", "b"], "e"]],
[["j", ["s", "o", "a", "f"]], ["p", "k", "l", "q", "t"]],
]

Filter 2 lists over one pass (log(N) complexity)

I have two lists similar to these (equal length and populated with string values):
a = ["W", "", "X", "", "Y", "Z"]
b = ["a", "b", "c", "d", "e", "f"]
I want to remove the elements from list a, if the element is an empty string. In addition, I want to remove corresponding elements from the b list (with same index). So the desired resulting lists for the example above would be:
a = ["W", "X", "Y", "Z"]
b = ["a", "c", "e", "f"]
I can do this with a couple of for loops but I wonder if it can be done in a more efficient way (list comprehensions)? The reason for this is that I'm working with very large lists (~ 4.5 million elements) and efficiency is critical.
You could use zip() to transform your data twice:
>>> a, b = zip(*((av, bv) for av, bv in zip(a,b) if av))
>>> a
('W', 'X', 'Y', 'Z')
>>> b
('a', 'c', 'e', 'f')
a = ["W", "", "X", "", "Y", "Z"]
b = ["a", "b", "c", "d", "e", "f"]
aNew,bNew = zip(*[(a[i], b[i]) for i in range(len(a)) if len(a[i]) > 0])
print(aNew)
#('W', 'X', 'Y', 'Z')
print(bNew)
#('a', 'c', 'e', 'f')

Categories

Resources