从 numpy 数组中有效地采样以相同数字结尾的连续整数序列?

问题描述

假设我有以下 numpy 数组:

Space = np.arange(7) 

问题:如何从 Space 生成一组 N 个样本,以便:

  1. 每个样本仅由递增或递减的连续数字组成
  2. 采样是通过替换完成的,因此样本不需要单调递增或递减。
  3. 每个样本都以 6 或 0 结尾,并且
  4. 样本长度没有限制(但是,一旦选择了 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 个值时,它真的很出色。