在python中没有替换的加权随机样本

时间:2017-04-21 18:18:08

标签: python numpy random

我需要获得一个 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])) 

虽然这有效,但它需要在数组,列表和数组之间来回切换,因此不太理想。

我正在寻找最简单易懂的解决方案,因为此代码将与其他人共享。

3 个答案:

答案 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解决方案 无需更换的加权样品。

有几种方法可以定义populationweights的参数用途。可以定义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