【问题标题】:Why does random.shuffle fail on numpy lists?为什么 random.shuffle 在 numpy 列表上失败?
【发布时间】:2020-02-10 00:21:34
【问题描述】:

我有一个行向量数组,我在其上运行 random.shuffle:

#!/usr/bin/env python                                                                                                                                                                                                                                                

import random
import numpy as np

zzz = np.array([[0.1, 0.2, 0.3, 0.4, 0.5],
                [0.6, 0.7, 0.8, 0.9, 1. ]])

iterations = 100000
f = 0
for _ in range(iterations):
    random.shuffle(zzz)
    if np.array_equal(zzz[0], zzz[1]):
        print(zzz)
        f += 1

print(float(f)/float(iterations))

在 99.6% 到 100% 的时间里,在 zzz 上使用 random.shuffle 会返回一个包含相同元素的列表,例如

$ ./test.py
...
[[ 0.1  0.2  0.3  0.4  0.5]
 [ 0.1  0.2  0.3  0.4  0.5]]
0.996

使用numpy.random.shuffle 似乎可以通过此测试并正确地洗牌行向量。我很想知道为什么random.shuffle 失败了。

【问题讨论】:

  • 你应该给random.shuffle一个列表,例如zl = list(zzz)zl = zzz.tolist()。不要指望为列表设计的 Python 函数来正确处理二维数组,尤其是在处理就地更改时。
  • random.shuffle 返回 none 而不是修改后的列表。见:stackoverflow.com/questions/17649875/…>
  • @rpm10 是对 OP 的回应吗?

标签: python numpy random shuffle


【解决方案1】:

如果您查看 random.shuffle 的 code,它会按以下方式执行交换:

x[i], x[j] = x[j], x[i]

numpy.array 会失败,不会引发任何错误。示例:

>>> zzz[1], zzz[0] = zzz[0], zzz[1]
>>> zzz
array([[0.1, 0.2, 0.3, 0.4, 0.5],
       [0.1, 0.2, 0.3, 0.4, 0.5]])

原因是 Python 首先完全计算右侧,然后进行赋值(这就是为什么使用 Python 单行交换是可能的)但对于 numpy 数组,这不是 True。

numpy

>>> arr = np.array([[1],[1]])
>>> arr[0], arr[1] = arr[0]+1, arr[0]
>>> arr
array([[2],
       [2]])

Python

>>> l = [1,1]
>>> l[0], l[1] = l[0]+1, l[0]
>>> l
[2, 1]

【讨论】:

  • 这正是我希望看到的答案,它使错误变得清晰。谢谢!
【解决方案2】:

试试这样:

#!/usr/bin/env python                                                                                                                                                                                                                                                

import random
import numpy as np

zzz = np.array([[0.1, 0.2, 0.3, 0.4, 0.5],
                [0.6, 0.7, 0.8, 0.9, 1. ]])

iterations = 100000
f = 0
for _ in range(iterations):
    random.shuffle(zzz[0])
    random.shuffle(zzz[1])
    if np.array_equal(zzz[0], zzz[1]):
        print(zzz)
        f += 1

print(float(f)/float(iterations))

【讨论】:

  • 谢谢,我不是要打乱行向量中的元素,但很好奇为什么 numpy 和原生 Python 库之间的行为不同。
【解决方案3】:
In [200]: zzz = np.array([[0.1, 0.2, 0.3, 0.4, 0.5], 
     ...:                 [0.6, 0.7, 0.8, 0.9, 1. ]]) 
     ...:                                                                                      
In [201]: zl = zzz.tolist()                                                                    
In [202]: zl                                                                                   
Out[202]: [[0.1, 0.2, 0.3, 0.4, 0.5], [0.6, 0.7, 0.8, 0.9, 1.0]]

random.random 可能正在使用就地分配,例如:

In [203]: zzz[0],zzz[1]=zzz[1],zzz[0]                                                          
In [204]: zzz                                                                                  
Out[204]: 
array([[0.6, 0.7, 0.8, 0.9, 1. ],
       [0.6, 0.7, 0.8, 0.9, 1. ]])

注意复制。

但应用于列表列表:

In [205]: zl[0],zl[1]=zl[1],zl[0]                                                              
In [206]: zl                                                                                   
Out[206]: [[0.6, 0.7, 0.8, 0.9, 1.0], [0.1, 0.2, 0.3, 0.4, 0.5]]
In [207]: zl[0],zl[1]=zl[1],zl[0]                                                              
In [208]: zl                                                                                   
Out[208]: [[0.1, 0.2, 0.3, 0.4, 0.5], [0.6, 0.7, 0.8, 0.9, 1.0]]

我测试了zl = list(zzz),仍然得到了数组行为。这个zl 是一个包含zzz 视图的列表。 tolist 列出了s totally independent ofzzz` 的列表。

简而言之,random.random 无法正确处理 ndarray 的就地修改。 np.random.shuffle 设计用于处理数组的第一个暗角,因此它是正确的。

ndarray 的正确分配是:

In [211]: zzz = np.array([[0.1, 0.2, 0.3, 0.4, 0.5], 
     ...:                 [0.6, 0.7, 0.8, 0.9, 1. ]]) 
     ...:                                                                                      
In [212]: zzz[[0,1]] = zzz[[1,0]]                                                              
In [213]: zzz                                                                                  
Out[213]: 
array([[0.6, 0.7, 0.8, 0.9, 1. ],
       [0.1, 0.2, 0.3, 0.4, 0.5]])
In [214]: zzz[[0,1]] = zzz[[1,0]]                                                              
In [215]: zzz                                                                                  
Out[215]: 
array([[0.1, 0.2, 0.3, 0.4, 0.5],
       [0.6, 0.7, 0.8, 0.9, 1. ]])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-16
    • 2021-07-02
    • 1970-01-01
    相关资源
    最近更新 更多