I'm trying to calculate mutual information in Python, using numpy. My implementation so far is:
def mutual_info(x, y, bins=[10000, 10000]):
"""Calculate mutual information based on 2D histrograms
"""
hist = np.histogram2d(x, y, bins, normed=True)[0]
joint_prob = hist / np.sum(hist)
hist = np.histogram(x, bins[0], density=True)[0]
probs_x = hist / np.sum(hist)
hist = np.histogram(y, bins[1], density=True)[0]
probs_y = hist / np.sum(hist)
probs = joint_prob / (np.reshape(probs_x, [-1, 1]) * probs_y)
# use masked array to avoid NaNs
info = (joint_prob * np.ma.log2(probs)).sum()
return info
The problem is that it is not returning a near-zero value for two completely independent random variables:
a = np.random.rand(10000)
b = np.random.rand(10000)
mutual_info(a, a)
# 12.471484491681876
mutual_info(a, b)
# 11.640764212276384
I notice that in R, using the mi.plugin
function from the entropy
package also doesn't result in a near-zero result, but it is at least much lower for independent variables:
R> a = runif(10000)
R> b = runif(10000)
R> counts = hist2d(a, a, show=FALSE, bins=1000)[['counts']]
R> mi.plugin(counts)
[1] 5.288
R> counts = hist2d(a, b, show=FALSE, bins=1000)[['counts']]R> mi.plugin(counts)
[1] 1.532
Is there something wrong with my implementation, or am I misunderstanding mutual information?