4

約20,000個のオブジェクトの辞書があります。キーはオブジェクトの文字列表現であり、値はオブジェクト自体です。各オブジェクトには属性self.lengthとがありますself.rateself.rateとして計算され1.5E-8*self.lengthます。

代わりに、レートに基づいて、このdictから事前に決定された数(この例では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以上はもちろん、1つのオブジェクトが選択されるまでに何度も繰り返す必要があることだと思います。

長さの分布:

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

5 に答える 5

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)

ドキュメント

PS、クラスを呼び出すのは悪い考えobjectです。これは、組み込みのユニバーサル基本クラスの名前でもあるためです。

于 2012-08-01T11:20:06.957 に答える
1

この方法の方が速いかどうかはわかりませんが、より正確です。

  1. でcumsumを実行し、lengthというリストに保存しますcumsum
  2. 長さが整数であると仮定すると(そうでない場合は、正規化して0から1までの数値を選択する必要があります)、0からの最後の要素までの乱数を選択します。cumsum
  3. cumsumに目を通し、選択した数以下の最初の要素のインデックスを取得します。
  4. 手順2に進み、別の番号を選択します。

その場合、次のようlengthsになるとしましょう。ここで、との間の数値をランダムに選択します。要素は、私にとってより正確に聞こえる確率で取得されます。[1,4,2,10,5]cumsum[1,5,7,17,22]022ilengeths[i]/cumsum[-1]

于 2012-08-01T11:17:57.880 に答える
1

アイテムの重みを段階的に合計することにより、[0、T)で一様に乱数を選択することにより、重みに従ってランダムに1つを選択できます。ここで、Tはすべての重みの合計であり、最初のアイテムの大きい方のアイテムを取得します。それよりも合計(たとえば、バイナリチョップによる)。より大きなサンプルが必要な場合は、これを繰り返すか、このコードのように乱数を並べ替えて、マージソートのような手順を実行できます。複雑さは同じですが、バイナリチョップは常にエラーが発生しやすいため、コードは少し単純だと思います。

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']

はっきりしない場合は、「レート」を重みとして使用できます。レートが0.15のオブジェクトと0.3のオブジェクトがある場合、重要なのは、2番目のオブジェクトが最初のオブジェクトの2倍の頻度で表示されることです。これが、このコードでウェイトが行うことです。

于 2012-08-01T12:28:18.000 に答える
0

レートは5.865e-07から0.0011845の間で、均一なランダム選択は0から1の間です。中央値、1311に基づいて500個のオブジェクトを選択できれば、幸運だと思います。

ランダムな選択を変更する必要があります

x = random.uniform(0,1)

することが

import random
x = random.triangular(51, 103000 , 1311 )
于 2012-08-01T11:48:27.470 に答える
-2

十分なオブジェクトが必要な場合は、次のようにselect関数を記述できます。

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

このように、確率は定数cと長さ(レートに反映されます)に依存します

于 2012-08-01T11:27:02.297 に答える