Pengambilan sampel dari distribusi von Mises-Fisher dengan Python?

14

Saya mencari cara sederhana untuk mengambil sampel dari distribusi multivariat von Mises-Fisher dengan Python. Saya telah melihat dalam modul stats dalam modul scipy dan numpy tetapi hanya menemukan distribusi univariat von Mises. Apakah ada kode yang tersedia? Saya belum menemukan.

Rupanya, Wood (1994) telah merancang algoritma untuk pengambilan sampel dari distribusi vMF menurut tautan ini , tetapi saya tidak dapat menemukan makalahnya.

- sunting Untuk presisi, saya tertarik dengan algoritma yang sulit ditemukan dalam literatur (sebagian besar makalah fokus pada ). Artikel seminal (Wood, 1994) tidak dapat ditemukan secara gratis, setahu saya.S2

mik
sumber
1
Input ke scipy.stats.vonmisesdapat berupa array, sehingga Anda dapat menentukan distribusi sebagai array. Lihat contoh
rightskewed
K = vonmises.pdf([x,x], kappa=[[1],[10]])κ
Saya mencari algoritma VM * awalnya dalam Simulasi distribusi von Mises Fisher (Wood, 1994). Siapa saja?
mic
3
Saya menemukan jawaban di utas ini sangat berguna. Saya telah menyediakan fungsi utilitas yang sedikit dibersihkan untuk melakukan ini sebagai bagian dari paket ini: https://github.com/clara-labs/spherecluster/blob/develop/spherecluster/util.py , bagi mereka yang masih ingin menghasilkan ini data.
Jaska

Jawaban:

11

Akhirnya, saya mengerti. Inilah jawaban saya.

Saya akhirnya menaruh tangan saya pada Statistik Directional (Mardia dan Jupp, 1999) dan pada algoritma Ulrich-Wood untuk pengambilan sampel. Saya memposting di sini apa yang saya mengerti darinya, yaitu kode saya (dengan Python).

Skema penolakan sampel:

def rW(n, kappa, m):
    dim = m-1
    b = dim / (np.sqrt(4*kappa*kappa + dim*dim) + 2*kappa)
    x = (1-b) / (1+b)
    c = kappa*x + dim*np.log(1-x*x)

    y = []
    for i in range(0,n):
        done = False
        while not done:
            z = sc.stats.beta.rvs(dim/2,dim/2)
            w = (1 - (1+b)*z) / (1 - (1-b)*z)
            u = sc.stats.uniform.rvs()
            if kappa*w + dim*np.log(1-x*w) - c >= np.log(u):
                done = True
        y.append(w)
    return y

v1-w2+wμwv

def rvMF(n,theta):
    dim = len(theta)
    kappa = np.linalg.norm(theta)
    mu = theta / kappa

    result = []
    for sample in range(0,n):
        w = rW(n, kappa, dim)
        v = np.random.randn(dim)
        v = v / np.linalg.norm(v)

        result.append(np.sqrt(1-w**2)*v + w*mu)

    return result

Dan, untuk pengambilan sampel yang efektif dengan kode ini, berikut adalah contohnya:

import numpy as np
import scipy as sc
import scipy.stats

n = 10
kappa = 100000
direction = np.array([1,-1,1])
direction = direction / np.linalg.norm(direction)

res_sampling = rvMF(n, kappa * direction)
mik
sumber
3
(+1) Terima kasih telah membagikan jawaban Anda (terutama meskipun ada kemungkinan kecil pertanyaan Anda akan ditutup)!
whuber
4

(Saya minta maaf atas pemformatan di sini, saya membuat akun hanya untuk menjawab pertanyaan ini, karena saya juga mencoba mencari tahu ini baru-baru ini).

Jawaban mik tidak benar, vektor v perlu berasal Shal-2 di ruang singgung untuk μ, itu adalah, v harus menjadi satuan vektor ortogonal untuk μ. Kalau tidak, vektorv1-w2+wμtidak akan memiliki norma. Anda dapat melihat ini dalam contoh yang diberikan oleh mic. Untuk memperbaikinya, gunakan sesuatu seperti:

import scipy.linalg as la
def sample_tangent_unit(mu):
    mat = np.matrix(mu)

    if mat.shape[1]>mat.shape[0]:
        mat = mat.T

    U,_,_ = la.svd(mat)
    nu = np.matrix(np.random.randn(mat.shape[0])).T
    x = np.dot(U[:,1:],nu[1:,:])
    return x/la.norm(x)

dan ganti

v = np.random.randn(dim)
v = v / np.linalg.norm(v)

dalam contoh mic dengan panggilan ke

v = sample_tangent_unit(mu)
Kevin
sumber