HOWTO · NumPy

NumPy は 2つの配列をシャッフルする

Python で対応する 2つの配列をシャッフルするために使用できる主なメソッドは、sklearn.utils.shuffle()関数、numpy.random.shuffle()関数、および numpy.random.permutation()関数の 3つです。

このページの内容

関連する2つの配列をシャッフルするとは、行の順序を両方で一緒に変えることです。一方の配列の行 i がもう一方の配列の行 i を表している場合(たとえば、特徴量の行とそのラベル)、両方の配列に同じ並べ替えを適用する必要があります。別々にシャッフルすると、その対応関係が崩れます。以下の例では、2列の特徴量配列とラベル配列を使い、各行の値を見て対応関係を確認できるようにしています。

2つの配列をシャッフルし、対応する行をそろえる

両方の配列に同じ行順を適用します。各入力の第1次元の長さは同じでなければなりません。複数の列を持つ配列では、共通の順序で行全体を選ぶため、行内の値の対応は保たれます。

特徴量の行とラベルの対応を確認しやすいよう、順序を固定した次の例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

import numpy as np

features = np.array([[10, 100], [20, 200], [30, 300]])
labels = np.array([1, 2, 3])
order = np.array([2, 0, 1])

print(features[order])
print(labels[order])
[[ 30 300]
 [ 10 100]
 [ 20 200]]
[3 1 2]

固定した順序は説明用です。ランダムな並びにしたい場合は、以下のいずれかの方法で生成した1つの順序を使います。

sklearn.utils.shuffle() を使う

sklearn.utils.shuffle() は複数の配列を受け取り、すべてに一貫した順序を適用して、代入できるシャッフル済み配列を返します。scikit-learn がインストールされている場合は、まず from sklearn.utils import shuffle で関数を読み込み、次に features_shuffled, labels_shuffled = shuffle(features, labels) のように呼び出します。関連する配列をすべて同じ呼び出しに渡せば、行の対応が保たれます。すでに scikit-learn をプロジェクトで使っている場合に便利です。呼び出すたびにランダムな順序は変わることがあり、この関数は元の配列をその場で並べ替えるのではなく、代入用の結果を返します。

このインポートと呼び出しには scikit-learn が必要で、すべての Python 環境に導入されているわけではありません。利用できる場合は、呼び出しの前にインポートし、返された両方の配列を代入してください。この依存関係を必要とする呼び出しは、実行例ではなく構文例として示しています。以下の NumPy の各節では、scikit-learn を使わない実行可能な方法を紹介します。

NumPy で共通の並べ替え順を使う

numpy.random.permutation(features.shape[0]) は、行番号をランダムな順序に並べた配列を作ります。それを order に保存し、features[order] と labels[order] で両方の配列を選択します。NumPy の高度なインデックス指定は新しい結果を作るため、元の配列は元の順序のまま残ります。order を保持しておけば、同じ並びを再利用したり、ほかの配列にも適用したり、後の処理のためにデータと一緒に保存したりできます。インデックス順を生成して保持するこの方法なら、特定の乱数状態用インターフェースに依存せずに済みます。

ここでは1つの順序を生成し、両方の配列のインデックスに使います。出力される行順は変わりますが、特徴量とラベルの組み合わせは保たれます。

このセクションのコードと表示されるラベルや出力は英語のままです。

import numpy as np

features = np.array([[10, 100], [20, 200], [30, 300]])
labels = np.array([1, 2, 3])
order = np.random.permutation(features.shape[0])

features_shuffled = features[order]
labels_shuffled = labels[order]
print(features_shuffled)
print(labels_shuffled)

NumPy で共通のインデックス配列をシャッフルする

numpy.random.shuffle() は入力されたシーケンスをその場で変更します。order = np.arange(features.shape[0]) でインデックス配列を作り、np.random.shuffle(order) でその配列をシャッフルしてから、features[order] と labels[order] で両方の元配列を選択します。元データを変更せずに、行の順序を明示できます。この方法は配列を追加する場合にも使えます。対応する配列すべてに同じシャッフル済みインデックスを適用してください。その場でシャッフルされるのは order だけで、インデックス指定の結果は別の配列です。後続のコードが order を変更する可能性があり、この並びを保つ必要がある場合は、order のコピーを保存してください。

この例で変更されるのは order だけです。元の2つの配列には、同じシャッフル済みシーケンスをインデックスとして使います。

このセクションのコードと表示されるラベルや出力は英語のままです。

import numpy as np

features = np.array([[10, 100], [20, 200], [30, 300]])
labels = np.array([1, 2, 3])
order = np.arange(features.shape[0])
np.random.shuffle(order)

features_shuffled = features[order]
labels_shuffled = labels[order]
print(features_shuffled)
print(labels_shuffled)

形状、変更される対象、再現性を確認する

配列の第1次元は互換性が必要です。つまり、軸0に沿った長さは一致しなければなりませんが、それ以外の次元は異なっていてもかまいません。たとえば、特徴量の行を並べた行列と、1次元のラベル配列を組み合わせられます。インデックス順を適用する前に先頭のサイズを確認し、すべての配列で各インデックスが有効であることを確かめてください。

3つの方法では、変更される対象が異なります。sklearn.utils.shuffle() は代入用のシャッフル済み配列を返します。numpy.random.permutation() は新しいインデックス順を返し、それを配列のインデックス指定に使うと並べ替えられた結果が作られます。numpy.random.shuffle() は受け取ったインデックス配列を変更しますが、ここでの例では元の配列を直接シャッフルしません。どの方法でも、関連する配列すべてに同じ順序を使った場合に限り、対応関係が保たれます。

同じ並びを再現するには、生成したインデックス配列を保持して元の配列に再度適用するか、データと一緒に保存します。同じインデックス順を再利用すれば、特定の乱数状態用 API に頼らず、同じ並びを作れます。新しい独立したシャッフルが必要なら、新しい順序を生成してください。これらの例は、特定の出力順を保証するものではありません。