Более быстрая свертка функций плотности вероятности в Python
предположим, что необходимо вычислить свертку общего числа дискретных функций плотности вероятности. Для примера ниже приведены четыре дистрибутивов, которые принимают значения 0,1,2 с заданными вероятностями:
import numpy as np
pdfs = np.array([[0.6,0.3,0.1],[0.5,0.4,0.1],[0.3,0.7,0.0],[1.0,0.0,0.0]])
свертку можно найти следующим образом:
pdf = pdfs[0]
for i in range(1,pdfs.shape[0]):
pdf = np.convolve(pdfs[i], pdf)
вероятности видения 0,1,...,8 затем на
array([ 0.09 , 0.327, 0.342, 0.182, 0.052, 0.007, 0. , 0. , 0. ])
эта часть является узким местом в моем коде, и кажется, что должно быть что-то доступное для векторизации этой операции. У кого-нибудь есть предложение сделать это быстрее?
альтернативно, решение, где вы можете использовать
pdf1 = np.array([[0.6,0.3,0.1],[0.5,0.4,0.1]])
pdf2 = np.array([[0.3,0.7,0.0],[1.0,0.0,0.0]])
convolve(pd1,pd2)
и получить попарные свертки
array([[ 0.18, 0.51, 0.24, 0.07, 0. ],
[ 0.5, 0.4, 0.1, 0. , 0. ]])
также очень помогло бы.
1 ответов
вы можете вычислить свертку всех ваших PDF-файлов эффективно, используя быстрые преобразования Фурье( FFS): ключевым фактом является то, что БПФ свертки является произведением FFTs индивидуальных функций плотности вероятности. Поэтому преобразуйте каждый PDF-файл, умножьте преобразованные PDF-файлы вместе, а затем выполните обратное преобразование. Вам нужно будет заполнить каждый входной PDF-файл нулями соответствующей длины, чтобы избежать эффектов от wraparound.
это должно быть разумно эффективный: если у вас есть m
PDF-файлы, каждый из которых содержит n
записи, то время для вычисления свертки с помощью этого метода должно расти как (m^2)n log(mn)
. Время доминирует FFS, и мы эффективно вычисляем m + 1
независимая БПФ (m
прямые преобразования и одно обратное преобразование), каждый из массива длиной не более mn
. Но как всегда, если вы хотите реальные тайминги, вы должны профиль.
вот код:
import numpy.fft
def convolve_many(arrays):
"""
Convolve a list of 1d float arrays together, using FFTs.
The arrays need not have the same length, but each array should
have length at least 1.
"""
result_length = 1 + sum((len(array) - 1) for array in arrays)
# Copy each array into a 2d array of the appropriate shape.
rows = numpy.zeros((len(arrays), result_length))
for i, array in enumerate(arrays):
rows[i, :len(array)] = array
# Transform, take the product, and do the inverse transform
# to get the convolution.
fft_of_rows = numpy.fft.fft(rows)
fft_of_convolution = fft_of_rows.prod(axis=0)
convolution = numpy.fft.ifft(fft_of_convolution)
# Assuming real inputs, the imaginary part of the output can
# be ignored.
return convolution.real
применяя это к вашему примеру, вот что я получаю:
>>> convolve_many([[0.6, 0.3, 0.1], [0.5, 0.4, 0.1], [0.3, 0.7], [1.0]])
array([ 0.09 , 0.327, 0.342, 0.182, 0.052, 0.007])