python - resueltos - jupyter notebook tutorial español
¿La mejor manera de escribir una función de Python que integre un gaussiano? (5)
Al intentar usar el método cuádruple de scipy para integrar un gaussiano (digamos que hay un método gaussiano llamado gauss), tuve problemas para pasar los parámetros necesarios a gauss y dejar que quad hiciera la integración sobre la variable correcta. ¿Alguien tiene un buen ejemplo de cómo usar quad w / a una función multidimensional?
Pero esto me llevó a una gran pregunta sobre la mejor manera de integrar un gaussiano en general. No encontré una integración gaussiana en scipy (para mi sorpresa). Mi plan era escribir una función gaussiana simple y pasarla a quad (o tal vez ahora a un integrador de ancho fijo). ¿Qué harías?
Editar: Ancho fijo significa algo así como trapz que usa un dx fijo para calcular áreas bajo una curva.
Lo que he llegado hasta ahora es un método make___gauss que devuelve una función lambda que luego puede entrar en quad. De esta manera puedo hacer una función normal con el promedio y la varianza que necesito antes de integrar.
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)
Cuando traté de pasar una función general gaussiana (que debe llamarse con x, N, mu y sigma) y completar algunos de los valores usando quad como
quad(gen_gauss, -inf, inf, (10,2,0))
los parámetros 10, 2 y 0 NO necesariamente coincidían con N = 10, sigma = 2, mu = 0, lo que provocó la definición más extendida.
El erf (z) en scipy.special requeriría que defina exactamente qué t es inicialmente, pero es bueno saber que está allí.
¿Por qué no siempre haces tu integración de infinito a infinito para que siempre sepas la respuesta? (¡bromas!)
Mi suposición es que la única razón por la que ya no hay una función gaussiana enlatada en SciPy es que es una función trivial para escribir. Su sugerencia acerca de escribir su propia función y pasarla a quad para integrar sonidos excelentes. Utiliza la herramienta SciPy aceptada para hacer esto, es un esfuerzo de código mínimo para ti, y es muy legible para otras personas, incluso si nunca han visto SciPy.
¿Qué quiere decir exactamente con un integrador de ancho fijo? ¿Quiere decir usar un algoritmo diferente de lo que QUADPACK está usando?
Editar: para completar, aquí hay algo así como lo que probaría para un gaussiano con la media de 0 y la desviación estándar de 1 de 0 a + infinito:
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 )
Eso es un poco feo porque la función de Gauss es un poco larga, pero aún bastante trivial de escribir.
Supongo que estás manejando Gaussianos multivariantes; si es así, SciPy ya tiene la función que estás buscando: se llama MVNDIST ("MultiVariate Normal DisTribution"). La documentación de SciPy es, como siempre, terrible, así que ni siquiera puedo encontrar dónde está enterrada la función, pero está en La documentación es fácilmente la peor parte de SciPy, y me ha frustrado sin fin en el pasado.
Los gaussianos de una sola variable solo usan la función de error anterior, de la que están disponibles muchas implementaciones.
En cuanto a atacar el problema en general, sí, como menciona James Thompson, solo quiere escribir su propia función de distribución gaussiana y alimentarla a quad (). Sin embargo, si puede evitar la integración generalizada, es una buena idea hacerlo: las técnicas de integración especializadas para una función en particular (como MVNDIST usa) van a ser mucho más rápidas que una integración multidimensional Monte Carlo estándar, que puede ser extremadamente lenta. para una alta precisión.
naves astillas con la "función de error", también conocida como integral gaussiana:
import scipy.special
help(scipy.special.erf)
De acuerdo, pareces estar bastante confundido sobre varias cosas. Comencemos por el principio: mencionaste una "función multidimensional", pero luego pasas a discutir la curva gaussiana usual de una variable. Esta no es una función multidimensional: cuando la integras, solo integras una variable (x). Es importante hacer la distinción, porque hay un monstruo llamado "distribución gaussiana multivariante" que es una verdadera función multidimensional y, si está integrada, requiere integrar más de dos o más variables (que usa la costosa técnica de Monte Carlo que mencioné anteriormente). Pero parece que solo estás hablando del Gaussiano regular de una variable, que es mucho más fácil de trabajar, integrar y todo eso.
La distribución gaussiana de una variable tiene dos parámetros, sigma
y mu
, y es una función de una sola variable que denotará x
. También parece llevar un parámetro de normalización n
(que es útil en un par de aplicaciones). Los parámetros de normalización generalmente no se incluyen en los cálculos, ya que puede volverlos a conectar al final (recuerde que la integración es un operador lineal: int(n*f(x), x) = n*int(f(x), x)
). Pero podemos llevarlo a cabo si lo desea; la notación que me gusta para una distribución normal es entonces
N(x | mu, sigma, n) := (n/(sigma*sqrt(2*pi))) * exp((-(x-mu)^2)/(2*sigma^2))
(léase que como "la distribución normal de x
dada sigma
, mu
, n
está dada por ...") Hasta ahora, muy bien; esto coincide con la función que tienes. Tenga en cuenta que la única variable verdadera aquí es x
: los otros tres parámetros se fijan para cualquier gaussiano en particular.
Ahora, para un hecho matemático: es probable que todas las curvas gaussianas tengan la misma forma, simplemente se desplazan un poco. Entonces podemos trabajar con N(x|0,1,1)
, llamada "distribución normal estándar", y simplemente traducimos nuestros resultados a la curva general de Gauss. Entonces, si tienes la integral de N(x|0,1,1)
, puedes calcular trivialmente la integral de cualquier gaussiano. Esta integral aparece con tanta frecuencia que tiene un nombre especial: la función de error erf
. Debido a algunas viejas convenciones, no es exactamente erf
; hay un par de factores aditivos y multiplicativos que también se transportan.
Si Phi(z) = integral(N(x|0,1,1), -inf, z)
; es decir, Phi(z)
es la integral de la distribución normal estándar desde menos infinito hasta z
, entonces es cierto por la definición de la función de error que
Phi(z) = 0.5 + 0.5 * erf(z / sqrt(2))
.
Del mismo modo, si Phi(z | mu, sigma, n) = integral( N(x|sigma, mu, n), -inf, z)
; es decir, Phi(z | mu, sigma, n)
es la integral de la distribución normal dados los parámetros mu
, sigma
n
desde menos infinito hasta z
, entonces es verdadero por la definición de la función de error que
Phi(z | mu, sigma, n) = (n/2) * (1 + erf((x - mu) / (sigma * sqrt(2))))
.
Eche un vistazo al artículo de Wikipedia sobre el CDF normal si desea más detalles o una prueba de este hecho.
De acuerdo, esa debería ser suficiente explicación de fondo. De vuelta a su publicación (editada). Usted dice "El erf (z) en scipy.special requeriría que defina exactamente qué t es inicialmente". No tengo idea de lo que quieres decir con esto; ¿dónde t
(tiempo?) entra en esto? Afortunadamente, la explicación anterior ha desmitificado un poco la función de error y ahora está más claro por qué la función de error es la función correcta para el trabajo.
Su código Python está bien, pero yo preferiría un cierre sobre un lambda:
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
Usar un cierre permite la precomputación de las constantes k
, por lo que la función devuelta necesitará hacer menos trabajo cada vez que se llame (lo que puede ser importante si la está integrando, lo que significa que se la llamará muchas veces). Además, he evitado el uso del operador de exponenciación **
, que es más lento que simplemente escribir la cuadratura, y he levantado la división del bucle interno y la he reemplazado por una multiplicación. No he visto en absoluto su implementación en Python, pero desde la última vez que sintonicé un bucle interno para velocidad pura usando el ensamblaje x87 sin procesar, me parece recordar que agrega, resta o multiplica toma alrededor de 4 ciclos de CPU cada uno, se divide sobre 36, y exponenciación de aproximadamente 200. Eso fue hace un par de años, así que tome esos números con un grano de sal; aún así, ilustra su complejidad relativa. Además, calcular exp(x)
la fuerza bruta es una muy mala idea; hay trucos que puede tomar al escribir una buena implementación de exp(x)
que lo hacen significativamente más rápido y más preciso que una exponenciación de estilo a**b
general.
Nunca he usado la versión numpy de las constantes pi y e; Siempre me he quedado con las versiones simples del módulo de matemáticas. No sé por qué podrías preferir cualquiera de los dos.
No estoy seguro de a qué te refieres con la llamada quad()
. quad(gen_gauss, -inf, inf, (10,2,0))
debe integrar un gaussiano renormalizado desde menos infinito hasta más infinito, y siempre debe escupir 10 (su factor de normalización), ya que el gaussiano se integra a 1 sobre el linea real Cualquier respuesta lejos de 10 (no esperaría exactamente 10 ya que quad()
es solo una aproximación, después de todo) significa que algo está arruinado en algún lado ... es difícil decir lo que está mal sin saber el valor de retorno real y posiblemente el interno funcionamiento de quad()
.
Afortunadamente, eso ha desmitificado parte de la confusión y ha explicado por qué la función de error es la respuesta correcta a su problema, y cómo hacerlo todo usted mismo si tiene curiosidad. Si alguna de mis explicaciones no estaba clara, sugiero echar un vistazo rápido a Wikipedia primero; si todavía tiene preguntas, no dude en preguntar.
La distribución gaussiana también se llama distribución normal. La función cdf en el módulo de norma scipy hace lo que quiere.
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