我需要获得一个 k大小的样本而无需从群体中替换,群体中的每个成员都有相关的权重(W)。
Numpy的 random.choices 不会在没有替换的情况下执行此任务,并且 random.sample 不会接受加权输入。
目前,这就是我正在使用的:
P = np.zeros((1,Parent_number))
n=0
while n < Parent_number:
draw = random.choices(population,weights=W,k=1)
if draw not in P:
P[0,n] = draw[0]
n=n+1
P=np.asarray(sorted(P[0]))
虽然这有效,但它需要在数组,列表和数组之间来回切换,因此不太理想。
我正在寻找最简单易懂的解决方案,因为此代码将与其他人共享。
答案 0 :(得分:8)
您可以np.random.choice
使用replace=False
,如下所示:
np.random.choice(vec,size,replace=False, p=P)
其中vec
是您的人口,P
是权重向量。
例如:
import numpy as np
vec=[1,2,3]
P=[0.5,0.2,0.3]
np.random.choice(vec,size=2,replace=False, p=P)
答案 1 :(得分:3)
对于 numpy ,Miriam Farber的回答是要走的路。
对于纯python,该技术是对群体进行预加权,然后使用 random.sample()提取值而无需替换:
>>> # Extract 10 values without replacement from a population
>>> # of ten heads and four tails.
>>> from random import sample
>>> population = ['heads', 'tails']
>>> counts = [10, 4]
>>> weighted_pop = [elem for elem, cnt in zip(population, counts) for i in range(cnt)]
>>> sample(weighted_pop, k=10)
['heads', 'tails', 'tails', 'heads', 'heads', 'tails', 'heads', 'heads', 'heads', 'heads']
注意,权重实际上是计数。这很重要,因为当您在没有替换的情况下进行采样时,每次选择都需要将计数减少一个。
答案 2 :(得分:2)
numpy
可能是最佳选择。但是这是另一个纯Python解决方案
无需更换的加权样品。
有几种方法可以定义population
和weights
的参数用途。可以定义population
来表示项目总数,并且weights
可以定义影响选择的偏差列表。例如,在赛马模拟中,population
可能是马-每匹马都有唯一的名称,weights
是它们的性能等级。下面的功能遵循该模型。
from random import random
from bisect import bisect_left
from itertools import accumulate
def wsample(population, weights, k=1):
wts = list(weights)
sampl = []
rnums = [random() for _ in range(k)]
for r in rnums:
acm_wts = list(accumulate(wts))
total = acm_wts[-1]
i = bisect_left(acm_wts, total * r)
p = population[i]
wts[i] = 0
sampl.append(p)
return sampl
通过将其权重设置为0并重新计算累积的权重,可以有效地将选定的个人从进一步的选择中删除。如果使用此功能,请确保k <= len(population)
。
第一个版本为测试第二个版本提供了很好的参考。与第一个版本相比,以下版本非常快。
在此下一版本中,累加权重仅计算一次,并且采样中的冲突会导致重试。这样的效果是从可能的选择中删除范围,而仍未选择的范围将频段与其他活动频段保持相对比例,以保持正确的选择概率在发挥作用。
键入选定索引的字典可确保每个选定成员都是唯一的个体。 dict
保留添加项目的顺序,并按选择顺序返回它们。
这个想法似乎行得通。这两种实施方式之间的测试结果非常接近。
def wsample(population, weights, k=1):
accum = list(accumulate(weights))
total = accum[-1]
sampl = {}
while len(sampl) < k:
index = bisect_left(accum, total * random())
sampl[index] = population[index]
return list(sampl.values())
尽管每个选择都有超过k
次的额外循环机会(取决于参数),但每次迭代消除O(n)accumulate()
操作的机会却大于以更快的执行时间来弥补它。如果需要预先累加权重,则可以更快地进行计算,但是对于我的应用程序,无论如何,每个循环都需要计算一次。
要使用此功能,可能希望在使用无限循环的任何应用程序中都采取这种措施。并可能要检查一两次,以确保参数能够正常工作。
在以下测试中,总体由10,000个项目组成,这些项目具有相同的相应随机产生的权重。该程序在运行10年以上的计算机上托管的VM上运行-任何人都能获得比此更好的结果,但是它显示了这两种方法的相对速度。
第一版:
timeit.timeit("wsample(population, weights, k=5)", globals=globals(), number=10**4)
21.74719240899867
第二个版本:
timeit.timeit("wsample(population, weights, k=5)", globals=globals(), number=10**4)
4.32836378099455
修改了第二版以预先累积权重:
timeit.timeit("wsample(population, acm_weights, k=5)", globals=globals(), number=10**4)
0.05602245099726133