NumPyのrandomモジュールは、様々な確率分布から擬似乱数を生成するために用意されています。
ここでは、このrandomモジュールの使い方を解説します。すぐに乱数を生成したい場合は、「1. randomモジュールの使い方」と「2. randomモジュールのメソッド一覧」 だけ確認してください。実務上はこれだけ確認しておけば十分です。
なお、Numpyのversion1.17を境に、randomモジュールの乱数生成方法は変更となっています。従来の方法も引き続き使用可能ですが、処理速度や性能が大きく異なるため、今後は、「1. randomモジュールの使い方」で解説する方法を使いましょう。
従来の方法と新しい方法の違いは、「3. ver1.17より前と以後の違い」で解説しています。
「4. 乱数生成の仕組み」はマニアックであり、実務上知っておく必要はありません。読み飛ばしても全く問題ありません。
それでは見ていきましょう。
1. randomモジュールの使い方
乱数を生成するには、まず default_rng で Generatorクラスのインスタンス(ジェネレータオブジェクト)を作成します。これは、以下のように任意の変数に代入するだけで作成することができます。
# default_rngを変数に代入してジェネレータオブジェクトを作成
import numpy as np
rng = np.random.default_rng()
こうして作成したジェネレータオブジェクトに対してメソッドを呼び出すことで、様々な確率分布から乱数を生成することができます。例えば、以下のコードでは、standard_normalという、標準正規分布から乱数を生成するメソッドを呼び出しています。
rng.standard_normal(3)
以上が、numpyのrandomモジュールにおける乱数生成方法です。
2. randomモジュールのメソッド一覧
ジェネレータオブジェクトには、様々な確率分布から、種々の乱数を生成するためのメソッドが豊富に用意されています。以下に一覧を載せておきます。
それぞれのメソッド名をクリックすると、使い方の解説記事を読むことができます。
2.1. 使用頻度が特に高いもの
integers(low[, high, size, dtype, endpoint]) | 離散一様分布から整数の乱数を生成 |
random([size, dtype, out]) | 0.0以上1.0未満 の連続一様分布から浮動小数点数の乱数を生成 |
choice(a[, size, replace, p, axis, shuffle]) | 渡した配列から乱数を生成 |
2.2. 配列の並べ替え
shuffle(x[, axis]) | 既存の配列の要素をランダムに並べ替え |
permutation(x[, axis]) | 既存の配列の要素をランダムに並べ替えた新しい配列を |
2.3. 様々な確率分布
beta(a, b[, size]) | ベータ分布から乱数を生成 |
binomial(n, p[, size]) | 二項分布から乱数を生成 |
chisquare(df[, size]) | カイ二乗分布から乱数を生成 |
dirichlet(alpha[, size]) | ディリクレ分布から乱数を生成 |
exponential([scale, size]) | 指数分布から乱数を生成 |
f(dfnum, dfden[, size]) | F分布から乱数を生成 |
gamma(shape[, scale, size]) | ガンマ分布から乱数を生成 |
geometric(p[, size]) | 幾何分布から乱数を生成 |
gumbel([loc, scale, size]) | ガンベル分布から乱数を生成 |
hypergeometric(ngood, nbad, nsample[, size]) | 超幾何分布から乱数を生成 |
laplace([loc, scale, size]) | ラプラス分布(二重指数分布)から乱数を生成 |
logistic([loc, scale, size]) | ロジスティック分布から乱数を生成 |
lognormal([mean, sigma, size)] | 対数正規分布から乱数を生成 |
logseries(p[, size]) | 対数級数分布から乱数を生成 |
multinomial(n, pvals[, size]) | 多項分布から乱数を生成 |
multivariate_hypergeometric(colors, nsample) | 多変量超幾何分布から変量を生成 |
multivariate_normal(mean, cov[, size]) | 多変量正規分布から乱数を生成 |
negative_binomial(n, p[, size]) | 負の二項分布から乱数を生成 |
noncentral_chisquare(df, nonc[, size]) | 非心カイ二乗分布から乱数を生成 |
noncentral_f(dfnum, dfden, nonc[, size]) | 非心F分布から乱数を生成。 |
normal([loc, scale, size]) | 正規分布(ガウス分布)から乱数を生成 |
pareto(a[, size]) | パレート分布から乱数を生成 |
poisson([lam, size]) | ポアソン分布から乱数を生成 |
power(a[, size]) | べき分布から乱数を生成 |
rayleigh([scale, size]) | レイリー分布から乱数を生成 |
standard_cauchy([size]) | 標準コーシー分布から乱数を生成 |
standard_exponential([size, dtype, method, out]) | 標準指数分布から乱数を生成 |
standard_gamma(shape[, size, dtype, out]) | 標準ガンマ分布から乱数を生成 |
standard_normal([size, dtype, out]) | 標準正規分布から乱数を生成 |
standard_t(df[, size]) | t分布から乱数を生成 |
triangular(left, mode, right[, size]) | 三角分布から乱数を生成 |
uniform([low, high, size]) | 一様分布から乱数を生成 |
vonmises(mu, kappa[, size]) | フォン・ミーゼス分布から乱数を生成 |
wald(mean, scale[, size]) | ワルド分布(逆ガウス分布)から乱数を生成 |
weibull(a[, size]) | ワイブル分布から乱数を生成 |
zipf(a[,size]) | ジップ分布から乱数を生成 |
以上の一覧から、使用したいメソッドを選択して使いこなせるようにしましょう。
3. ver1.17より前と以後の違い
なお、以前は(NumPyのver1.17までは)、擬似乱数の生成は、以下のコードの様に関数(class RandomStateのメソッド)を使って行っていました
# 現在の乱数生成方法
import numpy as np
rng = np.random.default_rng()
rng.standard_normal()
# ver1.17までの乱数生成方法
np.random.standard_normal()
この変更が行われた最大の理由は、ジェネレータオブジェクト(class Generator)の方が、従来の方法よりも処理速度が速いため、大量のデータを扱う科学技術計算に適しているからです。
以下のコードで速度の違いを確認することができます。
import numpy as np
rng = np.random.default_rng()
%timeit -n 1 rng.standard_normal(100000)
%timeit -n 1 np.random.standard_normal(100000)
これに伴い、いくつかの関数(class RandomStateのメソッド)が変更・削除されています。それをまとめたものが以下です。
old | new |
---|---|
random_sample rand | random *random.randomと互換可能 |
randint random_integers | integers *オプション引数endpointが追加 |
tomaxint | 削除 *代わりに integers(0, np.iinfo(np.int_).max, endpoint=False)を使う |
seed | 削除 *代わりにSeedSequence.spawnを使う |
実務上、最も重要なのは次の二点でしょう。
- 0以上1未満の連続一様分布から小数の乱数を生成するために今まで頻繁に使われていたnumpy.random.random_sampleだが、現在ではGenerators.randomを使う。
- 離散一様分布から整数の乱数を生成するに今まで頻繁に使われていたrandom.randintだが、現在ではGenerator.integersを使う。
その他の主な違いは以下の通りです。
- ジェネレータメソッドの Generator.normal、Generator.exponential、Generator.gammaは256-stepジグラットメソッドを使用。これは従来のBox-Mullerやinverse CDFよりも2-10倍高速。
- オプション引数のdtypeは、np.float32かnp.float64を受け取り、選択した分布の単精度浮動小数点数(32bit)または倍精度浮動小数点数(64bit)の一様な確率変数を生成する。
- オプション引数のoutで、選択した分布の乱数を、既存の配列に代入することができる。
- すべてのビットジェネレータは、CTypes(PCG64.ctypes)とCFFI(PCG54.cffi)を通して、double型(倍精度浮動小数点数といわれる64ビットのデータ)、uint64型(64ビット符号なし整数)、uint32型(32ビット符号なし整数)を生成できる。これによって、numbaでビットジェネレータを使用できる。
- ビットジェネレータはCythonを通じて、ダウンストリーム・プロジェクト(プログラムの原作者や管理者の手を離れた個々の開発者による開発)に使用可能。
- Numpyのすべてのビットジェネレータが、シードを初期化ステートに変換するのにシードシーケンスを使う。
- choiceやpermutation、shuffleにキーワード引数axisが追加。これによって多次元配列からの乱数生成の方法が改善した。
注:
Generator(新しい方法)では、それまでNumpyのnormalで使われていたBox-Mullerメソッドは使用可能ではありません。正規分布や、RandomState.gammaやRandomState.standard_tのような正規分布に依存するその他の分布では、ジェネレータで正確なランダム値を再生成することは不可能です。ビットごとの後方互換性が必要ならRandomState(昔の方法)を使います。
4. 乱数生成の仕組み
ここでは、乱数生成の仕組みについて解説します。実務上は必要ないので、読まなくても全く問題ありません。
なお、理解にあたってはオブジェクト指向プログラミングの知識が必要不可欠です。これらについては、『【Python】オブジェクト指向プログラミングの概念と書き方』と『Pythonでのクラス(class)の使い方』をご確認ください。
ビットジェネレータとジェネレータ
さて、Numpyの乱数ルーチン(乱数生成の機能や処理を実装したひとまとまりのコードの集合)は、ビットジェネレータ(BitGenerator)とジェネレータ(Generator)を組み合わせて使うことで擬似乱数を生成するという仕組みになっています。
- ビットジェネレータ:乱数を生成するオブジェクト。これらは基本的に、32ビットか64ビットの符号なし整数型である。アルゴリズムとして、新型のclass GeneratorではPCG64、昔のclass RandomStateではMT19937を使っている。PCG64の方が高性能。
- ジェネレータ:ビットジェネレータからのランダムビットのシーケンスを、特定の確率分布(正規分布や一様分布、二項分布など)の数値のシーケンスに変換するオブジェクト。
ビットジェネレータは、ステートを管理し、random doubles と random unsigned の32ビット64ビットの値を生成するための関数を提供します。ランダムジェネレータは、ビットジェネレータが提供するストリームを引き取り、それを正規分布などの便利な分布に変換します。
この仕組みになっていることで、コードの重複を抑えながら、ビットジェネレータを使うことが可能になっています。また、これによってジェネレータは様々な種類のビットジェネレータで初期化(=使用可能な状態にすること)することができます。そして、様々な種類の乱数を取得できるように豊富な確率分布が公開されています(上述の確率分布の一覧を参照)。
なお、version1.17より前に使われていたランダムステート(class RandomState)の乱数ルーチンも引き続き使用可能ですが、単一のビットジェネレータ(MT19937)に限定されます。ランダムステートのインスタンスメソッドの一覧は以下のページに掲載されています。
ジェネレータとランダムステートの互換性
新しい方法であるGeneratorクラスは、従来のRandomStateクラスと互換性があります。どちらのクラスのインスタンスも、内部に、ビット系列を生成するためのビットジェネレータのインスタンスを保持しています(これはgen.bit_generator属性で確認することができます)。
次のようなコードは、従来のRandomStateと新型であるGeneratorの両方を適用できるようにするコードとして使うことができます。ただし、その場合はRandomStateとGeneratorの細かな違いは理解しておくべきです。
try:
rng_integers = rng.integers
except AttributeError:
rng_integers = rng.randint
a = rng_integers(1000)
ジェネレータオブジェクトは、ランダムステートオブジェクトと似ていますが、オブジェクトの初期化メソッド default_rng は、ビットジェネレータPCG64を、唯一の引数として渡します。
from numpy.random import default_rng
rng = default_rng(12345)
rng.random()
なお、ビットジェネレータインスタンスを使って、ジェネレータを直接インスタンス化することもできます。例えば、以前のversionで使っていたMT19937アルゴリズムを使うには、それを直接Generatorに渡します。
from numpy.random import Generator, MT19937
rng = Generator(MT19937(12345))
rng.random()
4. まとめ
以上が、乱数生成のために提供されているNumpyのrandomモジュールです。実務上、重要なのは「1. randomモジュールの使い方」なので、そこで乱数の生成方法をしっかりと抑えておきましょう。また、確率分布の全てを覚えておく必要はありませんが、様々な確率分布が用意されているということは頭に入れておきましょう。
コメント