web-dev-qa-db-fra.com

Densité normale multivariée en Python?

Existe-t-il un paquet python qui permet le calcul efficace du pdf normal multivarié?

Il ne semble pas être inclus dans Numpy/Scipy, et étonnamment, une recherche Google n'a pas été utile.

45
Benno

La normale multivariée est désormais disponible sur SciPy 0.14.0.dev-16fc0af:

from scipy.stats import multivariate_normal
var = multivariate_normal(mean=[0,0], cov=[[1,0],[0,1]])
var.pdf([1,0])
61
juliohm

Je viens d'en faire un à mes fins, donc je pensais que je partagerais. Il est construit en utilisant "les pouvoirs" de numpy, sur la formule du cas non dégénéré de http://en.wikipedia.org/wiki/Multivariate_normal_distribution et il valide également l'entrée.

Voici le code avec un exemple d'exécution

from numpy import *
import math
# covariance matrix
sigma = matrix([[2.3, 0, 0, 0],
           [0, 1.5, 0, 0],
           [0, 0, 1.7, 0],
           [0, 0,   0, 2]
          ])
# mean vector
mu = array([2,3,8,10])

# input
x = array([2.1,3.5,8, 9.5])

def norm_pdf_multivariate(x, mu, sigma):
    size = len(x)
    if size == len(mu) and (size, size) == sigma.shape:
        det = linalg.det(sigma)
        if det == 0:
            raise NameError("The covariance matrix can't be singular")

        norm_const = 1.0/ ( math.pow((2*pi),float(size)/2) * math.pow(det,1.0/2) )
        x_mu = matrix(x - mu)
        inv = sigma.I        
        result = math.pow(math.e, -0.5 * (x_mu * inv * x_mu.T))
        return norm_const * result
    else:
        raise NameError("The dimensions of the input don't match")

print norm_pdf_multivariate(x, mu, sigma)
20
user692734

Dans le cas courant d'une matrice de covariance diagonale, les multivariées PDF peuvent être obtenues en multipliant simplement l'univariée PDF valeurs renvoyées par un scipy.stats.norm exemple. Si vous avez besoin du cas général, vous devrez probablement le coder vous-même (ce qui ne devrait pas être difficile).

7
Sven Marnach

Si toujours nécessaire, ma mise en œuvre serait

import numpy as np

def pdf_multivariate_gauss(x, mu, cov):
    '''
    Caculate the multivariate normal density (pdf)

    Keyword arguments:
        x = numpy array of a "d x 1" sample vector
        mu = numpy array of a "d x 1" mean vector
        cov = "numpy array of a d x d" covariance matrix
    '''
    assert(mu.shape[0] > mu.shape[1]), 'mu must be a row vector'
    assert(x.shape[0] > x.shape[1]), 'x must be a row vector'
    assert(cov.shape[0] == cov.shape[1]), 'covariance matrix must be square'
    assert(mu.shape[0] == cov.shape[0]), 'cov_mat and mu_vec must have the same dimensions'
    assert(mu.shape[0] == x.shape[0]), 'mu and x must have the same dimensions'
    part1 = 1 / ( ((2* np.pi)**(len(mu)/2)) * (np.linalg.det(cov)**(1/2)) )
    part2 = (-1/2) * ((x-mu).T.dot(np.linalg.inv(cov))).dot((x-mu))
    return float(part1 * np.exp(part2))

def test_gauss_pdf():
    x = np.array([[0],[0]])
    mu  = np.array([[0],[0]])
    cov = np.eye(2) 

    print(pdf_multivariate_gauss(x, mu, cov))

    # prints 0.15915494309189535

if __== '__main__':
    test_gauss_pdf()

Dans le cas où je ferais des changements futurs, le code est ici sur GitHub

7
user2489252

Je connais plusieurs packages python qui l'utilisent en interne, avec des généralités différentes et pour des utilisations différentes, mais je ne sais pas si l'un d'eux est destiné aux utilisateurs.

statsmodels, par exemple, a la fonction et la classe cachées suivantes, mais elle n'est pas utilisée par statsmodels:

https://github.com/statsmodels/statsmodels/blob/master/statsmodels/miscmodels/try_mlecov.py#L36

https://github.com/statsmodels/statsmodels/blob/master/statsmodels/sandbox/distributions/mv_normal.py#L777

Essentiellement, si vous avez besoin d'une évaluation rapide, réécrivez-la pour votre cas d'utilisation.

3
Josef

J'utilise le code suivant qui calcule la valeur logpdf, ce qui est préférable pour les grandes dimensions. Il fonctionne également pour les matrices scipy.sparse.

import numpy as np
import math
import scipy.sparse as sp
import scipy.sparse.linalg as spln

def lognormpdf(x,mu,S):
    """ Calculate gaussian probability density of x, when x ~ N(mu,sigma) """
    nx = len(S)
    norm_coeff = nx*math.log(2*math.pi)+np.linalg.slogdet(S)[1]

    err = x-mu
    if (sp.issparse(S)):
        numerator = spln.spsolve(S, err).T.dot(err)
    else:
        numerator = np.linalg.solve(S, err).T.dot(err)

    return -0.5*(norm_coeff+numerator)

Le code provient de pyParticleEst , si vous voulez la valeur pdf au lieu du logpdf, prenez simplement math.exp () sur la valeur retournée

3
ajn

La densité peut être calculée d'une manière assez simple en utilisant les fonctions numpy et la formule sur cette page: http://en.wikipedia.org/wiki/Multivariate_normal_distribution . Vous pouvez également utiliser la fonction de vraisemblance (probabilité logarithmique), qui est moins susceptible de déborder pour les grandes dimensions et est un peu plus simple à calculer. Les deux impliquent simplement de pouvoir calculer le déterminant et l'inverse d'une matrice.

Le CDF, d'autre part, est un animal entièrement différent ...

2
Andrew Mao

Le code suivant m'a aidé à résoudre, quand on donne un vecteur, quelle est la probabilité que le vecteur soit dans une distribution normale multivariée.

import numpy as np
from scipy.stats import multivariate_normal

données avec tous les vecteurs

d= np.array([[1,2,1],[2,1,3],[4,5,4],[2,2,1]])

moyenne des données sous forme vectorielle, qui auront la même longueur que le vecteur d'entrée (ici son 3)

mean = sum(d,axis=0)/len(d)

OR
mean=np.average(d , axis=0)
mean.shape

trouver une covarianve de vecteurs qui auront la forme de [forme de vecteur d'entrée X forme de vecteur d'entrée] ici c'est 3x3

cov = 0
for e in d:
  cov += np.dot((e-mean).reshape(len(e), 1), (e-mean).reshape(1, len(e)))
cov /= len(d)
cov.shape

préparation d'une distribution gaussienne multivariée à partir de la variance moyenne et co

dist = multivariate_normal(mean,cov)

trouver la fonction de distribution de probabilité.

print(dist.pdf([1,2,3]))

3.050863384798471e-05

La valeur ci-dessus donne la probabilité.

0
Shahad