Python 指定大小的随机子集的Numpy列表
我想计算一个数组的随机子集列表,其中子集中的顺序是随机的,它们各自子集中的每个元素都是唯一的,我想高效地这样做(在时间和空间方面) 例如,带有一些参数Python 指定大小的随机子集的Numpy列表,python,arrays,numpy,random,Python,Arrays,Numpy,Random,我想计算一个数组的随机子集列表,其中子集中的顺序是随机的,它们各自子集中的每个元素都是唯一的,我想高效地这样做(在时间和空间方面) 例如,带有一些参数k=3和n=5的数组[1,2,3,4,5]应该生成一个矩阵 [[4,3,1], [1,2,5], [2,4,5], [3,2,5], [2,3,1]] i、 例如,我们从数组中获得包含k=3randomunique元素的n=5列表 我希望这是尽可能快,而不创建一个庞大的查找表的可能组合,因为时间和空间都是至关重要的 我试着用numpy.r
k=3
和n=5
的数组[1,2,3,4,5]
应该生成一个矩阵
[[4,3,1],
[1,2,5],
[2,4,5],
[3,2,5],
[2,3,1]]
i、 例如,我们从数组中获得包含k=3
randomunique元素的n=5
列表
我希望这是尽可能快,而不创建一个庞大的查找表的可能组合,因为时间和空间都是至关重要的
我试着用numpy.random.choice(array,n*k).重塑((n,k))
来实现这一点,除了唯一性部分之外,这几乎是我想要的。我决定了以下几点
subsets = numpy.zeros(n).reshape((n,1))
subsets = numpy.apply_along_axis(lambda x: numpy.random.choice(array, k, replace=False),1, subsets)
然而,由于这不是纯粹的numpy,这是缓慢的。对我的申请来说太慢了。是否有一些方法可以改进运行时,或者使用纯numpy命令来实现这一点
任何帮助都将不胜感激 假设
k
将比n
小得多,您可以自己实施采样而无需更换:
N = [1, 2, 3, 4, 5]
k = 3
n = 5
arr = np.array([N] * n)
arr
array([[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5]])
[np.random.shuffle(i) for i in arr]
arr
array([[3, 5, 1, 2, 4],
[5, 1, 3, 2, 4],
[3, 5, 2, 4, 1],
[4, 5, 1, 2, 3],
[1, 5, 3, 2, 4]])
arr[:, :3]
array([[3, 5, 1],
[5, 1, 3],
[3, 5, 2],
[4, 5, 1],
[1, 5, 3]])
idx = np.random.randint([5,4,3], size=[5,3])
idx[:,2:] += idx[:,2:] >= idx[:,1,None]
idx[:,1:] += idx[:,1:] >= idx[:,0,None]
np.array([1,2,3,4,5])[arr]
将其放入函数中,可以是:
def sample_noreplace(arr, n, k):
assert k <= len(arr)
idx = np.random.randint(len(arr) - np.arange(k), size=[n, k])
for i in range(k-1, 0, -1):
idx[:,i:] += idx[:,i:] >= idx[:,i-1,None]
return np.array(arr)[idx]
这给了我:{0:600288,1:599656,2:600494,3:599233,4:600329}
,看起来很统一。接下来,我们可以在每个位置内检查:
out = np.zeros([3, 5])
for i in range(3):
n, c = np.unique(arr[:,i], return_counts=True)
out[i, n] = c
这给了我一张桌子,看起来像:
array([[199936., 199701., 200106., 199843., 200414.],
[200227., 200044., 200345., 199897., 199487.],
[200125., 199911., 200043., 199493., 200428.]])
这看起来也很一致。考虑到你的数字,一个好的方法是简单地用替换画出,然后用重复画出 以下是从不同大小的阵列中提取5000个10%或10%(以较小者为准)样本的时间安排。pp_overdraw提取过多样本并丢弃,pp_fillin提取到准确的数量,重新绘制坏样本,直到没有剩余样本。pythran是一个编译解决方案。因为OP要求的是纯numpy,所以这里仅供参考 代码: 文件
输出矩阵中的第二个数组具有非唯一成员
1
在[1,5,1]
中,如果N较大,k较小,则此新版本在空间方面可能非常昂贵。这不是一个很好的解决方案,但使用map更快<代码>np.数组(列表(映射(lambda x:np.random.choice(数组,3,replace=False),范围(5))。您期望的大小是多少?n
和k
以及输入数组?@PaulPanzer目前,len(数组)大约为500,n在1000到5000之间,k在2到5000之间10@PaulPanzer说得好,我也应该问这个问题!请注意,如果k
远低于sqrt(len(array))
,则使用不同的算法会很有用,因为拾取重复的几率相对较低。如需了解sqrt的来源,请参阅from@SamMason如果您感兴趣,我已经为一个解决方案添加了代码,该解决方案对任何参数都很有效(O(len(array)+nk)
),但依赖Pyran(numba也可以)提高速度,因为它是循环的。--顺便说一句,你认为你的算法适用于哪种制度?它在k中是二次的,所以它也必须在kvery干净溶液中的某个地方,谢谢。我也很欣赏概率分析!
array([[199936., 199701., 200106., 199843., 200414.],
[200227., 200044., 200345., 199897., 199487.],
[200125., 199911., 200043., 199493., 200428.]])
import pyflip
import numpy as np
from simple_benchmark import BenchmarkBuilder, MultiArgument
B = BenchmarkBuilder()
@B.add_function()
def pythran(A,k,n):
assert len(A) >= k
if A.dtype not in (float,int) or A.ndim > 1:
return A[pyflip.without_repl(np.arange(len(A)),k,n)]
else:
return pyflip.without_repl(A,k,n)
from scipy import stats
@B.add_function()
def pp_overdraw(A,k,n):
N = len(A)
p = np.linspace(1-(k-1)/N,1-1/N,k-1).prod()
nn = int(n/p + 4*np.sqrt(n-n*p)) + 1
out = np.random.randint(0,N,(nn,k))
os = np.sort(out,1)
valid = (os[:,:-1] != os[:,1:]).all(1)
validx, = valid.nonzero()
while len(validx)<n: # very unlikely
replace = np.random.randint(0,N,(nn-len(validx),k))
rs = np.sort(replace,1)
rvalid = (rs[:,:-1] != rs[:,1:]).all(1)
out[~valid,:] = replace
valid[~valid] = rvalid
validx, = valid.nonzero()
return A[out[validx[:n]]]
@B.add_function()
def pp_fillin(A,k,n):
N = len(A)
out = np.random.randint(0,N,(n,k))
os = np.sort(out,1)
valid = (os[:,:-1] != os[:,1:]).all(1)
validx, = valid.nonzero()
while len(validx)<n:
replace = np.random.randint(0,N,(n-len(validx),k))
rs = np.sort(replace,1)
rvalid = (rs[:,:-1] != rs[:,1:]).all(1)
out[~valid,:] = replace
valid[~valid] = rvalid
validx, = valid.nonzero()
return A[out]
@B.add_function()
def OP(A,k,n):
subsets = np.zeros(n).reshape((n,1))
subsets = np.apply_along_axis(lambda x: np.random.choice(A, k, replace=False),1, subsets)
@B.add_function()
def Sam_Mason(A,k,n):
assert k <= len(A)
idx = np.random.randint(len(A) - np.arange(k), size=[n, k])
for i in range(k-1, 0, -1):
idx[:,i:] += idx[:,i:] >= idx[:,i-1,None]
return np.array(A)[idx]
@B.add_arguments('array size')
def argument_provider():
for exp in range(4, 15):
sz = 2**exp
A = np.arange(sz)
yield sz, MultiArgument([A,min(10,sz//10+1),5000])
r = B.run()
r.plot()
import pylab
pylab.savefig('norepl.png')
pythran -O3 pyflip.py
import numpy as np
#pythran export without_repl(int[:],int,int)
#pythran export without_repl(float[:],int,int)
def without_repl(A,k,n):
N = A.size
out = np.empty((n,k),A.dtype)
A = A.copy()
for i in range(n):
for j in range(k):
l = np.random.randint(j,N)
out[i,j] = A[l]
A[l] = A[j]
A[j] = out[i,j]
return out