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.
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])
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)
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).
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
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
Essentiellement, si vous avez besoin d'une évaluation rapide, réécrivez-la pour votre cas d'utilisation.
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
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 ...
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
d= np.array([[1,2,1],[2,1,3],[4,5,4],[2,2,1]])
mean = sum(d,axis=0)/len(d)
OR
mean=np.average(d , axis=0)
mean.shape
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
dist = multivariate_normal(mean,cov)
print(dist.pdf([1,2,3]))
3.050863384798471e-05
La valeur ci-dessus donne la probabilité.