У меня есть массив размером MxN, и мне нравится вычислять значение энтропии каждой строки. Какой способ сделать это быстрее всего?
Самый быстрый способ вычислить энтропию каждой строки массива numpy?
Ответы (2)
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
использует натуральный логарифм, поэтому, чтобы получить логарифм по основанию 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])
scipy.stats.entropy
также вычисляет то же значение, что и entr(p).sum(axis=1)
- person spinup; 07.02.2019
Как отметил @Warren, из вашего вопроса неясно, исходите ли вы из массива вероятностей или из самих сырых выборок. В своем ответе я предположил последнее, и в этом случае основным узким местом будет вычисление количества бункеров для каждой строки.
Предполагая, что каждый вектор образцов относительно длинный, самым быстрым способом сделать это, вероятно, будет использование _ 1_:
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)
Хотя метод Уоррена для вычисления энтропий из значений вероятности с использованием entr
немного быстрее, чем использование явной формулы, на практике это, вероятно, представляет крошечную долю от общего времени выполнения по сравнению со временем, затрачиваемым на вычисление счетчиков бинов.
Правильность теста для одной строки:
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 до количества уникальных значений, вы можете преобразовать их в этот формат, используя _ 6_:
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
-np.dot(a, b)
вместо -np.sum(a * b)
- person atomsmasher; 09.12.2016
np.dot
я не могу легко векторизовать вычисление энтропии по нескольким строкам. Один из способов - это что-то вроде -np.einsum('ij,ij->i', p, np.log2(p))
, хотя на самом деле вы могли бы просто использовать entr
для этой части, поскольку у нее есть аргумент axis
. В любом случае дорогостоящая часть обычно вычисляет количество бункеров.
- person ali_m; 10.12.2016
probs
, и мне нужна энтропия строк. Если у вас еще нет вероятностей, уточните вопрос. - person Warren Weckesser   schedule 09.11.2015