Related
This question already has answers here:
python shuffling with a parameter to get the same result
(4 answers)
Closed 1 year ago.
I have a list of values such as:
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
and I need to reproducibly return n random shuffles of this list.
Ideally, I need a function with seed such that f(lst, samples = 2, seed = 1234)
-> return two shuffles of the list lst such as:
[5, 7, 1, 6, 2, 8, 0, 4, 3, 9]
[8, 7, 3, 0, 1, 4, 5, 9, 6, 2]
Repeated execution of this function (with the same seed) would return the same two lists.
This works without numpy:
import sys
import random
some_seed = 123 # change this to get different shuffles
def n_shuffles(lst, n):
r = random.Random(some_seed)
for _ in range(n):
_l = lst[:]
r.shuffle(_l)
yield _l
l = list(range(10))
>>> [*n_shuffles(l, 3)]
[[8, 7, 5, 9, 2, 3, 6, 1, 4, 0], [7, 6, 3, 4, 1, 0, 2, 5, 9, 8], [1, 8, 5, 6, 4, 7, 9, 0, 2, 3]]
>>> [*n_shuffles(l, 3)]
[[8, 7, 5, 9, 2, 3, 6, 1, 4, 0], [7, 6, 3, 4, 1, 0, 2, 5, 9, 8], [1, 8, 5, 6, 4, 7, 9, 0, 2, 3]]
You can use np.copy
import numpy as np
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
def shuffle_list(arr:list,samples:int,seed:int):
np.random.seed(seed)
res = []
for i in range(samples):
arr_copy=np.copy(arr)
np.random.shuffle(arr_copy)
res.append(arr_copy)
return res
#test
print(shuffle_list(lst,2,1234))
output:
[array([7, 2, 9, 1, 0, 8, 4, 5, 6, 3]), array([7, 3, 5, 1, 4, 8, 0, 2, 6, 9])]
Ok, it wasn't an exact duplicate, but the proposed topic has pretty much shown that re-setting the seed() is the key:
import random
def shuffles(l,n):
random.seed(4) # just the same one as in the referred topic
return [random.sample(l,k=len(l)) for i in range(n)]
print(shuffles([1,2,3,4],3))
print("again:")
print(shuffles([1,2,3,4],3))
will generate
[[2, 4, 1, 3], [4, 1, 3, 2], [1, 2, 3, 4]]
again:
[[2, 4, 1, 3], [4, 1, 3, 2], [1, 2, 3, 4]]
I start with a list of integers:
A = [ 1, 2, 5, 3, 4, 6, 7, 8 ]
After a shuffle, I would like some elements (say 3, 4 and 5) to preserve their order of occurrence in A while the rest of the elements are free to be randomly shuffled. Something like:
Outcome #1:
A = [ 5, 2, 3, 1, 8, 4, 6, 7 ]
-or-
Outcome #2:
A = [ 7, 5, 6, 1, 3, 4, 8, 2 ]
-but not-
Outcome #3 (invalid outcome)
A = [7, 4, 6, 1, 3, 5, 8, 2]
Appreciate all suggestions!
Extract the elements you want to maintain relative ordering among, shuffle as normal, then glue the lists back together by randomly picking indexes for the "kept" elements to be inserted. All operations are linear if you use sets for speeding up in operations, which I didn't bother with. Ordering of the keep list matters.
>>> import random
>>> L = [1, 2, 5, 3, 4, 6, 7, 8]
>>> keep = [2, 3, 4]
>>> kept = [L[i] for i in keep][::-1]
>>> unkept = [x for i, x in enumerate(L) if i not in keep]
>>> random.shuffle(unkept)
>>> idxes = random.sample(list(range(len(L))), k=len(keep))
>>> result = [kept.pop() if i in idxes else unkept.pop() for i in range(len(L))]
>>> result
[6, 5, 3, 8, 4, 1, 7, 2]
Random tests:
import random
def shuffle_with_fixed_order(L, keep):
kept = [L[i] for i in keep][::-1]
unkept = [x for i, x in enumerate(L) if i not in keep]
random.shuffle(unkept)
idxes = random.sample(list(range(len(L))), k=len(keep))
return [kept.pop() if i in idxes else unkept.pop() for i in range(len(L))]
if __name__ == "__main__":
for _ in range(5000):
L = list(range(50))
random.shuffle(L)
keep = sorted(random.sample(L, k=20))
shuffled = shuffle_with_fixed_order(L, keep)
new_locs = [shuffled.index(L[i]) for i in keep]
assert all(x < y for x, y in zip(new_locs, new_locs[1:]))
This is one approach using random module
Ex:
import random
A = [ 1, 2, 5, 3, 4, 6, 7, 8 ]
result = A[2:5]
del A[2:5]
while A:
l = len(A)
result.insert(random.randint(0, l), A.pop(random.randrange(l)))
print(result)
Demo
def rand_shfl(lst):
result = lst[2:5]
del lst[2:5]
while A:
l = len(A)
result.insert(random.randint(0, l), A.pop(random.randrange(l)))
return result
for _ in range(10):
A = [ 1, 2, 5, 3, 4, 6, 7, 8 ]
print((rand_shfl(A)))
Output:
[1, 7, 6, 5, 8, 3, 4, 2]
[8, 1, 2, 7, 5, 3, 6, 4]
[8, 6, 7, 2, 1, 5, 3, 4]
[1, 7, 2, 8, 5, 3, 4, 6]
[6, 8, 7, 5, 1, 3, 4, 2]
[7, 2, 5, 6, 1, 3, 4, 8]
[1, 8, 5, 2, 3, 4, 6, 7]
[5, 7, 1, 6, 3, 8, 4, 2]
[1, 5, 7, 3, 2, 4, 8, 6]
[8, 2, 7, 6, 5, 3, 4, 1]
Probably below will work for you
import random
lst=[1, 2, 5, 3, 4, 6, 7, 8]
s_lst = {5:0,3:0,4:0}
idx_lst = [random.randint(0, len(lst)) for i in range(len(s_lst))]
idx_lst.sort()
for i, s in enumerate(s_lst.keys()):
s_lst[s] = idx_lst[i]
lst.remove(s)
random.shuffle(lst)
for k, v in s_lst.items():
lst.insert(v, k)
print(lst)
I work with large data sets in my research.
I need to duplicate an element in a Numpy array. The code below achieves this, but is there a function in Numpy that performs the operation in a more efficient manner?
"""
Example output
>>> (executing file "example.py")
Choose a number between 1 and 10:
2
Choose number of repetitions:
9
Your output array is:
[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>>
"""
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = int(input('Choose the number you want to repeat (1-10):\n'))
repetitions = int(input('Choose number of repetitions:\n'))
output = []
for i in range(len(x)):
if x[i] != y:
output.append(x[i])
else:
for j in range(repetitions):
output.append(x[i])
print('Your output array is:\n', output)
One approach would be to find the index of the element to be repeated with np.searchsorted. Use that index to slice the left and right sides of the array and insert the repeated array in between.
Thus, one solution would be -
idx = np.searchsorted(x,y)
out = np.concatenate(( x[:idx], np.repeat(y, repetitions), x[idx+1:] ))
Let's consider a bit more generic sample case with x as -
x = [2, 4, 5, 6, 7, 8, 9, 10]
Let the number to be repeated is y = 5 and repetitions = 7.
Now, use the proposed codes -
In [57]: idx = np.searchsorted(x,y)
In [58]: idx
Out[58]: 2
In [59]: np.concatenate(( x[:idx], np.repeat(y, repetitions), x[idx+1:] ))
Out[59]: array([ 2, 4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 8, 9, 10])
For the specific case of x always being [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], we would have a more compact/elegant solution, like so -
np.r_[x[:y-1], [y]*repetitions, x[y:]]
There is the numpy.repeat function:
>>> np.repeat(3, 4)
array([3, 3, 3, 3])
>>> x = np.array([[1,2],[3,4]])
>>> np.repeat(x, 2)
array([1, 1, 2, 2, 3, 3, 4, 4])
>>> np.repeat(x, 3, axis=1)
array([[1, 1, 1, 2, 2, 2],
[3, 3, 3, 4, 4, 4]])
>>> np.repeat(x, [1, 2], axis=0)
array([[1, 2],
[3, 4],
[3, 4]])
I am trying to create a function that, for each member of a list, returns the value of that member and the number of values either side of it. The only trick is that it has to "wrap around" when it is at the start or end of the list
For example:
a = [0,1,2,3,4,5,6,7,8,9]
myfunc(a,2) # 2 indicates 2 either side
[8,9,0,1,2]
[9,0,1,2,3]
[0,1,2,3,4]
...
...
[6,7,8,9,0]
[7,8,9,0,1]
I can work out how to do it from index 2 until 7:
def myfunc(vals, rnge):
for i in range(0+rnge, len(vals)-rnge):
print vals[i-rnge:i+rnge+1]
But I can't work out how to handle when it needs to wrap around.
you could try this (extend vals in both directions first). there may be something in collections that allows for this to be done more efficiently:
def myfunc(vals, rnge):
vals_ext = vals[-rnge:] + vals + vals[:rnge]
for i in range(len(vals)):
print( vals_ext[i:i+2*rnge+1] )
output:
[8, 9, 0, 1, 2]
[9, 0, 1, 2, 3]
[0, 1, 2, 3, 4]
[1, 2, 3, 4, 5]
[2, 3, 4, 5, 6]
[3, 4, 5, 6, 7]
[4, 5, 6, 7, 8]
[5, 6, 7, 8, 9]
[6, 7, 8, 9, 0]
[7, 8, 9, 0, 1]
How about:
def myfunc(vals, rnge):
valsX3 = vals*3;
for i in range(len(vals)):
print valsX3[i:i+2*rnge+1]
You could use something like this to avoid the duplication of arrays:
wrapping around slices in Python / numpy
Not sure how it does it internally though.
Here is an alternative approach you might also find useful:
def myfunc(alist, offset):
adeque = collections.deque(a)
adeque.rotate(offset)
for i in xrange(len(alist)):
print list(itertools.islice(adeque, 0, 2*offset+1))
adeque.rotate(-1)
a = [0,1,2,3,4,5,6,7,8,9]
myfunc(a,2) # 2 indicates 2 either side
It makes use of the deque collections object which has an efficient rotate function.
Generate your lists using the numpy element-wise modulo of an (unshifted) array.
You want 5 elements that wrap around 10, so use modulo 10. For example if the list starts with 8:
np.mod(np.arange(8,8+5,1),10) returns [8, 9, 0, 1, 2]
To get all 10 possible lists, evaluate list(np.mod(np.arange(s,s+5,1),10))for each start s=1,2,..,10
Or, in a single list comprehension without resorting to numpy,
[[t%10 for t in range(s,s+5)] for s in range(10)]
returns
[[0, 1, 2, 3, 4],
[1, 2, 3, 4, 5],
[2, 3, 4, 5, 6],
[3, 4, 5, 6, 7],
[4, 5, 6, 7, 8],
[5, 6, 7, 8, 9],
[6, 7, 8, 9, 0],
[7, 8, 9, 0, 1],
[8, 9, 0, 1, 2],
[9, 0, 1, 2, 3]]
Here is the solution :
def defunc(vals, start, rnge):
n = len(vals)
new = []
for i in xrange((start - rnge), (start + rnge)+ 1):
new.append(vals[i%n])
return new
def myfunc(vals, rnge):
n = len(vals)
for i in range(n):
print defunc(vals,i,rnge)
I am not slicing the list to make the logic clearer.
I hope this works
What is the simplest way I can read the nth letter of a matrix?
I thought this would be possible with a simple for loop but so far I haven't had any luck.
The best I can do so far is using a count which is not exactly elegant:
matrix = [[1, 3, 5, 2, 6, 2, 4, 1], [2, 6, 1, 6, 2, 5, 7], [1, 6, 2, 6, 8, 2, 6]]
count = 0
for n in matrix:
print matrix[count][nth]
count += 1
For example:
Read the 0th number of every row: 1, 2, 1.
Read the 4th number of every row: 6, 2, 8.
If your need to do this operation a lot you could transpose your matrix using zip(*matrix)
>>> matrix = [[1, 3, 5, 2, 6, 2, 4, 1], [2, 6, 1, 6, 2, 5, 7], [1, 6, 2, 6, 8, 2, 6]]
>>> matrix_t = zip(*matrix)
>>> matrix_t
[(1, 2, 1), (3, 6, 6), (5, 1, 2), (2, 6, 6), (6, 2, 8), (2, 5, 2), (4, 7, 6)]
>>> matrix_t[0]
(1, 2, 1)
>>> matrix_t[3]
(2, 6, 6)
Here's something that will handle rows of different lengths (as in your example), as well as supporting Python's special interpretation of negative indexes as relative to the end of the sequence (by changing them intolen(s) + n):
NULL = type('NULL', (object,), {'__repr__': lambda self: '<NULL>'})()
def nth_elems(n):
abs_n = abs(n)
return [row[n] if abs_n < len(row) else NULL for row in matrix]
matrix = [[1, 3, 5, 2, 6, 2, 4, 1], [2, 6, 1, 6, 2, 5, 7], [1, 6, 2, 6, 8, 2, 6]]
print nth_elems(0) # [1, 2, 1]
print nth_elems(6) # [4, 7, 6]
print nth_elems(7) # [1, <NULL>, <NULL>]
print nth_elems(-1) # [1, 7, 6]
Maybe this way?
column = [row[0] for row in matrix]
(for the 0th element)
In [1]: matrix = [[1, 3, 5, 2, 6, 2, 4, 1], [2, 6, 1, 6, 2, 5, 7], [1, 6, 2, 6, 8, 2, 6]]
In [2]: nth=0
In [3]: [row[nth] for row in matrix]
Out[3]: [1, 2, 1]
In [4]: nth=4
In [5]: [row[nth] for row in matrix]
Out[5]: [6, 2, 8]
Here is a solution using list comprehension:
[x[0] for x in matrix]
Which is basically, equal to:
for x in matrix:
print x[0]
You can also make it a function:
def getColumn(lst, col):
return [i[col] for i in lst]
Demo:
>>> matrix = [[1, 3, 5, 2, 6, 2, 4, 1], [2, 6, 1, 6, 2, 5, 7], [1, 6, 2, 6, 8, 2, 6]]
>>> def getColumn(lst, col):
return [i[col] for i in lst]
>>> getColumn(matrix, 0)
[1, 2, 1]
>>> getColumn(matrix, 5)
[2, 5, 2]
Hope this helps!
List comprehensions will work well here:
>>> matrix = [[1, 3, 5, 2, 6, 2, 4, 1], [2, 6, 1, 6, 2, 5, 7], [1, 6, 2, 6, 8, 2, 6]]
>>> # Get all the 0th indexes
>>> a = [item[0] for item in matrix]
>>> a
[1, 2, 1]
>>> # Get all the 4th indexes
>>> b = [item[4] for item in matrix]
>>> b
[6, 2, 8]
>>>
Your for loop is likely not doing what you expect. n is not an integer. It is the current row.
I think what you wanted to do was:
for row in matrix:
print row[0], row[4]
This prints,
1 6
2 2
1 8
Also, strictly speaking, matrix is a list of lists. To really have a matrix you might need to use numpy.
Lists in Python are not intended to be used like this. Using list comprehension may cause both memory and CPU issues if the data is sufficiently big. Consider using numpy if this is an issue.
Use zip:
>>> matrix = [[1, 3, 5, 2, 6, 2, 4, 1], [2, 6, 1, 6, 2, 5, 7], [1, 6, 2, 6, 8, 2, 6]]
>>> zip(*matrix)[0]
(1, 2, 1)
>>> zip(*matrix)[4]
(6, 2, 8)