问题描述
假设我有以下 numpy 数组:
Space = np.arange(7)
问题:如何从 Space 生成一组 N 个样本,以便:
- 每个样本仅由递增或递减的连续数字组成
- 采样是通过替换完成的,因此样本不需要单调递增或递减。
- 每个样本都以 6 或 0 结尾,并且
- 样本长度没有限制(但是,一旦选择了 6 或 0,每个样本就会终止)。
本质上,我正在通过 numpy 采样创建一个马尔可夫奖励过程(可能有一个更有效的数据包,但我不确定它会是什么。)例如,如果 N = 3,一个可能的采样集看起来像这样。
Sample = [[1,0],[4,3,4,5,6],2,1,0]]
我可以用这样不太优雅的东西来完成这个:
N = len(Space)
Set = []
for i in range(3):
X = np.random.randint(N)
if (X == 0) | (X==6):
Set.append(X)
else:
Sample = []
while (X !=0) & (X != 6):
Next = np.array([X-1,X+1])
X = np.random.choice(Next)
Sample.append(X)
Set.append(Sample)
return(Set)
但我想知道有什么更有效/pythonic 的方式来进行这种类型的采样,也许没有这么多循环?或者,是否有更好的 Python 库用于此类事情?谢谢。
解决方法
Numpy 在这里似乎没有多大帮助,我只是使用标准的 random
模块。主要原因是 random
在处理单个值时更快,除非需要,否则似乎不需要引入额外的依赖项。
from random import randint,choice
def bounded_path(lo,hi):
# r covers the interior space
r = range(lo+1,hi)
n = randint(lo,hi)
result = [n]
while n in r:
n += choice((-1,1))
result.append(n)
return result
似乎对我来说是正确的,例如评估上述 10 次,我得到:
[0]
[4,3,4,2,1,0]
[5,6]
[2,5,0]
[1,0]
[4,0]
[3,0]
[6]
[4,0]
刚刚做了随机数生成比较的快速基准:
def rng_np(X):
for _ in range(10):
X = np.random.choice(np.array([X-1,X+1]))
return X
def rng_py(X):
for _ in range(10):
X += choice((-1,+1))
return X
Numpy 版本慢了大约 30 倍。 Numpy 必须做很多额外的工作,每次迭代构建一个 Python 数组,转换为 Numpy 数组,切换 choice
以允许花哨的矢量化。 Python 知道原版中的 (-1,+1)
是常量,因此它只构建一次(例如,dis
可用于查看内部发生的情况)。
您可能可以通过处理更大的数字块来到达某个地方,但我怀疑它会快得多。保持起点的统一看起来很尴尬,但如果你真的小心,你可能会有所作为!当每个调用向量化大约 10 个值时,Numpy 开始收支平衡,当你有超过 100 个值时,它真的很出色。