note.nkmk.me

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

Date: 2018-01-21 / Modified: 2018-10-29 / tags: Python, リスト

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

choice()は要素を一つ取得、sample(), choices()は複数の要素をリストで取得できる。sample()は重複なしの非復元抽出、choices()は重複ありの復元抽出。

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

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

ランダムではなく任意の条件で要素を抽出したい場合は以下の記事を参照。

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

スポンサーリンク

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

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

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()で、リストからランダムで複数の要素を取得できる。要素の重複はなし(非復元抽出)。

第一引数にリスト、第二引数に取得したい要素の個数を指定する。リストが返される。

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から追加された関数。それより前のバージョンでは使えない。

引数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_

また、weightscum_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
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事