Лучший способ написать функцию Python, которая интегрирует гауссова?

при попытке использовать четырехъядерный метод scipy для интеграции Гаусса (скажем, есть гауссовский метод с именем Гаусса) у меня возникли проблемы с передачей необходимых параметров Гауссу и оставлением quad для интеграции по правильной переменной. У кого-нибудь есть хороший пример использования quad w/ многомерной функции?

но это привело меня к более грандиозному вопросу о лучшем способе интеграции Гаусса в целом. Я не нашел гауссовской интеграции в scipy (к моему сюрприз.) Мой план состоял в том, чтобы написать простую гауссову функцию и передать ее quad (или, может быть, теперь интегратор фиксированной ширины). Что бы ты сделал?

Edit: Fixed-width означает что-то вроде trapz, который использует фиксированный dx для вычисления областей под кривой.

то, к чему я пришел до сих пор, - это метод make___gauss, который возвращает лямбда-функцию, которая затем может перейти в quad. Таким образом, я могу сделать нормальную функцию со средним и дисперсией, которые мне нужны раньше интегрирующий.

def make_gauss(N, sigma, mu):
    return (lambda x: N/(sigma * (2*numpy.pi)**.5) *
            numpy.e ** (-(x-mu)**2/(2 * sigma**2)))

quad(make_gauss(N=10, sigma=2, mu=0), -inf, inf)

когда я попытался передать общую гауссову функцию (которую нужно вызвать с помощью x, N, mu и sigma) и заполнить некоторые значения, используя quad, как

quad(gen_gauss, -inf, inf, (10,2,0))

параметры 10, 2 и 0 не обязательно совпадали с N=10, sigma=2, mu=0, что вызвало более расширенное определение.

erf (z) в scipy.special потребует от меня точно определить, что такое t изначально, но приятно знать, что он есть.

5 ответов


хорошо, вы, кажется, довольно смущены несколькими вещами. Давайте начнем с начала: вы упомянули "многомерную функцию", но затем перейдем к обсуждению обычной гауссовой кривой с одной переменной. Это не многомерная функция: когда вы интегрируете ее, вы интегрируете только одну переменную (x). Различие важно сделать, потому что там is монстр, называемый "многомерным гауссовым распределением", который является истинным многомерным функция и, если интегрирована, требует интеграции по двум или более переменным (который использует дорогостоящий метод Монте-Карло, упомянутый выше). Но вы, кажется, просто говорите о регулярной гауссовой переменной, с которой намного проще работать, интегрировать и все такое.

С одной переменной Гауссовское распределение имеет два параметра, sigma и mu, и является функцией одной переменной, обозначим x. Вы также носить нормализации параметр n (что полезно в нескольких приложениях). Параметры нормализации обычно не включено в расчеты, так как вы можете просто прикрепить их обратно в конце (помните, что интеграция является линейным оператором:int(n*f(x), x) = n*int(f(x), x) ). Но мы можем носить его с собой, если хотите; обозначение, которое мне нравится для нормального распределения, тогда

N(x | mu, sigma, n) := (n/(sigma*sqrt(2*pi))) * exp((-(x-mu)^2)/(2*sigma^2))

(прочитайте это как " нормальное распределение x дано sigma, mu и n дана от...") Пока все хорошо; это соответствует функции, которую вы имеете. Обратите внимание, что единственное правда переменная здесь x: остальные три параметра -основные для любого конкретного Гаусса.

теперь для математического факта: доказуемо верно, что все гауссовы кривые имеют одинаковую форму,они просто немного сдвинуты. Таким образом, мы можем работать с N(x|0,1,1), вызванный "стандартным нормальным распределением", и как раз переводит наши результаты назад к генералу кривая Гаусса. Итак, если у вас есть Интеграл N(x|0,1,1), вы можете тривиально вычислить Интеграл любого Гауссова. Этот Интеграл появляется так часто, что имеет специальное название:функции ошибки erf. Из-за некоторых старых конвенций, это не ровно erf; есть пара аддитивных и мультипликативных факторов, которые также переносятся.

если Phi(z) = integral(N(x|0,1,1), -inf, z), то есть Phi(z) Интеграл стандартного нормального распределения от минус бесконечность до z, то это правда по определению функции ошибки, что

Phi(z) = 0.5 + 0.5 * erf(z / sqrt(2)).

аналогично, если Phi(z | mu, sigma, n) = integral( N(x|sigma, mu, n), -inf, z), то есть Phi(z | mu, sigma, n) является интегралом от нормального распределения заданных параметров mu, sigma и n от минус бесконечности до z, то это правда по определению функции ошибки, что

Phi(z | mu, sigma, n) = (n/2) * (1 + erf((x - mu) / (sigma * sqrt(2)))).

посмотри статья Википедии о нормальном CDF если вы хотите больше деталей или доказательств этого факта.

хорошо, это должно быть достаточным фоновым объяснением. Вернемся к вашему (отредактированному) сообщению. Вы говорите: "erf (z) в scipy.special потребует от меня точно определить, что такое t изначально". Я понятия не имею, что вы имеете в виду; где t (время?) войти в это вообще? Надеюсь, приведенное выше объяснение немного демистифицировало функцию ошибки, и теперь понятно, почему функция ошибки является правильной функцией для работа.

ваш код Python в порядке, но я бы предпочел закрытие над лямбда:

def make_gauss(N, sigma, mu):
    k = N / (sigma * math.sqrt(2*math.pi))
    s = -1.0 / (2 * sigma * sigma)
    def f(x):
        return k * math.exp(s * (x - mu)*(x - mu))
    return f

использование замыкания позволяет предварительно вычислить константы k и s, поэтому возвращаемой функции нужно будет делать меньше работы каждый раз, когда она вызывается (что может быть важно, если вы интегрируете ее, что означает, что она будет вызываться много раз). Кроме того, я избегал любого использования оператора возведения в степень **, что медленнее, чем просто писать квадрат, и поднял деление из внутреннего цикла и заменил его на умножение. Я вообще не смотрел на их реализацию в Python, но с моего последнего времени, настраивая внутренний цикл для чистой скорости с помощью сборки raw x87, я, кажется, помню, что добавляет, вычитает или умножает около 4 циклов процессора каждый, делит около 36 и экспоненциальность около 200. Это было пару лет назад, так что возьмите эти цифры с солью; тем не менее, это иллюстрирует их относительную сложность. А также, вычисляя exp(x) the путь грубой силы-очень плохая идея; есть трюки, которые вы можете взять при написании хорошей реализации exp(x) это делает его значительно быстрее и точнее, чем общий a**b возведение в степень стиля.

я никогда не использовал numpy версию констант pi и e; я всегда придерживался версий простого старого математического модуля. Не знаю, почему ты предпочитаешь любой из них.

я не уверен, что вы собираетесь с quad() звонок. quad(gen_gauss, -inf, inf, (10,2,0)) должны интегрируйте перенормированный Гауссов от минус бесконечности до плюс бесконечности и всегда должен выплюнуть 10 (ваш коэффициент нормализации), так как Гауссов интегрируется в 1 по реальной линии. Любой ответ далеко от 10 (я бы не ожидал ровно 10 с quad() это только приближение, в конце концов) означает, что что-то где-то напортачило... трудно сказать, что испорчено, не зная фактического возвращаемого значения и, возможно, внутренней работы quad().

надеюсь что демистифицировал некоторые из путаницы, и объяснил, почему функция ошибки правильный ответ на вашу проблему, а также как все это сделать сами, если вам интересно. Если мое объяснение не понятно, я предлагаю взглянуть в Википедии, если у вас остались вопросы, не стесняйтесь спрашивать.


scipy поставляется с "функцией ошибки", он же гауссовский Интеграл:

import scipy.special
help(scipy.special.erf)

Я предполагаю, что вы работаете с многомерными Гауссами; если это так, у SciPy уже есть функция, которую вы ищете: она называется MVNDIST ("многомерное нормальное распределение). Документация SciPy, как всегда, ужасна, поэтому я даже не могу найти, где похоронена функция, но он где-то там. Документация-это худшая часть SciPy, и в прошлом она разочаровывала меня до бесконечности.

Однопеременные Gaussians просто используют старую добрую функцию ошибки, какие многие реализации доступны.

Что касается атаки на проблему в целом, да, как упоминает Джеймс Томпсон, вы просто хотите написать свою собственную гауссовскую функцию распределения и передать ее quad(). Однако, если вы можете избежать обобщенной интеграции, это хорошая идея-специализированные методы интеграции для конкретной функции (например, mvndist) будут намного быстрее, чем стандартная многомерная интеграция Монте-Карло, которая может быть очень медленной для высокой точности.


гауссово распределение также называется нормальным распределением. Функция cdf в модуле scipy norm делает то, что вы хотите.

from scipy.stats import norm
print norm.cdf(0.0)
>>>0.5

http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.norm.html#scipy.stats.norm


почему бы просто не всегда делать вашу интеграцию от-бесконечности до + бесконечности, чтобы вы всегда знали ответ? (шутка!)

Я предполагаю, что единственная причина, по которой в SciPy еще нет консервированной гауссовой функции, заключается в том, что это тривиальная функция для записи. Ваше предложение о написании собственной функции и передаче ее quad для интеграции звучит отлично. Он использует принятый инструмент SciPy для этого, это минимальное усилие кода для вас, и он очень удобочитаем для других люди, даже если они никогда не видели составляющей.

что именно вы подразумеваете под интегратором фиксированной ширины? Вы имеете в виду использование другого алгоритма, чем тот, который использует QUADPACK?

Edit: для полноты, вот что я бы попробовал для Гаусса со средним значением 0 и стандартным отклонением 1 от 0 до + бесконечности:

from scipy.integrate import quad
from math import pi, exp
mean = 0
sd   = 1
quad(lambda x: 1 / ( sd * ( 2 * pi ) ** 0.5 ) * exp( x ** 2 / (-2 * sd ** 2) ), 0, inf )

это немного уродливо, потому что функция Гаусса немного длинная, но все же довольно тривиальная для записи.