无需替换的选择 - 通过改变列表

Selection without replacement - by mutating the list

我正在寻找 Python 中的有效函数,它无需替换即可进行样本选择,但实际上会改变原始列表。也就是说,替代这个:

random.sample(population, k)

在选择样本时从原始列表中删除元素。 列表可能有数百万个项目,并且可能有数十次对示例函数的后续调用。

理想情况下,我想做这样的事情:

sample_size_1 = 5   
sample_size_2 = 200   
sample_size_3 = 100   
population = range(10000000)  

sample_1 = select_sample(population, sample_size_1)  #population is shrunk  
sample_2 = select_sample(population, sample_size_2)  #population is shrunk again     
sample_3 = select_sample(population, sample_size_3)  #and population is shrunk again

其中 population 在每次调用 select_sample 之间有效收缩。

我有一些代码可以在这里展示,但我正在寻找一些已经可用的代码,或者比我的 while 循环更多 "pythonic"。

一个简单的方法是 shuffle your population,这样初始排序是随机的(如果它还不是随机的)。然后从末尾取出元素并删除它们。

您可以通过切片 population[-sample_size:] 获取元素并使用 population[-sample_size:] = [] 删除它们。

import random

population = list(range(100))

# Shuffle population so the ordering is random.
random.shuffle(population)

for sample_size in [1, 5, 10]:
    sample = population[-sample_size:]
    population[-sample_size:] = []
    print(sample)
    # [79]
    # [66, 89, 81, 0, 38]
    # [18, 39, 90, 36, 11, 32, 63, 65, 72, 67]

如果您只想一次删除一个元素(即如果 sample_size 为 1),您也可以使用 population.pop()

执行此操作的函数将是(假设您的人口已经洗牌):

def select_sample(pop, size):
    x = pop[-size:]
    pop[-size:] = []
    return x

问题是弹出列表中间真的很慢;最后删除是 and certainly fast. As another option, you could use the heap datastructure from heapq.

中的一种选择

一开始,您会将数据组织到 heapq 中作为元组 (random, value);然后使用 heappop 首先弹出随机数最低的值:

import heapq
import random

heap = [ (random.random(), v) for v in samples ]
heapq.heapify(heap)

def select_sample(size):
    return [ heapq.heappop(heap)[1] for _ in range(size) ]

在这种情况下,您应该选择弹出列表的末尾,因为这样可以保证更快;但是 heapq 擅长的是在未知大小的总体中选择固定大小的样本。