2185

Assume I have the following list:

foo = ['a', 'b', 'c', 'd', 'e']

What is the simplest way to retrieve an item at random from this list?

Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
Ray
  • 179,335
  • 97
  • 219
  • 202
  • A duplicate answer is here - https://stackoverflow.com/questions/9690009/pick-n-distinct-items-at-random-from-sequence-of-unknown-length-in-only-one-ite/49682832#49682832 – Solomon Vimal Feb 24 '22 at 03:06

18 Answers18

3211

Use random.choice():

import random

foo = ['a', 'b', 'c', 'd', 'e']
print(random.choice(foo))

For cryptographically secure random choices (e.g., for generating a passphrase from a wordlist), use secrets.choice():

import secrets

foo = ['battery', 'correct', 'horse', 'staple']
print(secrets.choice(foo))

secrets is new in Python 3.6. On older versions of Python you can use the random.SystemRandom class:

import random

secure_random = random.SystemRandom()
print(secure_random.choice(foo))
Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
Pēteris Caune
  • 41,622
  • 6
  • 55
  • 79
  • 4
    Does making two consecutive calls of `random.choice(foo)` return two different results? – Eduardo Pignatelli Dec 07 '18 at 17:13
  • 65
    @EduardoPignatelli Each choice is random, so it *can* return two different results, but depending on the start seed, it's not guaranteed. If you want to select *n* distinct random elements from a list *lst*, use [`random.sample(lst, n)`](https://docs.python.org/library/random.html#random.sample) – Graham Dec 23 '18 at 17:02
  • 10
    on a related note, `Standard pseudo-random generators are not suitable for security/cryptographic purposes.` [ref](https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html#b311-random) – XoXo Jan 30 '19 at 19:46
260

If you want to randomly select more than one item from a list, or select an item from a set, I'd recommend using random.sample instead.

import random
group_of_items = {'a', 'b', 'c', 'd', 'e'}  # a sequence or set will work here.
num_to_select = 2                           # set the number to select here.
list_of_random_items = random.sample(group_of_items, num_to_select)
first_random_item = list_of_random_items[0]
second_random_item = list_of_random_items[1] 

If you're only pulling a single item from a list though, choice is less clunky, as using sample would have the syntax random.sample(some_list, 1)[0] instead of random.choice(some_list).

Unfortunately though, choice only works for a single output from sequences (such as lists or tuples). Though random.choice(tuple(some_set)) may be an option for getting a single item from a set.

EDIT: Using Secrets

As many have pointed out, if you require more secure pseudorandom samples, you should use the secrets module:

import secrets                              # imports secure module.
secure_random = secrets.SystemRandom()      # creates a secure random object.
group_of_items = {'a', 'b', 'c', 'd', 'e'}  # a sequence or set will work here.
num_to_select = 2                           # set the number to select here.
list_of_random_items = secure_random.sample(group_of_items, num_to_select)
first_random_item = list_of_random_items[0]
second_random_item = list_of_random_items[1]

EDIT: Pythonic One-Liner

If you want a more pythonic one-liner for selecting multiple items, you can use unpacking.

import random
first_random_item, second_random_item = random.sample({'a', 'b', 'c', 'd', 'e'}, 2)
Paul
  • 3,284
  • 1
  • 15
  • 22
  • 5
    BTW `secrets` module was added to Python standard library in version 3.6 https://www.python.org/dev/peps/pep-0506/ – and1er Dec 13 '19 at 06:53
179

If you also need the index, use random.randrange

from random import randrange
random_index = randrange(len(foo))
print(foo[random_index])
Boris Verkhovskiy
  • 10,733
  • 7
  • 77
  • 79
Juan Pablo Rinaldi
  • 3,204
  • 1
  • 19
  • 20
56

As of Python 3.6 you can use the secrets module, which is preferable to the random module for cryptography or security uses.

To print a random element from a list:

import secrets
foo = ['a', 'b', 'c', 'd', 'e']
print(secrets.choice(foo))

To print a random index:

print(secrets.randbelow(len(foo)))

For details, see PEP 506.

Chris_Rands
  • 35,097
  • 12
  • 75
  • 106
42

I propose a script for removing randomly picked up items off a list until it is empty:

Maintain a set and remove randomly picked up element (with choice) until list is empty.

s=set(range(1,6))
import random

while len(s)>0:
  s.remove(random.choice(list(s)))
  print(s)

Three runs give three different answers:

>>> 
set([1, 3, 4, 5])
set([3, 4, 5])
set([3, 4])
set([4])
set([])
>>> 
set([1, 2, 3, 5])
set([2, 3, 5])
set([2, 3])
set([2])
set([])

>>> 
set([1, 2, 3, 5])
set([1, 2, 3])
set([1, 2])
set([1])
set([])
Community
  • 1
  • 1
kiriloff
  • 24,401
  • 34
  • 141
  • 212
  • 24
    Or you could just `random.shuffle` the `list` once and either iterate it or pop it to produce results. Either would result in a perfectly adequate "select randomly with no repeats" stream, it's just that the randomness would be introduced at the beginning. – ShadowRanger Dec 25 '15 at 03:23
  • 2
    Theoretically you can use the [pop()](https://docs.python.org/3.4/library/stdtypes.html#set.pop) method of a set to remove an arbitrary element from a set and return it, but it's probably not random enough. – Joubarc Jun 17 '16 at 07:07
32
foo = ['a', 'b', 'c', 'd', 'e']
number_of_samples = 1

In Python 2:

random_items = random.sample(population=foo, k=number_of_samples)

In Python 3:

random_items = random.choices(population=foo, k=number_of_samples)
Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
Fardin Abdi
  • 1,138
  • 14
  • 18
26

NumPy solution: numpy.random.choice

For this question, it works the same as the accepted answer (import random; random.choice()), but I added it because the programmer may have imported NumPy already (like me)

And also there are some differences between the two methods that may concern your actual use case.

import numpy as np
np.random.choice(foo) # randomly selects a single item

For reproducibility, you can do:

np.random.seed(123)
np.random.choice(foo) # first call will always return 'c'

For samples of one or more items, returned as an array, pass the size argument:

np.random.choice(foo, 5)          # sample with replacement (default)
np.random.choice(foo, 5, False)   # sample without replacement
C8H10N4O2
  • 16,948
  • 6
  • 87
  • 123
  • 1
    Note that this should not be used for cryptographic purposes, see the `secrets` module from other answers such as the one from Pēteris Caune! And a working link to documentation for `numpy.random.choice`: https://numpy.org/doc/stable/reference/random/generated/numpy.random.choice.html – xuiqzy Jan 24 '22 at 10:04
17

If you need the index, just use:

import random
foo = ['a', 'b', 'c', 'd', 'e']
print int(random.random() * len(foo))
print foo[int(random.random() * len(foo))]

random.choice does the same:)

Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
Janek Olszak
  • 3,780
  • 1
  • 26
  • 22
  • 2
    @tc. Actually, it does do essentially the same. Implementation of `random.choice(self, seq)` is `return seq[int(self.random() * len(seq))]`. – wim Apr 12 '13 at 04:56
  • 2
    @wim That's a little disappointing, but the *very* disappointing thing is that's also the definition of `randrange()` which means e.g. `random.SystemRandom().randrange(3<<51)` exhibits significant bias. *Sigh...* – tc. Apr 13 '13 at 23:55
  • 7
    @kevinsa5 Ultimately it's because a `float` (an IEEE double) can only take a finite number of values in [0,1). `Random.random()` generates its output in the traditional way: pick a random integer in `[0, 2**53)` and divide by `2**53` (53 is the number of bits in a double). So `random()` returns 2**53 equiprobable doubles, and you can divide this evenly into N outputs only if N is a power of 2. The bias is small for small N, but see `collections.Counter(random.SystemRandom().randrange(3<<51)%6 for i in range(100000)).most_common()`. (Java's Random.nextInt() avoids such bias.) – tc. Jan 18 '14 at 16:37
  • 1
    @tc. I suppose anything less than about `2**40`, (which is 1099511627776), would be small enough for the bias to not matter in practice? This should really be pointed out in the documentation, because if somebody is not meticulous, they might not expect problems to come from this part of their code. – Evgeni Sergeev May 29 '15 at 01:28
  • 1
    @tc.: Actually, `random` uses `getrandbits` to get an adequate number of bits to generate a result for larger `randrange`s (`random.choice` is also using that). This is true on both 2.7 and 3.5. It only uses `self.random() * len(seq)` when `getrandbits` is not available. It's not doing the stupid thing you think it is. – ShadowRanger Dec 25 '15 at 03:32
16

How to randomly select an item from a list?

Assume I have the following list:

foo = ['a', 'b', 'c', 'd', 'e']  

What is the simplest way to retrieve an item at random from this list?

If you want close to truly random, then I suggest secrets.choice from the standard library (New in Python 3.6.):

>>> from secrets import choice         # Python 3 only
>>> choice(list('abcde'))
'c'

The above is equivalent to my former recommendation, using a SystemRandom object from the random module with the choice method - available earlier in Python 2:

>>> import random                      # Python 2 compatible
>>> sr = random.SystemRandom()
>>> foo = list('abcde')
>>> foo
['a', 'b', 'c', 'd', 'e']

And now:

>>> sr.choice(foo)
'd'
>>> sr.choice(foo)
'e'
>>> sr.choice(foo)
'a'
>>> sr.choice(foo)
'b'
>>> sr.choice(foo)
'a'
>>> sr.choice(foo)
'c'
>>> sr.choice(foo)
'c'

If you want a deterministic pseudorandom selection, use the choice function (which is actually a bound method on a Random object):

>>> random.choice
<bound method Random.choice of <random.Random object at 0x800c1034>>

It seems random, but it's actually not, which we can see if we reseed it repeatedly:

>>> random.seed(42); random.choice(foo), random.choice(foo), random.choice(foo)
('d', 'a', 'b')
>>> random.seed(42); random.choice(foo), random.choice(foo), random.choice(foo)
('d', 'a', 'b')
>>> random.seed(42); random.choice(foo), random.choice(foo), random.choice(foo)
('d', 'a', 'b')
>>> random.seed(42); random.choice(foo), random.choice(foo), random.choice(foo)
('d', 'a', 'b')
>>> random.seed(42); random.choice(foo), random.choice(foo), random.choice(foo)
('d', 'a', 'b')

A comment:

This is not about whether random.choice is truly random or not. If you fix the seed, you will get the reproducible results -- and that's what seed is designed for. You can pass a seed to SystemRandom, too. sr = random.SystemRandom(42)

Well, yes you can pass it a "seed" argument, but you'll see that the SystemRandom object simply ignores it:

def seed(self, *args, **kwds):
    "Stub method.  Not used for a system random number generator."
    return None
Russia Must Remove Putin
  • 337,988
  • 84
  • 391
  • 326
  • 5
    This is just pedantic, but secrets isn't truly random, it's cryptographically secure pseudorandom. – Paul Jan 10 '21 at 22:07
12

In short, use random.sample method

The sample method returns a new list containing elements from the population while leaving the original population unchanged. The resulting list is in selection order so that all sub-slices will also be valid random samples.

import random
lst = ['a', 'b', 'c', 'd', 'e']
random.seed(0)  # remove this line, if you want different results for each run
rand_lst = random.sample(lst,3)  # 3 is the number of sample you want to retrieve
print(rand_lst)

Output:['d', 'e', 'a']

here is a running code https://onecompiler.com/python/3xem5jjvz

Memin
  • 3,210
  • 26
  • 27
  • Not working for me `AttributeError: 'module' object has no attribute 'seed'` – spuder Oct 12 '21 at 18:46
  • Here is an online tutorial (with Python3) shows the code with seed works https://onecompiler.com/python/3xem5jjvz . – Memin Oct 16 '21 at 19:38
12

I usually use the random module for working with lists and randomization

import random
foo = ['a', 'b', 'c', 'd', 'e']
print(random.choice(foo))
Pratik Thorat
  • 303
  • 2
  • 7
8

This is the code with a variable that defines the random index:

import random

foo = ['a', 'b', 'c', 'd', 'e']
randomindex = random.randint(0,len(foo)-1) 
print (foo[randomindex])
## print (randomindex)

This is the code without the variable:

import random

foo = ['a', 'b', 'c', 'd', 'e']
print (foo[random.randint(0,len(foo)-1)])

And this is the code in the shortest and smartest way to do it:

import random

foo = ['a', 'b', 'c', 'd', 'e']
print(random.choice(foo))

(python 2.7)

Will Dereham
  • 946
  • 9
  • 32
Liam
  • 5,218
  • 3
  • 32
  • 49
5

Random item selection:

import random

my_list = [1, 2, 3, 4, 5]
num_selections = 2

new_list = random.sample(my_list, num_selections)

To preserve the order of the list, you could do:

randIndex = random.sample(range(len(my_list)), n_selections)
randIndex.sort()
new_list = [my_list[i] for i in randIndex]

Duplicate of https://stackoverflow.com/a/49682832/4383027

Solomon Vimal
  • 860
  • 11
  • 25
4

You could just:

from random import randint

foo = ["a", "b", "c", "d", "e"]

print(foo[randint(0,4)])
3

This may already be an answer but you could use random.shuffle. Example:

import random
foo = ['a', 'b', 'c', 'd', 'e']
random.shuffle(foo)
Scott
  • 3,608
  • 5
  • 31
  • 50
Jax
  • 49
  • 7
1

The recommended numpy way is now to use an explicit RNG:

from numpy.random import default_rng

rng = default_rng()
rng.choice(foo)
iacob
  • 14,010
  • 5
  • 54
  • 92
-1

We can also do this using randint.

from random import randint
l= ['a','b','c']

def get_rand_element(l):
    if l:
        return l[randint(0,len(l)-1)]
    else:
        return None

get_rand_element(l)
Liam
  • 5,218
  • 3
  • 32
  • 49
Abdul Majeed
  • 2,471
  • 21
  • 25
  • 24
    Why on earth would you do it this way, when there's `random.choice()` and `random.randrange()`? – alexis Mar 19 '16 at 15:10
  • "random.choice()" will give you "IndexError: list index out of range" on empty list. – Abdul Majeed Sep 05 '17 at 05:45
  • 10
    As it should: that's what exceptions are for. Choosing from an empty list is an error. Returning `None` just kicks the can to some random later point where the invalid "element" triggers an exception; or worse yet, you get an incorrect program instead of an exception, and you don't even know it. – alexis Sep 05 '17 at 09:27
  • Can you add a reference to *randint*, preferably to the official documentation? – Peter Mortensen Jul 31 '21 at 19:00
-1

You can randomly print/pick an element from the given dictionary by randomising the slicing arguments using the slicing method. But make sure you have imported the random module, thats the big boy here.

import random
foo = ['a', 'b', 'c', 'd', 'e']
print(foo[random.randint(0,len(foo))])

Here we are setting a random number in the slice argument. Any number given in the foo[] would return the element attaining the index similar to the number given in slice. Just setting a random number there would do you job.

Eric Aya
  • 69,000
  • 34
  • 174
  • 243
LunaticXXD10
  • 307
  • 2
  • 8
  • What's the point of this answer? Why would you use this instead of `random.choice`? – juanpa.arrivillaga May 17 '22 at 20:25
  • Also note, it has a bug, since `random.randint` has *inclusive* boundaries, so `len(foo)` is a possible value, which would raise an `IndexError` – juanpa.arrivillaga May 17 '22 at 20:26
  • @juanpa.arrivillaga I am wondering if you had run the code once. If you did I am bounded to believe you pasted it into your messed up code which dosent meet the logic to mine.. If you've have a general perspective , this works well. random.randint() is more specified towards numbers, something that's required for to execute random slice number. Hope you modify your perceptions. – LunaticXXD10 May 29 '22 at 20:02
  • No, I didn't run the code and certainly would not have pasted it into my own code because it has a well-known bug (indeed, this bug is the reason that `random.randrange` was added). I am just simply aware of the semantics of the `random` module, and how it would work here. Or perhaps the *intention* of this code is to produce an `IndexError`? – juanpa.arrivillaga May 29 '22 at 20:25
  • "random.randint() is more specified towards numbers, something that's required for to execute random slice number. " This sentence doesn't make much sense. You mention "slice" but there are no slices here. In effect, it is a (buggy) implementation of `random.choice`. So no, I will not be modifying my perceptions. – juanpa.arrivillaga May 29 '22 at 20:25
  • `foo[...]` is not a *slice* unless you **use a slice**, e.g. `foo[1:10]` is a slice, i.e. `1:10` gets translated into a `slice(1, 10)` object. You are just using *regular indexing*. Not slicing. – juanpa.arrivillaga Jun 02 '22 at 20:42
  • `random.radint(0,len(foo)) ` **does not have range of `0, 5 - 1`, it has a range of `0 - 5` **inclusive**. This is *well documented in the `random` module* Your code "works" because run by itself it doesn't pick `5` ever. But try using `for _ in range(100): print(foo[random.randint(0,len(foo))])` and you *will* get an index error. You have a mindset, apparently, of not reading documentation of effectively testing your code – juanpa.arrivillaga Jun 02 '22 at 20:44
  • Again, this is a *well known problem with this method*, and it's **why** the `random.randrange` method was added, because most people expect the end parameter to be exclusive (as it is for `range`) and not inclusive – juanpa.arrivillaga Jun 02 '22 at 20:46
  • How about if i add print(foo[random.randint(0,len(foo)-1)]) – LunaticXXD10 Jun 02 '22 at 21:02