Pythonでリストからランダムに要素を選択するchoice, sample, choices
Python標準ライブラリのrandomモジュールのchoice()
, sample()
, choices()
関数を使うと、リストやタプル、文字列などのシーケンスオブジェクトからランダムに要素を選択して取得(ランダムサンプリング)できる。
choice()
は要素を一つ選択、sample()
, choices()
は複数の要素を選択する。sample()
は重複なしの非復元抽出、choices()
は重複ありの復元抽出。
ランダムではなく任意の条件で要素を抽出したい場合は以下の記事を参照。
リストの要素をランダムに並べ替えたい場合や、乱数やそのリスト自体を生成したい場合は以下の記事を参照。
- 関連記事: Pythonでリストの要素をシャッフル(random.shuffle, sample)
- 関連記事: Pythonでランダムな小数・整数を生成するrandom, randrange, randintなど
ランダムに要素を一つ選択: random.choice()
random.choice()
はリストからランダムに要素を一つ選択して返す。
import random
l = [0, 1, 2, 3, 4]
print(random.choice(l))
# 1
タプルや文字列でも同様。文字列の場合は一文字が選択される。
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()
はリストからランダムに複数の要素を選択してリストとして返す。要素の重複はなし(非復元抽出)。
第一引数にリスト、第二引数に取得したい要素の個数を指定する
import random
l = [0, 1, 2, 3, 4]
print(random.sample(l, 3))
# [3, 1, 0]
print(type(random.sample(l, 3)))
# <class 'list'>
第二引数を1
とした場合も要素が一つのリストが返される。0
とした場合は空のリスト。第一引数に指定したリストの要素数を超える値だとエラーとなる。
print(random.sample(l, 1))
# [1]
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))
# ['zzz', 'xxx']
print(random.sample('abcde', 2))
# ['c', 'd']
タプルや文字列に戻したい場合はtuple()
, join()
を使う。
print(tuple(random.sample(('xxx', 'yyy', 'zzz'), 2)))
# ('zzz', 'yyy')
print(''.join(random.sample('abcde', 2)))
# be
値を判定しているわけではないので、元のリストやタプルなどに同じ値の要素が含まれていると同じ値が選択される可能性がある。
l_dup = [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]
print(random.sample(l_dup, 3))
# [2, 0, 0]
重複した値を避けたい場合はset()
で集合(set
型)に変換してユニーク(一意)な要素のみを抽出してからsample()
を使えばよい。
print(set(l_dup))
# {0, 1, 2, 3}
print(random.sample(list(set(l_dup)), 3))
# [0, 2, 1]
Python 3.11からsample()
の第一引数にset
を直接指定するとエラーになるようになった。上の例(list(set(...))
)のように明示的にリストなどに変換する必要があるので注意。
ランダムに複数の要素を選択(重複あり): random.choices()
random.choices()
はリストからランダムに複数の要素を選択してリストとして返す。sample()
とは異なり、要素の重複を許して選択される(復元抽出)。
引数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
でもOK。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
random.seed(0)
print(random.choice(l))
# 3