29

各タプルが確率とアイテムで構成されるタプルのリストが与えられた場合、その確率に従ってアイテムをサンプリングしたいと思います。たとえば、リスト [ (.3, 'a'), (.4, 'b'), (.3, 'c')] を指定します。40% の確率で 'b' をサンプリングしたいと思います。

Pythonでこれを行う標準的な方法は何ですか?

適切な関数を持っていないように見えるランダムモジュールと、多項式関数を持っているが、この問題に対して適切な形式で結果を返さないように見える numpy.random を見てきました。私は基本的に、matlab で mnrnd のようなものを探しています。

どうもありがとう。

すべての回答を迅速にありがとうございます。明確にするために、私はサンプリングスキームの書き方の説明を探しているのではなく、一連のオブジェクトと重みが与えられた多項分布からサンプリングする簡単な方法を指摘するか、そのような関数が存在しないと言われたい.標準ライブラリにあるので、自分で書く必要があります。

4

9 に答える 9

19

これはあなたが望むことをするかもしれません:

numpy.array([.3,.4,.3]).cumsum().searchsorted(numpy.random.sample(5))
于 2011-06-21T22:18:58.183 に答える
11
import numpy

n = 1000
pairs = [(.3, 'a'), (.3, 'b'), (.4, 'c')]
probabilities = numpy.random.multinomial(n, zip(*pairs)[0])
result = zip(probabilities, zip(*pairs)[1])
# [(299, 'a'), (299, 'b'), (402, 'c')]
[x[0] * x[1] for x in result]
# ['aaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbb', 'cccccccccccccccccccc']

結果をどのように受け取りたいですか?

于 2011-06-21T22:24:22.253 に答える
3

たとえば、確率がパーセンテージにうまく収まる場合などにできるハックがあります。

たとえば、パーセンテージに問題がない場合は、次のように動作します (メモリのオーバーヘッドが高くなります)。

しかし、任意のフロート確率でそれを行う「本当の」方法は、構築後に累積分布からサンプリングすることです。これは、単位間隔 [0,1] を 'a'、'b'、および 'c' というラベルの付いた 3 つの線分に分割することと同じです。次に、単位間隔でランダムな点を選択し、それがどの線分かを確認します。

#!/usr/bin/python3
def randomCategory(probDict):
    """
        >>> dist = {'a':.1, 'b':.2, 'c':.3, 'd':.4}

        >>> [randomCategory(dist) for _ in range(5)]
        ['c', 'c', 'a', 'd', 'c']

        >>> Counter(randomCategory(dist) for _ in range(10**5))
        Counter({'d': 40127, 'c': 29975, 'b': 19873, 'a': 10025})
    """
    r = random.random() # range: [0,1)
    total = 0           # range: [0,1]
    for value,prob in probDict.items():
        total += prob
        if total>r:
            return value
    raise Exception('distribution not normalized: {probs}'.format(probs=probDict))

確率が 0 であっても値を返すメソッドには注意する必要があります。幸い、このメソッドはそうではありませんが、念のため、 を挿入できif prob==0: continueます。


記録のために、これを行うためのハックな方法は次のとおりです。

import random

def makeSampler(probDict):
    """
        >>> sampler = makeSampler({'a':0.3, 'b':0.4, 'c':0.3})
        >>> sampler.sample()
        'a'
        >>> sampler.sample()
        'c'
    """
    oneHundredElements = sum(([val]*(prob*100) for val,prob in probDict.items()), [])
    def sampler():
        return random.choice(oneHundredElements)
    return sampler

ただし、解像度の問題がない場合は、これがおそらく最速の方法です。=)

于 2011-06-21T22:09:55.647 に答える
1

リストに3つの「a」、4つの「b」、3つの「c」を作成し、ランダムに1つを選択する方法。十分な反復を行うと、目的の確率が得られます。

于 2011-06-21T22:04:32.390 に答える
1

多項関数は、ランダムな順序で分布のサンプルを取得するためのかなり簡単な方法だと思います。これはただの方法です

import numpy
from itertools import izip

def getSamples(input, size):
    probabilities, items = zip(*input)
    sampleCounts = numpy.random.multinomial(size, probabilities)
    samples = numpy.array(tuple(countsToSamples(sampleCounts, items)))
    numpy.random.shuffle(samples)
    return samples

def countsToSamples(counts, items):
    for value, repeats in izip(items, counts):
        for _i in xrange(repeats):
            yield value

入力は指定されたとおり[(.2, 'a'), (.4, 'b'), (.3, 'c')]で、サイズは必要なサンプル数です。

于 2011-06-21T22:57:30.710 に答える
0

の非常に単純な(そして正しい)答えに触発されたsholteだけです。次のような任意のアイテムを処理するために拡張するのがいかに簡単かを示します。

In []: s= array([.3, .4, .3]).cumsum().searchsorted(sample(54))
In []: c, _= histogram(s, bins= arange(4))
In []: [item* c[i] for i, item in enumerate('abc')]
Out[]: ['aaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbbbbbb', 'cccccccccccccccc']

更新
のフィードバックに基づいて、次のようphant0mに基づいてさらに簡単なソリューションを実装できることがわかりました。multinomial

In []: s= multinomial(54, [.3, .4, .3])
In []: [item* s[i] for i, item in enumerate('abc')]
Out[]: ['aaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbbbbbbb', 'cccccccccccc']

ここに私見があり、同様の結果をもたらすサンプリングに基づいた素晴らしい要約がempirical cdfあります。multinomialしたがって、要約すると、目的に最も適したものを選択してください。

于 2011-06-22T18:28:44.083 に答える
0

これがあなたが求めることを行うためのpythonicな方法であるかどうかはわかりませんが random.sample(['a','a','a','b','b','b','b','c','c','c'],k) 、kは必要なサンプルの数である場合に使用できます。

より堅牢な方法として、累積確率に基づいて単位区間をセクションに二分し、random.random()を使用して一様分布(0,1)から描画します。この場合、サブインターバルは(0、.3)(。3、.7)(。7,1)になります。どのサブインターバルに該当するかに基づいて要素を選択します。

于 2011-06-21T22:04:55.677 に答える