Get random sample from list while maintaining ordering of items?
我有一个排序列表,比如:(它不仅仅是数字,它是一个用复杂耗时算法排序的对象列表)
1 | mylist = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ,9 , 10 ] |
是否有一些python函数可以给我n个条目,但会保持顺序?
例子:
1 2 3 4 | randomList = getRandom(mylist,4) # randomList = [ 3 , 6 ,7 , 9 ] randomList = getRandom(mylist,4) # randomList = [ 1 , 2 , 4 , 8 ] |
等。。。
以下代码将生成大小为4的随机样本:
1 2 3 4 5 6 | import random sample_size = 4 sorted_sample = [ mylist[i] for i in sorted(random.sample(range(len(mylist)), sample_size)) ] |
(注意:对于python 2,最好使用
解释
1 | random.sample(range(len(mylist)), sample_size) |
生成原始列表索引的随机样本。
然后对这些索引进行排序,以保留原始列表中元素的顺序。
最后,在给定抽样索引的情况下,列表理解从原始列表中提取实际元素。
简单到编码O(n+k*log(k))方式
随机抽取一个不替换索引的样本,对索引进行排序,并从原始索引中提取它们。
1 2 | indices = random.sample(range(len(myList)), K) [myList[i] for i in sorted(indices)] |
或者更简明扼要地说:
1 | [x[1] for x in sorted(random.sample(enumerate(myList),K))] |
优化的O(N)-时间,O(1)-辅助空间方式
您也可以使用数学技巧,从左到右迭代遍历
1 2 3 4 5 6 7 8 9 10 11 12 | from __future__ import division def orderedSampleWithoutReplacement(seq, k): if not 0<=k<=len(seq): raise ValueError('Required that 0 <= sample_size <= population_size') numbersPicked = 0 for i,number in enumerate(seq): prob = (k-numbersPicked)/(len(seq)-i) if random.random() < prob: yield number numbersPicked += 1 |
概念证明和概率正确性测试:
在5小时内用1万亿个伪随机样本进行模拟:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> Counter( tuple(orderedSampleWithoutReplacement([0,1,2,3], 2)) for _ in range(10**9) ) Counter({ (0, 3): 166680161, (1, 2): 166672608, (0, 2): 166669915, (2, 3): 166667390, (1, 3): 166660630, (0, 1): 166649296 }) |
概率与真实概率之差小于1.0001。再次运行此测试会导致不同的顺序,这意味着它不会偏向于一个顺序。用更少的样本对
编辑:不知道为什么人们投票错误的评论或害怕投票…不,这个方法没有问题。=)
(用户Tegan在评论中也提供了一个有用的提示:如果这是python2,那么如果您真的关心额外的空间,您将像往常一样使用xrange。)
编辑:证据:考虑到从
编辑:Timothy Shields提到了储层采样,这是当
编辑:下面是另一种以更明显的语义方式对其进行编码的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | from __future__ import division import random def orderedSampleWithoutReplacement(seq, sampleSize): totalElems = len(seq) if not 0<=sampleSize<=totalElems: raise ValueError('Required that 0 <= sample_size <= population_size') picksRemaining = sampleSize for elemsSeen,element in enumerate(seq): elemsRemaining = totalElems - elemsSeen prob = picksRemaining/elemsRemaining if random.random() < prob: yield element picksRemaining -= 1 from collections import Counter Counter( tuple(orderedSampleWithoutReplacement([0,1,2,3], 2)) for _ in range(10**5) |
)
也许您可以生成索引的样本,然后从列表中收集项目。
1 2 3 | randIndex = random.sample(range(len(mylist)), sample_size) randIndex.sort() rand = [mylist[i] for i in randIndex] |
显然,在python 2.3中引入了
所以对于下面的版本,我们可以使用shuffle(例如4个项目):
1 2 3 | myRange = range(0,len(mylist)) shuffle(myRange) coupons = [ bestCoupons[i] for i in sorted(myRange[:4]) ] |
随机抽样实施。
1 2 | >>> random.sample([1, 2, 3, 4, 5], 3) # Three samples without replacement [4, 1, 5] |