使用numpy创建大型随机布尔矩阵

zim*_*rol 18 python random numpy

我正在尝试创建一个boolean随机填充TrueFalse具有给定概率的巨大矩阵p.起初我使用了这段代码:

N = 30000
p = 0.1
np.random.choice(a=[False, True], size=(N, N), p=[p, 1-p])  
Run Code Online (Sandbox Code Playgroud)

但遗憾的是,它似乎并没有因此而终止N.所以我尝试通过这样做将其拆分为单行的生成:

N = 30000
p = 0.1
mask = np.empty((N, N))
for i in range (N):
     mask[i] = np.random.choice(a=[False, True], size=N, p=[p, 1-p])            
     if (i % 100 == 0):
          print(i)
Run Code Online (Sandbox Code Playgroud)

现在发生了一些奇怪的事情(至少在我的设备上):第一个~1100行非常快速地生成 - 但在它之后,代码变得非常慢.为什么会这样?我在这里想念什么?有没有更好的方法来创建它有一个很大的矩阵True的概率条目pFalse条目的概率1-p

编辑:你们许多人都认为RAM会出现问题:由于运行代码的设备有近500GB的RAM,这不会有问题.

Ben*_*Ben 12

真的很惊讶还没有人提到这个解决方案..

这条线

np.random.choice(a=[False, True], size=(N, N), p=[p, 1-p])
Run Code Online (Sandbox Code Playgroud)

运行 NXN伯努利试验。(在你的例子中,有 900M!)伯努利试验只是一个随机实验,有两种可能的结果,概率为 p 和 1-p。

N 个伯努利试验的总和(每个试验的概率为 p)可以通过二项式分布建模。

我们可以利用这个事实来随机模拟 True 元素的总数。使用 NumPy,

import numpy as np

N = 30000
p = 0.1

# Build a random number generator
rng = np.random.default_rng(123)

# Randomly determine the total number of True values
Ntrue = rng.binomial(n=N*N, p=p, size=1)[0]  # 90016776
Run Code Online (Sandbox Code Playgroud)

现在我们可以通过随机选择 row 和 col 索引而不需要放回来随机确定每个 True 元素的位置

# Randomly determine true position
position_ids = rng.choice(a=N*N, size=Ntrue, replace=False)
positions = np.unravel_index(position_ids, shape=(N,N))
Run Code Online (Sandbox Code Playgroud)

现在我们可以填充压缩稀疏行(CSR)矩阵。

from scipy import sparse

# Build a compressed sparse row matrix with the constructor:
# csr_matrix((data, (row_ind, col_ind)), [shape=(M, N)])
result = sparse.csr_matrix((np.ones(shape=Ntrue), positions), shape=(N,N))
Run Code Online (Sandbox Code Playgroud)

请注意,此解决方案避免存储和计算900M 布尔值。

有趣的是,在偶然发现这个问题之前,我写了一个几乎相同的问题。


aws*_*ice 10

问题是你的RAM,这些值在创建时存储在内存中.我刚用这个命令创建了这个矩阵:

np.random.choice(a=[False, True], size=(N, N), p=[p, 1-p])

我使用了AWS i364GB RAM和8个内核的实例.要创建此矩阵,htop表明它占用了大约20GB的RAM.以下是您关心的基准:

time np.random.choice(a=[False, True], size=(N, N), p=[p, 1-p])

CPU times: user 18.3 s, sys: 3.4 s, total: 21.7 s
Wall time: 21.7 s


 def mask_method(N, p):
    for i in range(N):
        mask[i] = np.random.choice(a=[False, True], size=N, p=[p, 1-p])
        if (i % 100 == 0):
            print(i)

time mask_method(N,p)

CPU times: user 20.9 s, sys: 1.55 s, total: 22.5 s
Wall time: 22.5 s
Run Code Online (Sandbox Code Playgroud)

请注意,掩码方法在其峰值时仅占用约9GB的RAM.

编辑:第一个方法在完成该过程后刷新RAM,其中函数方法保留所有内容.


Eri*_*ric 6

因此,我尝试通过执行以下操作将其拆分为单行的生成:

有效的方法np.random.choice是,首先为数据的每个单元生成一个float64in [0, 1),然后使用将其转换为数组中的索引np.search_sorted。这个中间表示比布尔数组大8倍!

由于您的数据是布尔值,因此您可以将速度提高两倍

np.random.rand(N, N) > p
Run Code Online (Sandbox Code Playgroud)

自然地,您可以在循环解决方案中使用

似乎np.random.choice可以在此处进行一些缓冲-您可能要针对numpy提出问题。

另一种选择是尝试生成float32s而不是float64s。我不确定numpy是否可以立即执行此操作,但是您可以请求该功能。