计算每个numpy数组行熵的最快方法?

时间:2015-11-09 10:27:17

标签: python performance numpy entropy information-theory

我有一个大小为MxN的数组,我喜欢计算每一行的熵值。最快的方法是什么?

2 个答案:

答案 0 :(得分:6)

scipy.special.entr为数组中的每个元素计算-x * log(x)。在调用之后,您可以对行进行求和。

这是一个例子。首先,创建一个正值的数组p,其行总和为1:

In [23]: np.random.seed(123)

In [24]: x = np.random.rand(3, 10)

In [25]: p = x/x.sum(axis=1, keepdims=True)

In [26]: p
Out[26]: 
array([[ 0.12798052,  0.05257987,  0.04168536,  0.1013075 ,  0.13220688,
         0.07774843,  0.18022149,  0.1258417 ,  0.08837421,  0.07205402],
       [ 0.08313743,  0.17661773,  0.1062474 ,  0.01445742,  0.09642919,
         0.17878489,  0.04420998,  0.0425045 ,  0.12877228,  0.1288392 ],
       [ 0.11793032,  0.15790292,  0.13467074,  0.11358463,  0.13429674,
         0.06003561,  0.06725376,  0.0424324 ,  0.05459921,  0.11729367]])

In [27]: p.shape
Out[27]: (3, 10)

In [28]: p.sum(axis=1)
Out[28]: array([ 1.,  1.,  1.])

现在计算每行的熵。 entr使用自然对数,因此要获得base-2日志,请将结果除以log(2)

In [29]: from scipy.special import entr

In [30]: entr(p).sum(axis=1)
Out[30]: array([ 2.22208731,  2.14586635,  2.22486581])

In [31]: entr(p).sum(axis=1)/np.log(2)
Out[31]: array([ 3.20579434,  3.09583074,  3.20980287])

如果您不希望依赖scipy,则可以使用显式公式:

In [32]: (-p*np.log2(p)).sum(axis=1)
Out[32]: array([ 3.20579434,  3.09583074,  3.20980287])

答案 1 :(得分:1)

正如@Warren指出的那样,从你的问题中不清楚你是从一系列概率开始,还是从原始样本本身开始。在我的回答中,我假设后者,在这种情况下,主要瓶颈将是计算每行的箱数。

假设每个样本矢量都相对较长,最快的方法可能就是使用np.bincount

import numpy as np

def entropy(x):
    """
    x is assumed to be an (nsignals, nsamples) array containing integers between
    0 and n_unique_vals
    """
    x = np.atleast_2d(x)
    nrows, ncols = x.shape
    nbins = x.max() + 1

    # count the number of occurrences for each unique integer between 0 and x.max()
    # in each row of x
    counts = np.vstack((np.bincount(row, minlength=nbins) for row in x))

    # divide by number of columns to get the probability of each unique value
    p = counts / float(ncols)

    # compute Shannon entropy in bits
    return -np.sum(p * np.log2(p), axis=1)

尽管Warren使用entr从概率值计算熵的方法比使用显式公式略快,但在实践中,与计算时间相比,这可能只占总运行时间的一小部分bin计数。

测试单行的正确性:

vals = np.arange(3)
prob = np.array([0.1, 0.7, 0.2])
row = np.random.choice(vals, p=prob, size=1000000)

print("theoretical H(x): %.6f, empirical H(x): %.6f" %
      (-np.sum(prob * np.log2(prob)), entropy(row)[0]))
# theoretical H(x): 1.156780, empirical H(x): 1.157532

测试速度:

In [1]: %%timeit x = np.random.choice(vals, p=prob, size=(1000, 10000))
   ....: entropy(x)
   ....: 
10 loops, best of 3: 34.6 ms per loop

如果您的数据不包含介于0和唯一值数之间的整数索引,则可以使用np.unique将其转换为此格式:

y = np.random.choice([2.5, 3.14, 42], p=prob, size=(1000, 10000))
unq, x = np.unique(y, return_inverse=True)
x.shape = y.shape