Pythonライブラリ講座

【一覧表】NumPyの乱数生成(np.random)を分かりやすく解説!

前のページ|次のページ

7章では、NumPyの『乱数(rand, random_sample, randint, randn, normal)』『random.choice()によるデータの抽出』『乱数のシード』について解説します。

データ分析においてよく使われる乱数生成の関数を一覧表付きで網羅的に解説していますので、ぜひ最後までご覧ください!

本連載講座【Python ライブラリ編】では、データサイエンスに必要なPythonライブラリやその使い方を基礎から学ぶことができます。

NumPyPandasMatplotlibScipySeabornについて、初学者の方にも分かりやすいよう丁寧に解説しています。

さらに、学習した内容を定着させられるように各章演習問題を用意しています。

・Pythonでデータ分析ができるようになりたい

・Pythonの基礎事項は一通り学んだので、さらに深く学びたい

このように考えている方はTech Teacherが運営する【Python ライブラリ編】で、Pythonによるデータサイエンスの学習をすることをお勧めします!

なお、『Pythonについて全く知らない』・『Pythonの基礎事項がまだ分かっていない』という方は、まずコチラの【Python 基礎編】で基礎を一通り学習してからライブラリ編に取り掛かりましょう!

<ライブラリ編 目次>

<ライブラリの基礎>
1章:ライブラリとは

<NumPy>
2章:NumPyの概要と配列(ndarray)
3章:統計量や次元の取得/ソート
4章:配列のインデックス
5章:numpy.whereによる条件制御
6章:配列の結合/分割
7章:乱数

<SciPy>
8章:SciPyの概要と基本操作

<Pandas>
9章:SeriesDataFrame/統計量の取得
10章:データの読み込み/書き込み
11章:データの取り出し/追加
12章:データのソート
13章:データの結合
14章:階層型インデックス
15章:groupbyによる集計
16章:マッピング処理
17章:欠損値の扱い

<Matplotlib>
18章:Matplotlibの概要
19章:pyplotインターフェース
20章:オブジェクト指向インターフェース

<Seaborn>
21章:Seabornの概要と基本操作

乱数とは

乱数』とは、簡単に言うとランダムな数のことです。

例えば、サイコロを投げると1~6までの整数が完全にランダムで出現します。

そのため、サイコロの出目は乱数であると言うことができます。

NumPyでは、randomモジュールを用いることで様々な種類の乱数(正確には疑似乱数)を簡単に生成することができます。

乱数の種類と生成する関数

下表は、NumPyのrandomモジュールで生成できる主な乱数とそれらを生成する関数をまとめたものです。

関数乱数が従う分布
rand0.0以上1.0未満の一様分布
random_sample0.0以上1.0未満の一様分布
randint指定した範囲の一様分布(整数)
randn平均0, 標準偏差1の正規分布
normal指定した平均, 標準偏差の正規分布

なお、一様分布正規分布については以下の記事で詳しく解説しております。

いずれも基本的な確率分布なので、不安な方はぜひ参照してください!

rand

rand()』関数は、0.0以上1.0未満の一様分布に従う浮動小数点数の乱数を返します。

rand関数の引数の指定方法は以下の通りです。

・1次元配列の場合
np.random.rand(乱数の個数)

・多次元配列の場合
np.random.rand(1次元目の大きさ, 2次元目の大きさ, …)

では、実行例で確認してみましょう。

#0.0以上1.0未満の一様分布
x = np.random.rand(5)

print(x)
[0.55979423 0.81157485 0.89541704 0.11013642 0.50571754]

多次元配列を生成する場合は、各次元の大きさを引数として指定します。

#2×3の配列を生成する場合
y = np.random.rand(2, 3)

print(y)
[[0.14035342 0.37442591 0.16631856]
 [0.3992503  0.92944745 0.79290582]]

random_sample

random_sample()』関数はrand関数と同様に、0.0以上1.0未満の一様分布に従う浮動小数点数の乱数を返します。

random_sample関数の引数の指定方法は以下の通りです。

np.random.random_sample(配列の形状を表すタプル)

rand関数との違いは、配列の形状をタプルで指定するという点です。

なお、1次元配列の場合はタプルでなく整数を引数として指定することも可能です。

#0.0以上1.0未満の一様分布
x = np.random.random_sample(5)

print(x)
[0.08784795 0.54047262 0.04248919 0.94589097 0.71673511]
#2×3の配列を生成する場合
y = np.random.random_sample((2, 3))

print(y)
[[0.14581083 0.33595846 0.8615866 ]
 [0.6114845  0.53356767 0.78485187]]

randint

randint()』関数は、引数で指定した範囲内で一様分布に従う整数の乱数を返します。

randint関数の引数の指定方法は以下の通りです。

np.random.randint(下限, 上限, 配列の形状)

第一引数で下限、第二引数で上限、第三引数で配列の形状を指定します。

・多次元配列を生成する場合、配列の形状はタプルで指定する必要があることに注意しましょう
・生成される整数は(下限)以上(上限)未満の値であることに注意しましょう。

#50以上100未満の整数を5つ生成
x = np.random.randint(50, 100, 5)

#-10以上10未満の整数を3×4の配列で生成
y = np.random.randint(-10, 10, (3,4))

print(x)
print(y)
[59 62 68 52 54]
[[-7  8 -7  1]
 [ 0  1  3  8]
 [ 8  1 -5  9]]

なお、引数として整数(Nとする)を1つだけ指定した場合、0以上N未満の整数を1つだけ返します。

#0以上1000未満の整数を1つだけ生成
print(np.random.randint(1000))
308

randn

randn()』関数は、平均0, 標準偏差1の正規分布に従う浮動小数点数の乱数を返します。

randn関数の引数の指定方法は以下の通りです。

・1次元配列の場合
np.random.randn(乱数の個数)

・多次元配列の場合
np.random.randn(1次元目の大きさ, 2次元目の大きさ, …)

引数の指定方法はrand関数と同じです。

#0.0以上1.0未満の正規分布
x = np.random.randn(10)

print(x)
[ 0.72101398  1.57753075  0.01538328 -1.34453128  0.67869597 -1.88376924
 -1.37671326  0.43310617  0.07320538  1.06401262]

多次元配列もrand関数と同様の方法で取得することができます。

#2×3の配列を生成する場合
y = np.random.randn(2, 3)

print(y)
[[-1.52209929 -1.69464664  1.50052544]
 [ 0.62950238  0.59665093 -0.75334204]]

normal

normal()』関数は、指定した平均・標準偏差の正規分布に従う浮動小数点数の乱数を返します。

normal関数の引数の指定方法は以下の通りです。

np.random.normal(平均, 標準偏差, 配列の形状)

第一引数で正規分布の平均、第二引数で標準偏差、第三引数で配列の形状を指定します。

・多次元配列を生成する場合、配列の形状はタプルで指定する必要があることに注意しましょう

#平均100, 標準偏差10の正規分布
x = np.random.normal(100, 10, 5)

print(x)
[ 95.78584941 120.17366124 108.01494689 100.85792458 101.62315476]
#平均50, 標準偏差10の正規分布に従う2×3の配列を生成
y = np.random.normal(50, 10, (2,3))

print(y)
[[37.17746251 53.81397604 63.23320681]
 [52.42286056 39.38134457 58.53463845]]

random.choice()によるデータのランダム抽出

random.choice()』関数を用いると、配列から要素をランダムに抽出することができます。

random.choice()関数の書式は以下の通りです。

np.random.choice(配列, 形状, 重複の許可・不許可, 各要素の出現確率)

第一引数で元の配列、第二引数で取り出したときの形状(タプル)、第三引数で重複の許可・不許可、第四引数で配列の各要素の出現確率を設定します。

第三引数はTrueまたはFalseで指定し、省略した場合はTrue(重複あり)が適用されます。

第四引数では各要素の出現確率をリストで指定します。省略した場合、すべての要素が等確率で出現します。

それでは、実行例を見てみましょう。

x = np.array([1,2,3,4,5,6])

#xからランダムに10個抽出(重複あり)
x_cho = np.random.choice(x, 10)

#xからランダムに2×3個抽出(重複あり)
x_cho_arr = np.random.choice(x, (2,3))

print(x_cho)
print(x_cho_arr)
[2 6 1 6 6 6 4 2 4 1]
[[6 1 3]
 [6 4 1]]

第三引数をFalseとすると、重複なしで抽出が行われます。

このとき、抽出する個数がもとの配列の要素数を超えると正しく実行できません。

x = np.array([1,2,3,4,5,6])
#xからランダムに6個抽出(重複なし)
x_cho = np.random.choice(x, 6, replace=False)

print(x_cho)
[2 4 1 5 3 6]

第四引数にリストを渡して各要素の出現確率を指定することができます。

x = np.array([1,2,3,4,5,6])
#xからランダムに10個抽出(重複なし, 確率を指定)
x_cho = np.random.choice(x, 10, p=[0.1, 0.1, 0.1, 0.1, 0.1, 0.5])

print(x_cho)
[4 6 6 6 1 4 6 6 6 6]

確かに6が出る確率が他と比べて高くなっていることが分かります。

また、第一引数に配列ではなく0以上の整数値を渡すと、0以上指定した値未満の整数の配列から抽出が行われます。

#0以上100未満の整数配列から10個抽出
a = np.random.choice(100, 10)
print(a)
[37 25 77 72  9 20 80 69 79 47]

シードについて

ここまで紹介した乱数はすべてコンピューターで生成される疑似乱数と呼ばれるものであり、真の乱数とは異なります。

疑似乱数を生成する際に初期値をもとに計算を行うのですが、np.random.seed()を用いることでその初期値を自分で設定できます。

シード値を設定すると乱数生成の初期値が固定され、異なる環境でも毎回同じ結果が返ってくるようになります。

では実際に、シード値を0として以下のコードを実行してみましょう。

#シード値を設定
np.random.seed(0)

#平均0, 標準偏差1の正規分布に従う数を10個取得
x = np.random.randn(10)
print(x)
[ 1.76405235  0.40015721  0.97873798  2.2408932   1.86755799 -0.97727788
  0.95008842 -0.15135721 -0.10321885  0.4105985 ]

みなさんの環境でもこの実行例と同じような結果が得られたと思います。

シード値の設定は、データ分析において一貫性を担保したり、ソースコードを他人と共有した時に実行結果が変わらないようにするなどの目的で行われます。

7章の練習問題

以下の練習問題を解いてみましょう。

練習問題

問1. 以下の(1)~(4)を求めて出力してください。ただし、各問のプログラムの初めで、np.random.seed()を用いてシード値を0に設定してから実行するようにしてください。

(1) 0.0以上1.0未満の一様分布から10個の浮動小数点数を取り出した1次元配列
(2) 0以上100未満の整数の一様分布から値を取り出した2×3の配列
(3) 平均0、標準偏差1の正規分布から10個の浮動小数点数を取り出した1次元配列
(4) (3)で生成した配列から8個の要素を重複を許して取り出した1次元配列

問2(応用編). 「0以上1未満の一様分布から値を1つずつ取り出していくとき、総和が1になるまでに取り出した回数n」を求める操作を100000回繰り返し、100000回の試行におけるnの平均を出力してください。ただし、シード値は0とします。

解答

問1(クリックして解答を表示)

(1)

#(1)
np.random.seed(0)

x1 = np.random.rand(10)
print(x1)
[0.5488135  0.71518937 0.60276338 0.54488318 0.4236548  0.64589411
 0.43758721 0.891773   0.96366276 0.38344152]

(2)

#(2)
np.random.seed(0)

x2 = np.random.randint(100)
print(x2)
44

(3)

#(3)
np.random.seed(0)

x3 = np.random.randn(10)
print(x3)
[ 1.76405235  0.40015721  0.97873798  2.2408932   1.86755799 -0.97727788
  0.95008842 -0.15135721 -0.10321885  0.4105985 ]

(4)

#(4)
np.random.seed(0)

x4 = np.random.choice(x3, 8)
print(x4)
[-0.97727788  1.76405235  2.2408932   2.2408932  -0.15135721  0.4105985
  2.2408932  -0.97727788]

問2(クリックして解答を表示)

N = 100000 #試行回数
n_ave = 0 #取り出した回数の平均値

np.random.seed(0) #シード値の設定

for i in range(N):
    temp_sum = 0 #累積和
    n = 0 #取り出した回数
    #総和が1を超えるまで取り出す
    while temp_sum <= 1:
        temp_sum += np.random.rand()
        n += 1
    
    n_ave += n

#平均値を計算(取り出した回数の合計を試行回数で割る)
n_ave /= N
print(n_ave)
2.71626

ちなみにこのようにして求めた値は、試行回数を大きくすると自然対数の底e(=2.718…)に近づくということが知られています。

次のページへ

【Python】SciPyとは?行列式や最適化を簡単に計算する方法 前のページ|次のページ 8章ではPythonの科学計算ライブラリの一つである『SciPy』およびSciPyの使用例として...