Hier ist ein Python-Code, der EMD und den Fluss für zwei Mischungen von Gaussian berechnet. Ich hoffe das hilft.
"""
Compute earth mover's distance (EMD) to compare two mixtures of Gaussians
Requires: numpy, cv2, csb
"""
import numpy as np, cv2
from csb.numeric import log_sum_exp
class GMM(object):
"""
1d mixture of Gaussians
"""
def __init__(self, K):
"""
K: number of components
"""
self.mu = np.zeros(K)
self.sigma = np.ones(K)
self.w = np.ones(K)
self.w/= self.w.sum()
@property
def K(self):
return len(self.mu)
def log_prob(self, x):
logp = - 0.5 * np.subtract.outer(x, self.mu)**2/self.sigma**2. \
- 0.5 * np.log(2*np.pi*self.sigma**2) + np.log(self.w)
return log_sum_exp(logp.T,0)
def __call__(self, x, normed=True):
logp = self.log_prob(x)
if np.iterable(x) and normed:
logp -= log_sum_exp(logp)
return np.exp(logp)
def randomize(self, mu_range=(-10, 10.), sigma_range=(0.1, 5.)):
self.mu[...] = np.random.uniform(*mu_range, size=self.K)
self.sigma[...] = np.random.uniform(*sigma_range, size=self.K)
self.w[...] = np.random.random(self.K)
self.w /= self.w.sum()
def make_signature(x, p):
P = np.transpose([p,x])
P64 = cv2.cv.fromarray(np.array(P.tolist()))
P32 = cv2.cv.CreateMat(P64.rows, P64.cols, cv2.cv.CV_32FC1)
cv2.cv.Convert(P64, P32)
return P32
def calc_emd(q, p, x):
Q = make_signature(x, q)
P = make_signature(x, p)
flow = cv2.cv.CreateMat(len(q), len(p), cv2.cv.CV_32F)
cv2.cv.Set(flow, 0.)
return cv2.cv.CalcEMD2(P, Q, cv2.cv.CV_DIST_L2, flow=flow), np.asarray(flow[:,:])
if __name__ == '__main__':
x = np.linspace(-10., 10., 100)
## create two mixtures of Gaussians with 3 and 5 components
f = GMM(3)
g = GMM(5)
f.randomize()
g.randomize()
## evaluate mixtures on 1d grid
q = f(x)
p = g(x)
## compute EMD and flow
emd, F = calc_emd(q, p, x)
## check marginals
print np.fabs(F.sum(0) - q).max(), np.fabs(F.sum(1)-p).max()