web-dev-qa-db-ja.com

加重確率に基づいてpython辞書からキーを選択する方法は?

Python辞書があります。ここで、キーはいくつかのアイテムを表し、値はそのアイテムのいくつかの(正規化された)重み付けを表します。例:

d = {'a': 0.0625, 'c': 0.625, 'b': 0.3125}
# Note that sum([v for k,v in d.iteritems()]) == 1 for all `d`

アイテムと重みのこの相関関係を考えると、結果が「a」である場合の6.25%、結果が「b」である場合の32.25%、および62.5%となるように、dからキーを選択するにはどうすればよいですか。結果の 'c'?

10
Joseph
def weighted_random_by_dct(dct):
    Rand_val = random.random()
    total = 0
    for k, v in dct.items():
        total += v
        if Rand_val <= total:
            return k
    assert False, 'unreachable'

トリックを行う必要があります。各キーを調べて現在の合計を維持し、ランダムな値(0から1の間)がスロットに入ると、そのキーを返します

10
Anthony Sottile

これを頻繁に行うことを計画している場合は、numpyを使用して、 np.random.choice() を使用して重み付き確率のリストからキーを選択できます。以下の例では、重み付き確率を使用してキーを10,000回選択します。

import numpy as np

probs = [0.0625, 0.625, 0.3125]
keys = ['a', 'c', 'b']

choice_list = np.random.choice(keys, 10000, replace=True, p=probs)
4
roganjosh

ここでの使用例はわかりませんが、NLTKパッケージの度数分布/確率分布クラスを確認できます。NLTKパッケージには、すべての重要な詳細が含まれています。

FreqDist はカウンターの拡張であり、 ProbDistI インターフェースに渡すことができます。 ProbDistIインターフェイスは、分布のサンプリングに使用できる「generate()」メソッドと、特定のキーの確率を取得するために使用できる「prob(sample)」メソッドを公開します。

あなたの場合、最尤推定を使用したいので、MLEProbDist。配布をスムーズにしたい場合は、LaplaceProbDistまたはSimpleGoodTuringProbDistを試すことができます。

例えば:

from nltk.probability import FreqDist, MLEProbDist

d = {'a': 6.25, 'c': 62.5, 'b': 31.25}
freq_dist = FreqDist(d)
prob_dist = MLEProbDist(freq_dist)

print prob_dist.prob('a')
print prob_dist.prob('b')
print prob_dist.prob('c')
print prob_dist.prob('d')

「0.06250.31250.6250.0」と出力されます。

新しいサンプルを生成するには、次を使用できます。

prob_dist.generate()
3
Matt1267

Numpyを使用できる場合は、次のように numpy.random.choice 関数を使用できます。

import numpy as np

d = {'a': 0.0625, 'c': 0.625, 'b': 0.3125}

def pick_by_weight(d):
    d_choices = []
    d_probs = []
    for k,v in d.iteritems():
      d_choices.append(k)
      d_probs.append(v)
    return np.random.choice(d_choices, 1, p=d_probs)[0]


d = {'a': 0.0625, 'c': 0.625, 'b': 0.3125}
choice = pick_by_weight(d)
1
Joseph

キーが重み値であり、値が取得できるキーのリストである「反転」ディクショナリを保持すると便利な場合があります。そうすれば、より多くのキーが同じ重みを持つ場合に、それを配布するのが簡単になります。

from collections import defaultdict
import random

dict = {'a': 0.0625, 'd': 0.0625, 'c': 0.625, 'b': 0.3125}

inverted_dict = defaultdict(list)

for k, v in dict.items():
    inverted_dict[v].append(k)

# Here first you get a random value between 0 and 1, which is your weigth
# Then, you choose a random value from the list of keys that have the same weight
print(random.choice(inverted_dict[random.choice(inverted_dict.keys())]))
0
ChatterOne

私が理解したこと:0から1の間で一様に乱数を生成する単純なランダム関数が必要です。値が_0 to 0.0625_の間にある場合は、キーaを選択します。 0.0625 and (0.0625 + 0.625)の間にある場合は、キーcなどを選択します。これは、これで実際に言及されていることです answer

乱数は均一に生成されるため、重みが大きいキーが他のキーよりも多く選択されることが期待されます

0
Wasi Ahmad