NumPy, randomで乱数生成(np.random.rand, normalなど)

Modified: | Tags: Python, NumPy

NumPyでは、numpy.randomモジュールを利用して乱数を生成できる。

NumPy1.17以降はGeneratorインスタンスを利用する方法が推奨されているが、従来のnp.random.rand()np.random.normal()などの関数も使用可能(1.26.1時点)。

Python標準ライブラリのrandomモジュールについては以下の記事を参照。

本記事のサンプルコードのNumPyのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。

import numpy as np

print(np.__version__)
# 1.26.1

numpy.randomの使い方

Generatorインスタンスを利用(バージョン1.17以降)

まず、NumPy1.17以降で推奨されているGeneratorインスタンスを利用する方法を説明する。

基本的な使い方

np.random.default_rng()Generatorインスタンスを生成する。

rng = np.random.default_rng()
print(rng)
# Generator(PCG64)

Generatorインスタンスから各種メソッドを呼ぶことで、様々な種類の乱数を生成できる。

例えば、random()メソッドは0.0以上・1.0未満の浮動小数点数floatの一様分布の乱数を生成する。

引数sizeで形状を指定する。デフォルト(size=None)ではスカラー値、整数intを指定すると一次元の配列ndarray、タプルを指定するとその形状shapeの配列ndarrayが生成される。

random()メソッドでは第一引数がsize

print(rng.random())
# 0.2281556131299135

print(type(rng.random()))
# <class 'float'>

print(rng.random(3))
# [0.3888496  0.9058742  0.74585675]

print(type(rng.random(3)))
# <class 'numpy.ndarray'>

print(rng.random((2, 3)))
# [[0.42818743 0.22573104 0.33022384]
#  [0.64782123 0.82035051 0.31118108]]

print(type(rng.random((2, 3))))
# <class 'numpy.ndarray'>

例では省略しているが、タプルの要素を増やせば三次元以上の配列ndarrayも生成可能。

その他のメソッドについては後述。

シードの固定

np.random.default_rng()の引数に任意の非負整数値をシードとして指定できる。省略した場合は毎回異なるシードで初期化される。

rng_1 = np.random.default_rng(1234)
print(rng_1.random())
# 0.9766997666981422

rng_2 = np.random.default_rng(1234)
print(rng_2.random())
# 0.9766997666981422

乱数シードについての詳細は公式ドキュメントを参照。

乱数生成器の変更

np.random.default_rng()では、乱数生成器としてPCG64が使われる。

乱数生成器を選択したい場合はコンストラクタnp.random.Generator()を使う。引数としてBitGeneratorを指定する。PCG64のほか、MT19937やSFC64などが提供されている。

BitGeneratorの生成時にシードの指定が可能(もちろん省略してもよい)。乱数生成器が何であっても利用できるメソッドは同じ。

rng_mt = np.random.Generator(np.random.MT19937(1234))
print(rng_mt)
# Generator(MT19937)

print(rng_mt.random())
# 0.12038356302504949

レガシーな方法(RandomStateインスタンス、関数)

NumPy1.17以降はGeneratorインスタンスの利用が推奨されているが、従来の方法も利用可能。

あくまでも後方互換性のために残されているものなので、これから新しいコードを書くのであれば、より高速で今後も改良されていくGeneratorを利用するほうがよい。

Generator and its associated infrastructure was introduced in NumPy version 1.17.0. There is still a lot of code that uses the older RandomState and the functions in numpy.random. While there are no plans to remove them at this time, we do recommend transitioning to Generator as you can. The algorithms are faster, more flexible, and will receive more improvements in the future. Random sampling (numpy.random) — NumPy v1.26 Manual

RandomStateインスタンスを利用

Generatorが導入される前はRandomStateが利用されていた。

np.random.RandomState()でインスタンスを生成し、各種メソッドを呼ぶ。

rs = np.random.RandomState()
print(rs)
# RandomState(MT19937)

print(rs.rand())
# 0.07714411293114853

print(rs.rand(3))
# [0.41864167 0.24060533 0.33237037]

print(rs.rand(2, 3))
# [[0.44510064 0.81320106 0.79076827]
#  [0.84813432 0.83270079 0.10677157]]

シードの指定も可能。

rs_1 = np.random.RandomState(1234)
print(rs_1.rand())
# 0.1915194503788923

rs_2 = np.random.RandomState(1234)
print(rs_2.rand())
# 0.1915194503788923

関数を利用

RandomStateのメソッドは関数として利用できるようになっている。

print(np.random.rand(2, 3))
# [[0.22135358 0.56604746 0.03200072]
#  [0.04187504 0.32511305 0.0366836 ]]

シードの指定にはnp.random.seed()を使う。

np.random.seed(1234)
print(np.random.rand())
# 0.1915194503788923

np.random.seed(1234)
print(np.random.rand())
# 0.1915194503788923

一様分布の乱数生成: np.random.rand, randintなど

0.0以上・1.0未満の浮動小数点数

0.0以上・1.0未満([0.0, 1.0))の浮動小数点数の一様分布の乱数はGeneratorrandom()メソッドで生成できる。

第一引数がsize

rng = np.random.default_rng()

print(rng.random())
# 0.11769302730981768

print(rng.random(3))
# [0.58905312 0.90484592 0.90395364]

print(rng.random((2, 3)))
# [[0.59463288 0.19670697 0.95594319]
#  [0.94165422 0.18974122 0.33570092]]

従来のnp.random.random_sample()np.random.rand()に相当。np.random.rand()では形状shapeをタプルではなく位置引数d0, d1, ..., dnとして順に指定する。

print(np.random.random_sample((2, 3)))
# [[0.34446051 0.76218429 0.37339732]
#  [0.22533506 0.00148215 0.70198151]]

print(np.random.rand(2, 3))
# [[0.78823483 0.00620854 0.96619249]
#  [0.32573769 0.36434893 0.04472163]]

任意の範囲の浮動小数点数

任意の範囲の浮動小数点数の一様分布の乱数はGeneratoruniform()メソッドで生成できる。

第一引数low、第二引数high、第三引数sizeを指定する。lowは範囲に含まれるがhighは含まれない([low, high))。

rng = np.random.default_rng()

print(rng.uniform(-50.0, 50.0))
# 17.611381123655846

print(rng.uniform(-50.0, 50.0, 3))
# [ 5.80448109  6.64291183 22.27752257]

print(rng.uniform(-50.0, 50.0, (2, 3)))
# [[ 12.23855165  -9.97903127 -38.40667299]
#  [-14.42414448 -45.04563195  14.37486871]]

従来のnp.random.uniform()に相当する。

print(np.random.uniform(-50.0, 50.0, (2, 3)))
# [[-25.63149057  16.80691914  20.69418218]
#  [ 32.53480087 -10.83462837  15.18192629]]

任意の範囲の整数

任意の範囲の整数の一様分布の乱数はGeneratorintegers()メソッドで生成できる。

第一引数low、第二引数high、第三引数sizeを指定する。

第二引数highを省略すると、0以上・low未満([0, low))の範囲となる。

rng = np.random.default_rng()

print(rng.integers(100))
# 24

print(rng.integers(100, size=3))
# [ 9 25 27]

print(rng.integers(100, size=(2, 3)))
# [[78  2 42]
#  [25  6 16]]

第二引数highを指定すると、low以上・high未満([low, high))の範囲となる。

print(rng.integers(100, 200, (2, 3)))
# [[134 142 129]
#  [154 180 103]]

引数endpointTrueにすると、上限値も範囲に含まれる([0, low]または[low, high])。

print(rng.integers(100, 200, (2, 3), endpoint=True))
# [[170 157 172]
#  [121 200 191]]

従来のnp.random.randint()に相当する。ただし、randint()には引数endpointは無く、常に上限値は範囲に含まれない。

print(np.random.randint(100, 200, (2, 3)))
# [[120 121 138]
#  [159 140 197]]

正規分布の乱数生成: np.random.randn, normalなど

標準正規分布(平均0・標準偏差1)

平均0、標準偏差1(分散1)の正規分布(標準正規分布)に従う乱数はGeneratorstandard_normal()メソッドで生成できる。

第一引数がsize

rng = np.random.default_rng()

print(rng.standard_normal())
# 0.08496250507973527

print(rng.standard_normal(3))
# [ 1.21769711  1.81125807 -0.87641522]

print(rng.standard_normal((2, 3)))
# [[-1.79057437  0.30921794  0.78466028]
#  [ 0.9669326  -0.23709503  0.17453728]]

従来のnp.random.standard_normal()np.random.randn()に相当。np.random.randn()では形状shapeをタプルではなく位置引数d0, d1, ..., dnとして順に指定する。

print(np.random.standard_normal((2, 3)))
# [[ 1.59966341 -2.3255136  -0.90314338]
#  [-0.90576614  0.45550908 -0.88593054]]

print(np.random.randn(2, 3))
# [[ 0.13158995 -0.34018141  0.26866075]
#  [-0.35383739 -1.85442183 -0.1316313 ]]

任意の平均・標準偏差

任意の平均・標準偏差の正規分布(ガウス分布)に従う乱数はGeneratornormal()メソッドで生成できる。

第一引数loc(平均)、第二引数scale(標準偏差)、第三引数sizeを指定する。

rng = np.random.default_rng()

print(rng.normal(2, 2.5))
# 1.4099095964428612

print(rng.normal(2, 2.5, 3))
# [-1.02713245  2.80603029  6.84660003]

print(rng.normal(2, 2.5, (2, 3)))
# [[ 4.23662681  2.36596096 -0.08259898]
#  [ 2.27105608  1.58751381 -2.42582859]]

従来のnp.random.normal()に相当する。

print(np.random.normal(2, 2.5, (2, 3)))
# [[ 4.43180284  3.74464437  0.79083482]
#  [-0.3631814   0.39375677  7.8085918 ]]

その他の分布

一様分布や正規分布の他にも、様々な分布に従う乱数を生成できる。一覧は公式ドキュメントを参照。

二項分布やベータ分布、ガンマ分布、ポアソン分布などがある。指定できるパラメータなどは公式ドキュメントを参照。

rng = np.random.default_rng()

print(rng.binomial(10, 0.5, (2, 3)))
# [[3 7 5]
#  [4 3 5]]

print(rng.beta(2, 2, (2, 3)))
# [[0.84643935 0.50674151 0.30812967]
#  [0.52728096 0.76007311 0.26255972]]

print(rng.gamma(5, 1, (2, 3)))
# [[5.6484851  8.28210475 2.65957385]
#  [2.00776839 6.65851101 7.77808412]]

print(rng.poisson(4, (2, 3)))
# [[1 6 3]
#  [5 2 1]]

関連カテゴリー

関連記事