75791


以前に「[標準のrandomモジュールとnumpy.randomの違い](https://ja.stackoverflow.com/questions/47782/%E6%A8%99%E6%BA%96%E3%81%AErandom%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%A8numpy-
random%E3%81%AE%E9%81%95%E3%81%84)」について質問されていることは承知しています。また、その回答で計算速度の違いによるものとの認識をしております。

今回は、標準のrandom.shuffleとnumpy.random.shuffleの「結果の違い」について質問させていただきたいです。

2D arrayのRowをShuffleするだけのコードで、自分では理解できないことが発生しました。  
まずサンプルコードを提示して、質問を後に記述させていただきます。

[code]

    #!/usr/bin/python3.6
    
    import numpy as np
    import random
    
    T1 = [[1,2],[3,4],[5,6],[7,8],[9,10]];
    T2 = [[1,2],[3,4],[5,6],[7,8],[9,10]];
    
    T1 = np.array(T1);
    T2 = np.array(T2);
    
    for i in range(0, 10):
        print ("trial:", i)
        print ("T1:", T1[0], T1[1], T1[2], T1[3], T1[4])
        np.random.shuffle(T1)
        print ("T1_np_random:", T1[0], T1[1], T1[2], T1[3], T1[4])
    
        print ("T2:", T2[0], T2[1], T2[2], T2[3], T2[4])
        random.shuffle(T2)
        print ("T2_random:", T2[0], T2[1], T2[2], T2[3], T2[4])
        print ();
    
[/code]

このコードでは、5x2の2D arrayを２個(T1, T2)用意しています。  
この２つのarrayに対して、それぞれrandom.shuffleとnp.random.shuffleを10回行います。  
shuffleする度に結果を表示するだけのコードです。

randomを使いますので、毎回結果が違いますが、以下に一例をお示しします。

[code]

    trial: 0
    T1: [1 2] [3 4] [5 6] [7 8] [ 9 10]
    T1_np_random: [ 9 10] [7 8] [3 4] [5 6] [1 2]
    T2: [1 2] [3 4] [5 6] [7 8] [ 9 10]
    T2_random: [1 2] [3 4] [5 6] [1 2] [5 6]      <- [1 2][5 6]に置き換わっている？
    
    trial: 1
    T1: [ 9 10] [7 8] [3 4] [5 6] [1 2]
    T1_np_random: [1 2] [7 8] [3 4] [ 9 10] [5 6]
    T2: [1 2] [3 4] [5 6] [1 2] [5 6]
    T2_random: [1 2] [1 2] [5 6] [5 6] [3 4]
    
    trial: 2
    T1: [1 2] [7 8] [3 4] [ 9 10] [5 6]
    T1_np_random: [5 6] [ 9 10] [1 2] [7 8] [3 4]
    T2: [1 2] [1 2] [5 6] [5 6] [3 4]
    T2_random: [1 2] [1 2] [1 2] [5 6] [1 2]　　　<- [1 2]に置き換わっている？
    
    trial: 3
    T1: [5 6] [ 9 10] [1 2] [7 8] [3 4]
    T1_np_random: [ 9 10] [5 6] [7 8] [3 4] [1 2]
    T2: [1 2] [1 2] [1 2] [5 6] [1 2]
    T2_random: [1 2] [1 2] [1 2] [1 2] [1 2]
    
    trial: 4
    T1: [ 9 10] [5 6] [7 8] [3 4] [1 2]
    T1_np_random: [ 9 10] [3 4] [1 2] [5 6] [7 8]
    T2: [1 2] [1 2] [1 2] [1 2] [1 2]
    T2_random: [1 2] [1 2] [1 2] [1 2] [1 2]
    
    trial: 5
    T1: [ 9 10] [3 4] [1 2] [5 6] [7 8]
    T1_np_random: [3 4] [5 6] [ 9 10] [1 2] [7 8]
    T2: [1 2] [1 2] [1 2] [1 2] [1 2]
    T2_random: [1 2] [1 2] [1 2] [1 2] [1 2]
    
    trial: 6
    T1: [3 4] [5 6] [ 9 10] [1 2] [7 8]
    T1_np_random: [ 9 10] [1 2] [7 8] [3 4] [5 6]
    T2: [1 2] [1 2] [1 2] [1 2] [1 2]
    T2_random: [1 2] [1 2] [1 2] [1 2] [1 2]
    
    trial: 7
    T1: [ 9 10] [1 2] [7 8] [3 4] [5 6]
    T1_np_random: [3 4] [ 9 10] [7 8] [1 2] [5 6]
    T2: [1 2] [1 2] [1 2] [1 2] [1 2]
    T2_random: [1 2] [1 2] [1 2] [1 2] [1 2]
    
    trial: 8
    T1: [3 4] [ 9 10] [7 8] [1 2] [5 6]
    T1_np_random: [1 2] [3 4] [7 8] [5 6] [ 9 10]
    T2: [1 2] [1 2] [1 2] [1 2] [1 2]
    T2_random: [1 2] [1 2] [1 2] [1 2] [1 2]
    
    trial: 9
    T1: [1 2] [3 4] [7 8] [5 6] [ 9 10]
    T1_np_random: [7 8] [5 6] [ 9 10] [1 2] [3 4]
    T2: [1 2] [1 2] [1 2] [1 2] [1 2]
    T2_random: [1 2] [1 2] [1 2] [1 2] [1 2]
    
[/code]

結果の中にもお示ししましたが、trial 0(一回だけshuffleした状態)では、shuffleをするだけでなく[7 8] [ 9 10]が[1 2][5
6]に置き換わっています。またtrialごとに置き換わりが起る時と起こらない時があります。  
random.shuffleを使ったT2は、最終的には全て同じエレメントに置き換わっています。

このエラー（？）は、標準のrandomモジュールとnumpy.randomの計算速度の違いだけは説明できないと思います。

私の質問ですが、

  1. 単純にnumpy.arrayを(numpyではない)random.shuffleを行うことは避けるべきでしょうか？
  2. なぜこのようなエラーが出る原因は何でしょうか？

もし誰か説明できる方がおられましたら、ご教授をお願いします。  
よろしくお願いします。


CC BY-SA 4.0
2021-05-11T16:43:29.183
75790
2021-05-12T00:23:53.917
2021-05-12T00:23:53.917
3060
11048
question
python numpy random
標準のrandom.shuffleとnumpy.random.shuffleの結果の違いについて


原因は `T2` が 2次元の `numpy.ndarray` 型インスタンスだからです。

`random.shuffle()` のソースコードは以下になります。

[cpython/random.py at
3.6](https://github.com/python/cpython/blob/3.6/Lib/random.py#L263)

[code]

    def shuffle(self, x, random=None):
        """Shuffle list x in place, and return None.
        Optional argument random is a 0-argument function returning a
        random float in [0.0, 1.0); if it is the default None, the
        standard random.random will be used.
        """
    
        if random is None:
            randbelow = self._randbelow
            for i in reversed(range(1, len(x))):
                # pick an element in x[:i+1] with which to exchange x[i]
                j = randbelow(i+1)
                x[i], x[j] = x[j], x[i]
        else:
                      :
    
[/code]

操作としては `x[i], x[j] = x[j], x[i]` になります(tuple unpacking)。これを `T2` に対して行ってみます。

[code]

    >>> T2 = [[1,2],[3,4],[5,6],[7,8],[9,10]]
    >>> T2 = np.array(T2)
    >>> type(T2)
    <class 'numpy.ndarray'>
    >>> T2
    array([[ 1,  2],
           [ 3,  4],
           [ 5,  6],
           [ 7,  8],
           [ 9, 10]])
    >>> T2.shape
    (5, 2)
    
    >>> T2[0], T2[1] = T2[1], T2[0]
    >>> T2
    array([[ 3,  4],
           [ 3,  4],
           [ 5,  6],
           [ 7,  8],
           [ 9, 10]])
    
[/code]

`T2[0]` の値と `T2[1]` の値を交換する際に、内部では作業領域(一時変数)が確保されて `T2[0]` の値がコピーされるのですが、この場合は
`T2[0]` が `numpy.ndarray` 型のインスタンスなので `T2[0]` への参照(のコピー)が割り当てられる事になります(shallow
copy)。

[code]

    ## reference
    >>> T2 = np.array([[1,2],[3,4],[5,6],[7,8],[9,10]])
    >>> v = T2[0]  ## assignment with shallow copy
    >>> v
    array([1, 2])
    >>> T2[0] = T2[1]
    >>> T2[0]
    array([3, 4])
    >>> v
    array([3, 4])
    
    ## numpy's swap array items
    >>> T2 = np.array([[1,2],[3,4],[5,6],[7,8],[9,10]])
    >>> T2[[0, 1]] = T2[[1, 0]]
    >>> T2
    array([[ 3,  4],
           [ 1,  2],
           [ 5,  6],
           [ 7,  8],
           [ 9, 10]])
    
[/code]

上記の様に `numpy.ndarray` での要素の swap 操作を行なうと想定通りの結果になります。

>   1. 単純にnumpy.arrayを(numpyではない)random.shuffleを行うことは避けるべきでしょうか？
>

はい、多次元の `numpy.ndarray` 型インスタンスに `random.shuffle()`
を使用するのは避けるべき、となります。1次元(いわゆる `vector`)であれば `ndarray` の要素は scalar
になるので上記の様な問題は発生しないのですけれども。


CC BY-SA 4.0
2021-05-11T19:07:54.870
75791
2021-05-11T19:56:11.383
2021-05-11T19:56:11.383
75790
answer