scikit-learnにてrandom_stateを指定したときの動作が何のオブジェクトを加えたかによって変わるので備忘録的にメモ。

from typing import Union, Optional

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

data = load_iris(as_frame=True)
X = data.data
y = data.target

数字で指定する場合

def test_train_test_split(
    iter: int, random_state: Optional[Union[np.random.RandomState, int]]
) -> None:
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, random_state=random_state, test_size=0.2
    )
    print(f"iter: {iter}")
    display(y_train.to_frame().head(), y_test.to_frame().head())


for iter in range(3):
    test_train_test_split(iter=iter, random_state=0)

iter: 0
target
137 2
84 1
27 0
127 2
132 2
target
114 2
62 1
33 0
107 2
7 0
iter: 1
target
137 2
84 1
27 0
127 2
132 2
target
114 2
62 1
33 0
107 2
7 0
iter: 2
target
137 2
84 1
27 0
127 2
132 2
target
114 2
62 1
33 0
107 2
7 0

同じ結果が得られる。

numpy.random.RandomStateで指定する場合

事前に変数として持っておくケース

rng_ = np.random.RandomState(0)
for iter in range(3):
    test_train_test_split(iter=iter, random_state=rng_)

iter: 0
target
137 2
84 1
27 0
127 2
132 2
target
114 2
62 1
33 0
107 2
7 0
iter: 1
target
52 1
131 2
113 2
98 1
30 0
target
92 1
141 2
130 2
119 2
48 0
iter: 2
target
107 2
90 1
9 0
147 2
148 2
target
85 1
137 2
77 1
108 2
122 2

同じ結果にならない。

直接指定する場合

for iter in range(3):
    test_train_test_split(iter=iter, random_state=np.random.RandomState(0))

iter: 0
target
137 2
84 1
27 0
127 2
132 2
target
114 2
62 1
33 0
107 2
7 0
iter: 1
target
137 2
84 1
27 0
127 2
132 2
target
114 2
62 1
33 0
107 2
7 0
iter: 2
target
137 2
84 1
27 0
127 2
132 2
target
114 2
62 1
33 0
107 2
7 0

数字で指定したのと同じ効果。再現できる。

考え方

上記の例だと少し複雑だが、簡単な例で書き換えると以下と同じ。

for i in range(3):
    rng_ = np.random.RandomState(0)
    print(rng_.rand())
0.5488135039273248
0.5488135039273248
0.5488135039273248
rng_ = np.random.RandomState(0)
for i in range(3):
    print(rng_.rand())
0.5488135039273248
0.7151893663724195
0.6027633760716439