所以我想知道使用np.random.choice
生成二维数组是否有更有效的解决方案,其中每一行都有唯一的值。
例如,对于形状为(3,4)
的数组,我们期望输出为:
# Expected output given a shape (3,4)
array([[0, 1, 3, 2],
[2, 3, 1, 0],
[1, 3, 2, 0]])
这意味着每行的值必须相对于列数是唯一的。因此,对于out
中的每一行,整数应该只在0到3之间。
我知道我可以通过将False
传递给replace
参数来实现它。但我只能为每一行而不是整个矩阵。例如,我可以这样做:
>>> np.random.choice(4, size=(1,4), replace=False)
array([[0,2,3,1]])
但是当我尝试这样做时:
>>> np.random.choice(4, size=(3,4), replace=False)
我收到这样的错误:
File "<stdin>", line 1, in <module>
File "mtrand.pyx", line 1150, in mtrand.RandomState.choice
(numpy\random\mtrand\mtrand.c:18113)
ValueError: Cannot take a larger sample than population when
'replace=False'
我认为这是因为它试图绘制3 x 4 = 12
样本,因为矩阵的大小没有替换,但我只给它4
的限制。
我知道我可以使用for-loop
:
>>> a = (np.random.choice(4,size=4,replace=False) for _ in range(3))
>>> np.vstack(a)
array([[3, 1, 2, 0],
[1, 2, 0, 3],
[2, 0, 3, 1]])
但我想知道是否有一个没有使用任何for循环的解决方法? (我有点假设如果我的行数大于1000,添加for循环可能会使它变慢。但是你可以看到我实际上在a
中创建了一个生成器,所以我也不确定如果它毕竟有效。)
答案 0 :(得分:14)
我经常使用的一个技巧是生成随机数组并使用argsort
将唯一索引作为必需的唯一数字。因此,我们可以做 -
def random_choice_noreplace(m,n, axis=-1):
# m, n are the number of rows, cols of output
return np.random.rand(m,n).argsort(axis=axis)
样品运行 -
In [98]: random_choice_noreplace(3,7)
Out[98]:
array([[0, 4, 3, 2, 6, 5, 1],
[5, 1, 4, 6, 0, 2, 3],
[6, 1, 0, 4, 5, 3, 2]])
In [99]: random_choice_noreplace(5,7, axis=0) # unique nums along cols
Out[99]:
array([[0, 2, 4, 4, 1, 0, 2],
[1, 4, 3, 2, 4, 1, 3],
[3, 1, 1, 3, 2, 3, 0],
[2, 3, 0, 0, 0, 2, 4],
[4, 0, 2, 1, 3, 4, 1]])
运行时测试 -
# Original approach
def loopy_app(m,n):
a = (np.random.choice(n,size=n,replace=False) for _ in range(m))
return np.vstack(a)
计时 -
In [108]: %timeit loopy_app(1000,100)
10 loops, best of 3: 20.6 ms per loop
In [109]: %timeit random_choice_noreplace(1000,100)
100 loops, best of 3: 3.66 ms per loop
答案 1 :(得分:0)
除了Divakar的好答案外,这是另一种替代方法,它在我的计算机上的速度甚至快了大约20%:
ng generate component Andy
时间:
def random_choice_noreplace_2(m, n):
data = np.arange(m * n).reshape(n, m) % m
for row in data: np.random.shuffle(row)
return data
答案 2 :(得分:0)
这是我的解决方案,无需更换即可重复采样,并根据Divakar的答案进行了修改。他在评论部分建议对结果进行切片。样本数<数组长度。但是,如果数组的长度很大但不是,这可能不是最有效的方法。的样本数量很小,因为argsort
可能需要很长时间。我建议改用argpartition
。
def random_choice_noreplace2(l, n_sample, num_draw):
'''
l: 1-D array or list
n_sample: sample size for each draw
num_draw: number of draws
Intuition: Randomly generate numbers, get the index of the smallest n_sample number for each row.
'''
l = np.array(l)
return l[np.argpartition(np.random.rand(num_draw,len(l)), n_sample-1,axis=-1)[:,:n_sample]]
时间-
def loopy_app(l, n_sample, num_draw):
l = np.array(l)
a = [np.random.choice(l,size=n_sample,replace=False) for _ in range(num_draw)]
return np.vstack(a)
def random_choice_noreplace(l, n_sample, num_draw):
# m, n are the number of rows, cols of output
l = np.array(l)
return l[np.random.rand(num_draw,len(l)).argsort(axis=-1)[:,:n_sample]]
In [2]: %timeit loopy_app(range(100),2,1000)
48.5 ms ± 2.91 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [3]: %timeit random_choice_noreplace(range(100),2,1000)
7.8 ms ± 210 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [4]: %timeit random_choice_noreplace2(range(100),2,1000)
2.71 ms ± 57.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
正确性-
In [5]: np.random.seed(42)
...: random_choice_noreplace(range(100),2,10)
Out[5]:
array([[72, 10],
[28, 71],
[ 8, 5],
[32, 71],
[ 7, 56],
[63, 15],
[40, 28],
[94, 64],
[21, 98],
[45, 36]])
In [6]: np.random.seed(42)
...: random_choice_noreplace2(range(100),2,10)
Out[6]:
array([[72, 10],
[28, 71],
[ 8, 5],
[32, 71],
[ 7, 56],
[63, 15],
[40, 28],
[94, 64],
[21, 98],
[45, 36]])