我觉得这个应该很容易,但经过无数次搜索和尝试后,我无法找到答案。基本上我有很多项目,我想以随机顺序采样而无需替换。在这种情况下,它们是2D阵列中的单元格。我将用于较小数组的解决方案不会转换,因为它需要改组内存数组。如果我必须采样的数量很小,我也可以随机抽样物品并保留我尝试过的值列表。不幸的是,我经常需要对所有细胞中的很大一部分进行采样,尽可能多。
我想创建的是迭代器,它使用itertools,numpy和/或random的某种组合产生下一个随机单元格(x和y索引)。另一种可能的解决方案是创建一个迭代器,它将产生0和(x_count * y_count)之间的下一个随机数(无替换),我可以将其映射回单元格位置。这些都不容易实现。
感谢任何消息!
这是我目前的解决方案。
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,并为困难的情况抛出错误。
答案 0 :(得分:3)
我认为,对于接近R * C
的样本大小而言,如果没有使用大量辅助存储空间,就无法对没有替换的序列进行采样。虽然有一些聪明的方法可以减少小样本量的存储量,但如果您希望采样的数据量超过数据集的三分之一,那么最好只创建一个单独的列表。 random.sample
是此目的的自然选择;坦率地说,我只是将你的2-d numpy阵列的扁平版本直接传递给它。 (除非你也需要索引,在这种情况下,随机抽样整数并将它们转换成坐标,la hexparrot的解决方案,是一种合理的方法。)
>>> a = numpy.arange(25).reshape((5, 5))
>>> random.sample(a.ravel(), 5)
[0, 13, 8, 18, 4]
如果你看一下random.sample
的实现,你会发现对于较小的样本大小,它已经大致与上面的perl代码相同 - 跟踪集合中先前选择的项目并丢弃选择已经在集合中。对于较大的样本大小,它会创建输入的副本 - 这比较大的值的集合更有效,因为集合比每个存储项目的列表占用更多空间 - 并且稍微修改了Fisher-Yates shuffle,当它有sample_size
项时停止(即它不会对整个列表进行洗牌,因此它比自己改变整个事物更有效。)
基本上,我的赌注是你不会比random.sample
更好,滚动你自己,除非你在c中编写代码。
然而 - 我确实找到了这个,你可能会觉得有趣:numpy.random.choice
。这似乎是在c速度下进行或不进行替换的随机采样。抓到了吗?它是Numpy 1.7的新功能!
答案 1 :(得分:2)
这种方法怎么样?我首先创建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坐标,只需通过数组[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()表示它是用于随机抽样而无需替换的。这种方法坚持建议&#39;对于从大量人群中抽样,这种方法特别快速且节省空间:样本(xrange(10000000),60)。&#39;在python random页面上找到。
我注意到虽然我使用get_random_item()作为生成器,但底层sample()仍然生成一个完整列表,所以内存使用仍然是y * x + sample_size,但它运行得非常快。< / p>
答案 2 :(得分:1)
您已经在使用O(N = R * C)内存,因此您也可以使用O(N)内存作为迭代器。复制所有元素并随意对它们进行排序,就像对单维案例一样。如果你要访问每个元素,这是一个合理的事情,你说这是你的情况。
(对于记录:否则,内存不是一个很糟糕的问题,因为你只需要“记住”你以前的位置。因此你可以保留一个已经访问过的索引列表。如果您计划访问每个元素,那就太糟糕了,因为如果实施不断增长的黑名单,拒绝抽样可能需要很长时间。(您也可以使用递减的白名单来实现它,这相当于第一个解决方案。)
答案 3 :(得分:1)
您已经说过,对于网格中的大多数单元格,您的测试可能会失败。如果是这种情况,随机抽样细胞可能不是一个好主意,因为你很难跟踪你已经检查过的细胞,而不需要使用大量的内存。
相反,您可能最好将测试应用于整个网格,并从通过它的那些中选择一个随机元素。
此函数返回一个传递测试的随机元素(如果全部失败则返回None)。它使用的内存非常少。
def findRandomElementThatPasses(iterable, testFunc):
value = None
passed = 0
for element in iterable:
if testFunc(element):
passed += 1
if random.random() > 1.0/passed:
value = element
return value
你可以用以下内容来称呼它:
e = findRandomElementThatPasses((x,y) for x in xrange(X_SIZE)
for y in xrange(Y_SIZE),
someFunctionTakingAnXYTuple)
如果您使用的是Python 3,请使用range而不是xrange。
答案 4 :(得分:0)
在perl中你可以这样做:
# how many you got and need
$xmax = 10000000;
$ymax = 10000000;
$sampleSize = 10000;
# build hash, dictionary in python
$cells = {};
$count = 0;
while($count < $sampleSize) {
$x = rand($xmax);
$y = rand($ymax);
if(! $cells->{$x}->{$y}) {
$cells->{$x}->{$y}++;
$count++;
}
}
# now grab the stuff
foreach ($x keys %{$cells}) {
foreach ($y keys %{$cells->{$x}}) {
getSample($x, $y);
}
}
没有重复,相当随意,内存不太难。