python – 无需替换的内存高效随机数迭代器

我觉得这个应该很容易,但经过无数次搜索和尝试后,我无法找到答案.基本上我有很多项目,我想以随机顺序采样而无需替换.在这种情况下,它们是2D阵列中的单元格.我将用于较小数组的解决方案不会转换,因为它需要改组内存数组.如果我必须采样的数量很小,我也可以随机抽样物品并保留我尝试过的值列表.不幸的是,我经常需要对所有细胞中的很大一部分进行采样,尽可能多.

我想创建的是迭代器,它使用itertools,numpy和/或random的一些组合产生下一个随机单元格(x和y索引).另一种可能的解决方案是创建一个迭代器,它将产生0和(x_count * y_count)之间的下一个随机数(无替换),我可以将其映射回单元格位置.这两者似乎都不容易实现.

感谢任何sugestions!

这是我目前的解决方案.

import numpy as np
import itertools as itr
import random as rdm

#works great
x_count = 10
y_count = 5

#good luck!
#x_count = 10000
#y_count = 20000

x_indices = np.arange(x_count)
y_indices = np.arange(y_count)

cell_indices = itr.product(x_indices, y_indices)
list_cell_indices = list(cell_indices)
rdm.shuffle(list_cell_indices)

for i in range(25):
    print list_cell_indices[i]

所以根据当前的反应和我翻译perl的尝试,我一无所知,我理解我能做的最好的事情如下:

import numpy as np
import itertools as itr
import random as rdm

x_count = 10000
y_count = 5000

sample_count = 10000
keep_probability = 0.01


tried_cells = set()
kept_cells = set()

while len(kept_cells) < sample_count:
    x = rdm.randint(0, x_count)
    y = rdm.randint(0, y_count)

    if (x, y) in tried_cells:
        pass
    else:
        tried_cells.add((x, y))
        keep = rdm.random() < keep_probability
        if keep:
            kept_cells.add((x,y))


print "worked"

在大多数情况下,使用的处理时间和内存并没有那么糟糕.也许我可以检查平均单元格keep_probability和sample_count并为困难案例抛出错误.

解决方法:

这种方法怎么样?我首先创建x * y数组并将其重塑为2-D.然后,知道每个单元格可以由单个整数唯一标识,从0到(x * y)获取样本.

import numpy

x_count = 10000
y_count = 20000

x_indices = numpy.arange(x_count)
y_indices = numpy.arange(y_count)

large_table = numpy.arange(y_count * x_count).reshape(y_count, x_count)
print large_table

def get_random_item(sample_size):
    from random import sample
    for i in sample(xrange(y_count * x_count), sample_size):
        y,x = divmod(i, y_count)
        yield (x,y)

for x,y in get_random_item(10):
    print '%12i   x: %5i y: %5i' % (large_table[x][y],  x,y)

哪个回报:

(首先模拟您通过产品创建的现有二维阵列)

[[        0         1         2 ...,      9997      9998      9999]
 [    10000     10001     10002 ...,     19997     19998     19999]
 [    20000     20001     20002 ...,     29997     29998     29999]
 ..., 
 [199970000 199970001 199970002 ..., 199979997 199979998 199979999]
 [199980000 199980001 199980002 ..., 199989997 199989998 199989999]
 [199990000 199990001 199990002 ..., 199999997 199999998 199999999]]

然后,它返回2-dim坐标,只需通过array [x] [y]即可将其转换为单元格内容

   154080675   x: 15408 y:   675
   186978188   x: 18697 y:  8188
   157506087   x: 15750 y:  6087
   168859259   x: 16885 y:  9259
    29775768   x:  2977 y:  5768
    94167866   x:  9416 y:  7866
    15978144   x:  1597 y:  8144
    91964007   x:  9196 y:  4007
   163462830   x: 16346 y:  2830
    62613129   x:  6261 y:  3129

sample()声明它”用于无需替换的随机抽样’,这种方法遵循建议’这对于从大群体中抽样特别快且节省空间:样本(xrange(10000000),60).在python random页面上找到.

我注意到虽然我使用get_random_item()作为生成器,但底层sample()仍然生成一个完整列表,因此内存使用仍然是y * x sample_size,但它运行得相当迅速.

上一篇:使用groupby迭代从长到宽的python单行(或两行)


下一篇:python – 不将可迭代(itertools.combinations)转换为列表的混洗组合