Campionamento dalla distribuzione von Mises-Fisher in Python?


14

Sto cercando un modo semplice per campionare da una distribuzione multivariata von Mises-Fisher in Python. Ho cercato nel modulo stats in scipy e nel modulo numpy ma ho trovato solo la distribuzione univariata di von Mises. C'è qualche codice disponibile? Non ho ancora trovato

Apparentemente, Wood (1994) ha progettato un algoritmo per il campionamento dalla distribuzione vMF secondo questo link , ma non riesco a trovare il documento.

- modifica Per precisione, sono interessato all'algoritmo che è difficile da trovare in letteratura (la maggior parte degli articoli si concentra su ). L'articolo fondamentale (Wood, 1994) non può essere trovato gratuitamente, per quanto ne sappia.S2


1
L'input to scipy.stats.vonmisespuò essere simile a array, quindi è possibile specificare la distribuzione come array. Vedi questo esempio
rightskewed

Grazie per la tua risposta. Ma, sembra che sia più un prodotto di 1-D von Mises di un vero e proprio nD von Mises-Fisher: K = vonmises.pdf([x,x], kappa=[[1],[10]]). Un vMF 2-D dovrebbe avere solo un real come parametro. Sei d'accordo? κ
Mic

Sto cercando l'algoritmo VM * originariamente in Simulazione della distribuzione di von Mises Fisher (Wood, 1994). Chiunque?
Mic

3
Ho trovato le risposte in questa discussione davvero utili. Ho fornito una funzione di utilità leggermente ripulita per farlo come parte di questo pacchetto: https://github.com/clara-labs/spherecluster/blob/develop/spherecluster/util.py , per coloro che stanno ancora cercando di generarlo dati.
Jaska,

Risposte:


11

Alla fine l'ho ottenuto. Ecco la mia risposta

Alla fine ho messo le mani su Directional Statistics (Mardia e Jupp, 1999) e sull'algoritmo di Ulrich-Wood per il campionamento. Pubblico qui ciò che ho capito, ovvero il mio codice (in Python).

Lo schema di campionamento del rifiuto:

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

Quindi, il campionamento desiderato è , dove è il risultato dello schema di campionamento del rifiuto e viene uniformemente campionato sull'ipersfera.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

E, per un efficace campionamento con questo codice, ecco un esempio:

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)

3
(+1) Grazie per aver condiviso la tua risposta (soprattutto nonostante il potenziale scoraggiamento di avere la domanda inizialmente chiusa)!
whuber

4

(Mi scuso per la formattazione qui, ho creato un account solo per rispondere a questa domanda, dal momento che stavo anche provando a capirlo di recente).

vSp-2μvμv1-w2+wμ

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)

e sostituisci

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

nell'esempio del microfono con una chiamata a

v = sample_tangent_unit(mu)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.