Pythonでリストからランダムに要素を選択するchoice, sample, choices

Python標準ライブラリのrandomモジュールの関数choice(), sample(), choices()を使うと、リストやタプル、文字列などのシーケンスオブジェクトからランダムに要素を選択して取得(ランダムサンプリング)できます。 choice()は要素を一つ取得、sample(), choices()は複数の要素をリストで取得できます。sample()は重複なしの非復元抽出、choices()は重複ありの復元抽出。

random --- 擬似乱数を生成する — Python 3.7.1 ドキュメント

ここでは以下の内容について説明します。

ランダムに要素を一つ選択: random.choice() ランダムに複数の要素を選択(重複なし): random.sample() ランダムに複数の要素を選択(重複あり): random.choices() 乱数シードを固定

ランダムではなく任意の条件で要素を抽出したい場合は

リストの要素をランダムに並べ替えたい場合や、乱数やそのリスト自体を生成したい場合は

ランダムに要素を一つ選択: random.choice()

randomモジュールの関数choice()で、リストからランダムで要素が一つ選択され取得できます。

random.choice() — Python 3.7.1 ドキュメント

import random

l = [0, 1, 2, 3, 4]

print(random.choice(l))
# 0

タプルや文字列でも同じです。文字列の場合は一文字が選択されます。

print(random.choice(('xxx', 'yyy', 'zzz')))
# yyy

print(random.choice('abcde'))
# b

空のリストやタプル、文字列を引数として指定するとエラー。

# print(random.choice([]))
# IndexError: Cannot choose from an empty sequence

ランダムに複数の要素を選択(重複なし): random.sample()

randomモジュールの関数sample()で、リストからランダムで複数の要素を取得できます。要素の重複はなし(非復元抽出)。 第一引数にリスト、第二引数に取得したい要素の個数を指定します。リストが返されます。

random.sample() — Python 3.7.1 ドキュメント

import random

l = [0, 1, 2, 3, 4]

print(random.sample(l, 3))
# [1, 3, 2]

print(type(random.sample(l, 3)))
# <class 'list'>

第二引数を1とした場合も要素が一つのリストが返されます。0とした場合は空のリスト。第一引数に指定したリストの要素数を超える値だとエラーとなります。

print(random.sample(l, 1))
# [0]

print(random.sample(l, 0))
# []

# print(random.sample(l, 10))
# ValueError: Sample larger than population or is negative

第一引数をタプルや文字列にした場合も返されるのはリスト。

print(random.sample(('xxx', 'yyy', 'zzz'), 2))
# ['xxx', 'yyy']

print(random.sample('abcde', 2))
# ['a', 'e']

タプルや文字列に戻したい場合はtuple(), join()を使います。

print(tuple(random.sample(('xxx', 'yyy', 'zzz'), 2)))
# ('yyy', 'xxx')

print(''.join(random.sample('abcde', 2)))
# de

ランダムに複数の要素を選択(重複あり): random.choices()

randomモジュールの関数choices()で、リストからランダムで複数の要素を取得できます。sample()とは異なり、要素の重複を許して選択される(復元抽出)。 choices()はPython3.6から追加された関数。それより前のバージョンでは使えない。

random.choices() — Python 3.7.1 ドキュメント

引数kで取得したい要素の個数を指定します。重複が認められているので、取得する要素数kを元のリストの要素数より大きくすることもできます。 kはキーワード専用引数なのでk=3などのようにキーワードを指定する必要があります。

import random

l = [0, 1, 2, 3, 4]

print(random.choices(l, k=3))
# [2, 1, 0]

print(random.choices(l, k=10))
# [3, 4, 1, 4, 4, 2, 0, 4, 2, 0]

kのデフォルトは1。省略した場合は要素数1のリストが返されます。

print(random.choices(l))
# [1]

引数weightsでそれぞれの要素が選ばれる重み(確率)を指定できます。weightsに指定するリストの要素の型はintでもfloatでも問題ありません。0にするとその要素は選ばれない。

print(random.choices(l, k=3, weights=[1, 1, 1, 10, 1]))
# [0, 2, 3]

print(random.choices(l, k=3, weights=[1, 1, 0, 0, 0]))
# [0, 1, 1]

引数cum_weightsに累積的な重みとして指定することもできます。以下のサンプルコードのcum_weightsは上の一つ目のweightsと等価。

print(random.choices(l, k=3, cum_weights=[1, 2, 3, 13, 14]))
# [3, 2, 3]

引数weights, cum_weightsのデフォルトはどちらもNoneで、それぞれの要素が同じ確率で選択されます。 引数weightsまたはcum_weightsの長さ(要素数)が元のリストと異なるとエラーが発生します。

# print(random.choices(l, k=3, weights=[1, 1, 1, 10, 1, 1, 1]))
# ValueError: The number of weights does not match the population_

また、weightsとcum_weightsを同時に指定してもエラーとなります。

# print(random.choices(l, k=3, weights=[1, 1, 1, 10, 1], cum_weights=[1, 2, 3, 13, 14]))
# TypeError: Cannot specify both weights and cumulative weights

ここまでサンプルコードで例として第一引数にリストを指定していたが、タプルや文字列でも同じです。

乱数シードを固定

randomモジュールの関数seed()に任意の整数を与えることで、乱数シードを固定することができます。常に同じ要素が選択されます。

random.seed(0)
print(random.choice(l))
# 3
Last Updated: 6/26/2019, 10:34:03 PM