916

How do I shuffle a list of objects? I tried random.shuffle:

import random

b = [object(), object()]

print(random.shuffle(b))

But it outputs:

None
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
utdiscant
  • 11,128
  • 8
  • 31
  • 40
  • 7
    Can you give an example how it fails? random.shuffle should work invariant to the type of the objects in the list. – bayer Jun 10 '09 at 17:01
  • 156
    As stated below, random.shuffle doesn't return a new shuffled list; it shuffles the list in place. So you shouldn't say "print random.shuffle(b)" and should instead do the shuffle on one line and print b on the next line. – Eli Courtwright Jun 10 '09 at 17:09
  • 3
    is there an option that doesn't mutate the original array but return a new shuffled array? – Charlie Parker Mar 29 '17 at 17:57
  • 1
    @Charlie: No, there's no `shuffle()` options for that. Just use `random.sample(b, len(b))` instead. – martineau Jun 19 '17 at 17:26
  • Why did you try to print the output of `shuffle`? It should be `None` as it is shuffling the array in place – SomethingSomething Mar 18 '19 at 14:28
  • There are two separate issues here: the how-to question (for which a proper canonical should **only** ask that question, and which OP already knew how to solve), and the "why" question of the unexpected result from the `print`. The latter is a **general** issue that a) is separate from knowing about `random.shuffle` and b) affects many other standard library (and popular third-party) interfaces, and reflects a general Python design principle that is best understood with a separate answer. As such, this version of the question is not properly focused; I have linked duplicates for each aspect. – Karl Knechtel Apr 19 '23 at 09:26

26 Answers26

1449

random.shuffle should work. Here's an example, where the objects are lists:

from random import shuffle

x = [[i] for i in range(10)]
shuffle(x)
print(x)

# print(x)  gives  [[9], [2], [7], [0], [4], [5], [3], [1], [8], [6]]

Note that shuffle works in place, and returns None.

More generally in Python, mutable objects can be passed into functions, and when a function mutates those objects, the standard is to return None (rather than, say, the mutated object).

tom10
  • 67,082
  • 10
  • 127
  • 137
  • Does this have a few random seed or is it truly random (maybe has some time element)? – seokhoonlee Apr 28 '16 at 09:05
  • 1
    @seokhoonlee Neither. It is a pseduo-random number generator which, when possible, is seeded by a source of real randomness from the OS. For all but cryptography purposes it is random "enough". This is laid out in detail in the [`random` module's documentation](https://docs.python.org/3/library/random.html). – dimo414 May 05 '16 at 02:50
  • 2
    use clone for a new list – Mohammad Mahdi KouchakYazdi Jan 11 '17 at 11:19
  • 13
    is there an option that doesn't mutate the original array but return a new shuffled array? – Charlie Parker Mar 29 '17 at 17:57
  • 6
    @CharlieParker: Not that I know of. You could use `random.sample(x, len(x))` or just make a copy and `shuffle` that. For `list.sort` which has a similar issue, there's now `list.sorted`, but there's not a similar variant for `shuffle`. – tom10 Mar 29 '17 at 18:43
  • 7
    @seokhonlee for crypto-secure randomness, use `from random import SystemRandom` instead; add `cryptorand = SystemRandom()` and change row 3 to `cryptorand.shuffle(x)` – browly May 03 '17 at 23:10
  • Is there a way we can use something like (inplace = False)? Basically, looking to keep the original list intact while working on shallow shuffled list. – Brijesh Sep 28 '17 at 02:40
  • 1
    For those who want a new copy of the list instead of shuffling in place, see this question: https://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list – livingtech Nov 27 '17 at 16:44
  • Note that shuffle won't work as expected with slicing: e.g. `shuffle(x[5:])` will leave the original list unchanged – leoll2 Jul 28 '22 at 09:28
134

As you learned the in-place shuffling was the problem. I also have problem frequently, and often seem to forget how to copy a list, too. Using sample(a, len(a)) is the solution, using len(a) as the sample size. See https://docs.python.org/3.6/library/random.html#random.sample for the Python documentation.

Here's a simple version using random.sample() that returns the shuffled result as a new list.

import random

a = range(5)
b = random.sample(a, len(a))
print a, b, "two list same:", a == b
# print: [0, 1, 2, 3, 4] [2, 1, 3, 4, 0] two list same: False

# The function sample allows no duplicates.
# Result can be smaller but not larger than the input.
a = range(555)
b = random.sample(a, len(a))
print "no duplicates:", a == list(set(b))

try:
    random.sample(a, len(a) + 1)
except ValueError as e:
    print "Nope!", e

# print: no duplicates: True
# print: Nope! sample larger than population
Jochem Schulenklopper
  • 6,452
  • 4
  • 44
  • 62
Ted
  • 1,539
  • 1
  • 11
  • 6
  • 2
    is there an option that doesn't mutate the original array but return a new shuffled array? – Charlie Parker Mar 29 '17 at 17:57
  • just copy the list @CharlieParker: `old = [1,2,3,4,5]; new = list(old); random.shuffle(new); print(old); print(new)` (replace ; with newlines) – fjsj May 17 '18 at 18:22
  • `old[:]` also could do a shallow copy for list `old`. – Xiao Feb 14 '19 at 10:24
  • `sample()` is particularly helpful for prototyping a data analysis. `sample(data, 2)` for setting up the glue code of a pipeline, then "widening" it step-wise, up to `len(data)`. – Katrin Leinweber Aug 30 '19 at 13:32
89

The documentation for random.shuffle states that it will

Shuffle the sequence x in place.

Don't do:

print(random.shuffle(xs))  # WRONG!

Instead, do:

random.shuffle(xs)
print(xs)
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Ohad Cohen
  • 5,756
  • 3
  • 39
  • 36
49
#!/usr/bin/python3

import random

s=list(range(5))
random.shuffle(s) # << shuffle before print or assignment
print(s)

# print: [2, 4, 1, 3, 0]
Michael
  • 491
  • 4
  • 2
39

For numpy (popular library for scientific and financial applications), use np.random.shuffle:

import numpy as np
b = np.arange(10)
np.random.shuffle(b)
print(b)
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
fantabolous
  • 21,470
  • 7
  • 54
  • 51
  • 2
    What I like about this answer is that I can control the random seed in numpy. I bet there is a way to do that in the random module but that is not obvious to me right now... which means I need to read more. – VanBantam Jun 02 '20 at 17:53
  • 3
    @VanBantam In the `random` module, there is the `seed()` function, which can be used for creating reproducible outputs (https://docs.python.org/3/library/random.html#random.seed) – simon Apr 20 '23 at 08:06
25
>>> import random
>>> a = ['hi','world','cat','dog']
>>> random.shuffle(a,random.random)
>>> a
['hi', 'cat', 'dog', 'world']

It works fine for me. Make sure to set the random method.

Dan Lorenc
  • 5,376
  • 1
  • 23
  • 34
17

If you have multiple lists, you might want to define the permutation (the way you shuffle the list / rearrange the items in the list) first and then apply it to all lists:

import random

perm = list(range(len(list_one)))
random.shuffle(perm)
list_one = [list_one[index] for index in perm]
list_two = [list_two[index] for index in perm]

Numpy / Scipy

If your lists are numpy arrays, it is simpler:

import numpy as np

perm = np.random.permutation(len(list_one))
list_one = list_one[perm]
list_two = list_two[perm]

mpu

I've created the small utility package mpu which has the consistent_shuffle function:

import mpu

# Necessary if you want consistent results
import random
random.seed(8)

# Define example lists
list_one = [1,2,3]
list_two = ['a', 'b', 'c']

# Call the function
list_one, list_two = mpu.consistent_shuffle(list_one, list_two)

Note that mpu.consistent_shuffle takes an arbitrary number of arguments. So you can also shuffle three or more lists with it.

Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
  • I had used an alternative " from sklearn.utils import shuffle", "list1,list2=shuffle(list1,list2)" but the list1 and list2 swapped over randomly, which I didn't want. – vk3who Oct 24 '20 at 05:36
14

For one-liners, userandom.sample(list_to_be_shuffled, length_of_the_list) with an example:

import random
random.sample(list(range(10)), 10)

outputs: [2, 9, 7, 8, 3, 0, 4, 1, 6, 5]

weiyixie
  • 551
  • 5
  • 6
12
from random import random
my_list = range(10)
shuffled_list = sorted(my_list, key=lambda x: random())

This alternative may be useful for some applications where you want to swap the ordering function.

Jeff
  • 552
  • 1
  • 8
  • 16
  • Also, note that thanks to `sorted`, this is a functional shuffle (if you're into that sort of thing). – Inaimathi Nov 15 '17 at 19:52
  • 2
    This doesn't truly randomly distribute the values due to the stability of Timsort. (Values with the same key are left in their original order.) EDIT: I suppose it doesn't matter since the risk of collision with 64-bit floats is quite minimal. – Mateen Ulhaq Nov 13 '18 at 19:30
10

In some cases when using numpy arrays, using random.shuffle created duplicate data in the array.

An alternative is to use numpy.random.shuffle. If you're working with numpy already, this is the preferred method over the generic random.shuffle.

numpy.random.shuffle

Example

>>> import numpy as np
>>> import random

Using random.shuffle:

>>> foo = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> foo

array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])


>>> random.shuffle(foo)
>>> foo

array([[1, 2, 3],
       [1, 2, 3],
       [4, 5, 6]])

Using numpy.random.shuffle:

>>> foo = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> foo

array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])


>>> np.random.shuffle(foo)
>>> foo

array([[1, 2, 3],
       [7, 8, 9],
       [4, 5, 6]])
Gordon Bean
  • 4,272
  • 1
  • 32
  • 47
7

'print func(foo)' will print the return value of 'func' when called with 'foo'. 'shuffle' however has None as its return type, as the list will be modified in place, hence it prints nothing. Workaround:

# shuffle the list in place 
random.shuffle(b)

# print it
print(b)

If you're more into functional programming style you might want to make the following wrapper function:

def myshuffle(ls):
    random.shuffle(ls)
    return ls
JonDoe
  • 71
  • 1
  • 1
  • 2
    Since this passes a reference to the list, the original gets modified. You might want to copy the list before shuffling using [deepcopy](http://docs.python.org/2/library/copy.html) – shivram.ss Feb 12 '14 at 20:54
  • @shivram.ss In this case, you'd want something like `random.sample(ls, len(ls))` if you really want to go down that route. – Arda Xi Feb 28 '15 at 18:43
5

One can define a function called shuffled (in the same sense of sort vs sorted)

def shuffled(x):
    import random
    y = x[:]
    random.shuffle(y)
    return y

x = shuffled([1, 2, 3, 4])
print x
malbarbo
  • 10,717
  • 1
  • 42
  • 57
3
import random

class a:
    foo = "bar"

a1 = a()
a2 = a()
a3 = a()
a4 = a()
b = [a1,a2,a3,a4]

random.shuffle(b)
print(b)

shuffle is in place, so do not print result, which is None, but the list.

Michał Zaborowski
  • 3,911
  • 2
  • 19
  • 39
ravi tanwar
  • 598
  • 5
  • 16
2

you can either use shuffle or sample . both of which come from random module.

import random
def shuffle(arr1):
    n=len(arr1)
    b=random.sample(arr1,n)
    return b

OR

import random
def shuffle(arr1):
    random.shuffle(arr1)
    return arr1
ravi tanwar
  • 598
  • 5
  • 16
2

In case you need an in-place shuffling and ability to manipulate seed, this snippet would help:

from random import randint

a = ['hi','world','cat','dog']
print(sorted(a, key=lambda _: randint(0, 1)))

Remember that "shuffling" is a sorting by randomised key.

Vladimir Ignatev
  • 2,054
  • 1
  • 20
  • 34
1

Make sure you are not naming your source file random.py, and that there is not a file in your working directory called random.pyc.. either could cause your program to try and import your local random.py file instead of pythons random module.

1

You can go for this:

>>> A = ['r','a','n','d','o','m']
>>> B = [1,2,3,4,5,6]
>>> import random
>>> random.sample(A+B, len(A+B))
[3, 'r', 4, 'n', 6, 5, 'm', 2, 1, 'a', 'o', 'd']

if you want to go back to two lists, you then split this long list into two.

kiriloff
  • 25,609
  • 37
  • 148
  • 229
1
def shuffle(_list):
    if not _list == []:
        import random
        list2 = []
        while _list != []:
            card = random.choice(_list)
            _list.remove(card)
            list2.append(card)
        while list2 != []:
            card1 = list2[0]
            list2.remove(card1)
            _list.append(card1)
        return _list
tuomastik
  • 4,559
  • 5
  • 36
  • 48
Pogramist
  • 35
  • 3
  • This function can help you if you don't want to use random module – Pogramist May 29 '17 at 14:52
  • This solution is not only verbose but inefficient (runtime is proportional to the square of the list size). – toolforger Oct 13 '18 at 08:33
  • The second loop could be replaced by `_list.extend(list2)`, which is more succinct AND more efficient. – toolforger Oct 13 '18 at 08:36
  • A Python function that modifies a parameter should never return a result. It's just a convention, but a useful one: People often lack the time to look at the implementation of all the functions that they call, so anybody who just sees your function's name and that it has a result will be very surprised to see the function update its parameter. – toolforger Oct 13 '18 at 08:38
1

you could build a function that takes a list as a parameter and returns a shuffled version of the list:

from random import *

def listshuffler(inputlist):
    for i in range(len(inputlist)):
        swap = randint(0,len(inputlist)-1)
        temp = inputlist[swap]
        inputlist[swap] = inputlist[i]
        inputlist[i] = temp
    return inputlist
user8327014
  • 167
  • 1
  • 4
1
""" to shuffle random, set random= True """

def shuffle(x,random=False):
     shuffled = []
     ma = x
     if random == True:
         rando = [ma[i] for i in np.random.randint(0,len(ma),len(ma))]
         return rando
     if random == False:
          for i in range(len(ma)):
          ave = len(ma)//3
          if i < ave:
             shuffled.append(ma[i+ave])
          else:
             shuffled.append(ma[i-ave])    
     return shuffled
Josh Anish
  • 101
  • 1
  • 5
  • a small introduction or explanation would be helpful? – kacase Mar 28 '18 at 09:53
  • the function is helpful for shuffling activity, imagine you have to shuffle a list of numbers for three times and in the three times you require random shuffle to occur then just turn the random argument to True else if you don't require randomness and you want same shuffling order to be preserved then don't make any changes, just run the code. – Josh Anish Apr 16 '18 at 17:23
  • Since there is no use case where the caller of this function would decide at runtime whether he wants the random or the non-random shuffle, this function should be split into two. – toolforger Oct 13 '18 at 08:41
  • There is no description what the non-random shuffle is supposed to do. (On a tangent, it is not an answer so the question, so it does not serve the purpose of Stack Overflow.) – toolforger Oct 13 '18 at 08:43
1
import random
class a:
    foo = "bar"

a1 = a()
a2 = a()
b = [a1.foo,a2.foo]
random.shuffle(b)
Xavier
  • 11
  • 2
0

The shuffling process is "with replacement", so the occurrence of each item may change! At least when when items in your list is also list.

E.g.,

ml = [[0], [1]] * 10

After,

random.shuffle(ml)

The number of [0] may be 9 or 8, but not exactly 10.

Gatsby
  • 21
  • 4
0

Plan: Write out the shuffle without relying on a library to do the heavy lifting. Example: Go through the list from the beginning starting with element 0; find a new random position for it, say 6, put 0’s value in 6 and 6’s value in 0. Move on to element 1 and repeat this process, and so on through the rest of the list

import random
iteration = random.randint(2, 100)
temp_var = 0
while iteration > 0:

    for i in range(1, len(my_list)): # have to use range with len()
        for j in range(1, len(my_list) - i):
            # Using temp_var as my place holder so I don't lose values
            temp_var = my_list[i]
            my_list[i] = my_list[j]
            my_list[j] = temp_var

        iteration -= 1
Enber
  • 13
  • 2
0

You can use random.choices() to shuffle your list.

TEAMS = [A,B,C,D,E,F,G,H]
random.choices(TEAMS,k = len(TEAMS)) 

The above code will return a randomized list same length as your previous list.

Hope It Helps !!!

0

For anyone interested in using the Index Sequential Method (Ouarda et.al., 1997) to reorder a list:

def ISM(dList):
    nList = dList.copy()
    dRng = range(len(dList))
    for i in dRng[:-1]:
        nList[i] = dList[i+1]
    nList[-1] = dList[0]        
    return nList

This will work for a single value list...

valList = [1,2,3,4,5,6,7]

for l in range(len(valList)):
    print(valList)
    dataList = ISM(valList)

This will print out...

[1, 2, 3, 4, 5, 6, 7]
[2, 3, 4, 5, 6, 7, 1]
[3, 4, 5, 6, 7, 1, 2]
[4, 5, 6, 7, 1, 2, 3]
[5, 6, 7, 1, 2, 3, 4]
[6, 7, 1, 2, 3, 4, 5]
[7, 1, 2, 3, 4, 5, 6]

or a list of nested lists...

nestedList = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]

for l in range(len(nestedList)):
    print(nestedList)
    nestedList = ISM(nestedList)

This will print out...

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
[[4, 5, 6], [7, 8, 9], [10, 11, 12], [1, 2, 3]]
[[7, 8, 9], [10, 11, 12], [1, 2, 3], [4, 5, 6]]
[[10, 11, 12], [1, 2, 3], [4, 5, 6], [7, 8, 9]]

@Shantanu Sharma provides some great methods for breaking a list of values into a sequence of nested lists of size n.

Here's the method I used...

valList = [1,2,3,4,5,6,7,8,9,10,11,12]

# Yield successive n-sized lists from l
def SequenceList(l, n):
    # looping till length l
    for i in range(0, len(l), n):
        yield l[i:i + n]

nestedList = list(SequenceList(valList, 3))

This will print out...

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
Nunya
  • 63
  • 1
  • 5
-1

It works fine. I am trying it here with functions as list objects:

    from random import shuffle

    def foo1():
        print "foo1",

    def foo2():
        print "foo2",

    def foo3():
        print "foo3",

    A=[foo1,foo2,foo3]

    for x in A:
        x()

    print "\r"

    shuffle(A)
    for y in A:
        y()

It prints out: foo1 foo2 foo3 foo2 foo3 foo1 (the foos in the last row have a random order)

Stefan Gruenwald
  • 2,582
  • 24
  • 30