Amostragem da distribuição de von Mises-Fisher em Python?

14

Estou procurando uma maneira simples de fazer uma amostra de uma distribuição multivariada de von Mises-Fisher em Python. Eu procurei no módulo de estatísticas no scipy e no módulo numpy, mas só encontrei a distribuição univariada de von Mises. Existe algum código disponível? Ainda não encontrei.

Aparentemente, Wood (1994) projetou um algoritmo para amostragem da distribuição vMF de acordo com este link , mas não consigo encontrar o documento.

- edit Por precisão, estou interessado no algoritmo que é difícil de encontrar na literatura (a maioria dos trabalhos se concentra em ). O artigo seminal (Wood, 1994) não pode ser encontrado de graça, que eu saiba.S2

microfone
fonte
1
A entrada para scipy.stats.vonmisespode ser de matriz, para que você possa especificar a distribuição como um array. Veja este exemplo
rightskewed 14/06/2015
Obrigado pela sua resposta. Mas, parece que é mais um produto de 1-D von Mises do que uma verdadeira nD von Mises-Fisher: K = vonmises.pdf([x,x], kappa=[[1],[10]]). Um vMF 2-D deve ter apenas um real como parâmetro. Você concorda? κ
mic
Estou procurando o algoritmo VM * originalmente em Simulação da distribuição de von Mises Fisher (Wood, 1994). Qualquer um?
mic
3
Eu achei as respostas neste tópico aqui realmente úteis. Forneci uma função utilitário levemente limpa para fazer isso como parte deste pacote: https://github.com/clara-labs/spherecluster/blob/develop/spherecluster/util.py , para aqueles que ainda desejam gerar esse dados.
Jaska

Respostas:

11

Finalmente, entendi. Aqui está a minha resposta.

Finalmente, coloquei minhas mãos nas Estatísticas Direcionais (Mardia e Jupp, 1999) e no algoritmo de amostragem de Ulrich-Wood. Coloco aqui o que entendi, ou seja, meu código (em Python).

O esquema de amostragem por rejeição:

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

E, para amostrar efetivamente com esse código, aqui está um exemplo:

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)
microfone
fonte
3
(+1) Obrigado por compartilhar sua resposta (especialmente apesar do desânimo em potencial de ter sua pergunta inicialmente fechada)!
whuber
4

(Peço desculpas pela formatação aqui, criei uma conta apenas para responder a essa pergunta, pois também estava tentando descobrir isso recentemente).

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 substitua

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

no exemplo do microfone com uma chamada para

v = sample_tangent_unit(mu)
Kevin
fonte