Python不是从对象列表中随机抽样

时间:2012-08-01 11:07:19

标签: python performance algorithm

我有大约20,000个对象的字典。键是对象的字符串表示,值是对象本身。每个对象都有属性self.lengthself.rateself.rate计算为1.5E-8*self.length

我需要根据他们的费率选择一个预先确定的数字(我们假设这个例子是500)这个项目中的项目。具有较低速率的对象将不太可能被选择,而具有较高速率的对象则更有可能被选择。

我认为我能做到这一点的方式非常缓慢。

在while循环中,当所选对象的数量小于所需选择的数量时,我生成一个介于0之间的随机数和dict的长度并选择该元素。然后我生成另一个随机数,如果随机数小于列表中所选对象的rate,则会将其添加到所选对象中。这开始似乎很好,但现在我意识到它太慢了。有没有人建议如何更快地做到这一点?

一些代码: 对象的类定义

from numpy import random
class object():
    def __init__(self, length):
        self.length  = length
        self.rate = (1.15E-8*self.length)

    def select(self):
        x = random.uniform(0,1)
        if(x<self.rate):
            return True
        else:
            return False

完成其余工作的功能(在另一个模块中):

def select_random(object_dict,maxselect):
    nselect = 0
    object_names = object_dict.keys()
    selected_objects = []
    while(nselect < maxselect):
        x = random.randint(0,len(object_dict))
        if(object_dict[object_names[x]].select()):
            nselect +=1
            selected_objects.append(object_names[x])
    return(selected_objects)

我认为让它变得非常慢的原因是每个对象的选择概率都很小,甚至在选择一个对象之前需要进行多次迭代,更不用说500个或更多了。

长度分布:

Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     51     822    1311    1770    2112  103000 

5 个答案:

答案 0 :(得分:2)

试试这个:

import numpy as np    # requires NumPy 1.7 (!)

def select_random(object_dict, n):
    keys = object_dict.keys()
    rate = np.array([x.rate for x in keys])
    prob = rate / rate.sum()
    return np.random.choice(keys, size=n, replace=True, p=prob)

Documentation

P.S。,调用类object是一个坏主意,因为这也是内置通用基类的名称。

答案 1 :(得分:1)

我不知道这种方法会更快但是更准确:

  1. length上做一个cumsum并将其保存到名为cumsum
  2. 的列表中
  3. 假设长度是整数(否则你必须标准化并选择0到1之间的数字)选择0到cumsum的最后一个元素之间的随机数
  4. 查看cumsum并获取第一个元素的索引,该索引小于或等于您选择的数字。
  5. 转到第2步。选择其他号码。
  6. 让我们说lengths[1,4,2,10,5],然后cumsum[1,5,7,17,22]现在您随机选择022之间的数字 - 你会得到i概率为lengeths[i]/cumsum[-1]的元素,这对我来说听起来更准确。

答案 2 :(得分:1)

通过逐步总结项目的权重,您可以根据权重随机选择一个,在[0,T)中统一选择一个随机数,其中T是所有权重的总和,并取项目&#39 ;第一个总数大于(通过例如二进制斩)。如果你想要一个更大的样本,你可以重复这个,或者像这样的代码对随机数进行排序并进行合并排序之类的步骤。复杂性是相同的,但代码有点简单,我认为二进制斩波总是容易出错。

import random

def accumulate_weights(weighted_items):
    T = 0.0
    for w, i in weighted_items:
        T += w
        yield (T, i)

def sample_weighted(weighted_items, n):
    cumulative = list(accumulate_weights(weighted_items))
    T = cumulative[-1][0]
    i = 0
    for sample in sorted(random.uniform(0, T) for _ in xrange(n)):
        while sample > cumulative[i][0]:
            i += 1
        yield cumulative[i][1]

r = list(sample_weighted([(1.0, 'a'), (2.0, 'b'), (5.0, 'c'), (1.0, 'd')], 10000))
print [(x, r.count(x)) for x in 'abcd']

如果情况不明显,您可以使用&#39;费率&#39;作为权重。当你有一个物体的速率为0.15而另一个物体的速度为0.3时,重要的是第二个物体出现的频率是第一个物体的两倍。这是代码中的权重!

答案 3 :(得分:0)

您的费率介于5.865e-07和0.0011845之间,您的统一随机选择介于0和1之间,我相信如果您能够根据中位数1311选择500个对象,您将会很幸运。

您需要更改随机选择

x = random.uniform(0,1)

import random
x = random.triangular(51, 103000 , 1311 )

答案 4 :(得分:-2)

如果你需要足够的对象,可以用这种方式编写select函数:

def select(self):
  x = randint(0,self.length)
  if x > self.legth - c:
   return False
  return True

这种方式的概率取决于常数c和长度(反映速率)