normal probability density 函数 f 由下式给出
给定f 和x,我们希望求解?。请问sympy能不能解方程:
import sympy as sy
from sympy.abc import x, y, sigma
expr = (1/(sy.sqrt(2*sy.pi)*sigma) * sy.exp(-x**2/(2*sigma**2))) - y
ans = sy.solve(expr, sigma)[0]
print(ans)
# sqrt(2)*exp(LambertW(-2*pi*x**2*y**2)/2)/(2*sqrt(pi)*y)
因此,就LambertW function、W 而言,似乎存在一个封闭形式的解决方案,它满足
z = W(z) * exp(W(z))
对于所有复值z。
我们也可以使用 sympy 来找到给定 x 和 y 的数值结果,但是
也许做数值工作会更快
scipy.special.lambertw:
import numpy as np
import scipy.special as special
def sigma_func(x, y):
results = set([np.real_if_close(
np.sqrt(2)*np.exp(special.lambertw(-2*np.pi*x**2*y**2, k=k)/2)
/(2*np.sqrt(np.pi)*y)).item() for k in (0, -1)])
results = [s for s in results if np.isreal(s)]
return results
一般来说,LambertW 函数返回复数,但我们只
对sigma 的实值解决方案感兴趣。 Per the
docs,
special.lambertw 有两个部分真实的分支,分别是 k=0 和 k=1。所以
上面的代码检查返回的值(对于这两个分支)是否是真实的,并且
如果存在,则返回任何实际解决方案的列表。如果没有真正的解决方案,
然后返回一个空列表。如果 pdf 值 y 不是
对于 sigma 的任何实际值(对于给定的 x 值)达到。
你可以这样使用它:
x = 30.0
loc = 40.0
y = 0.02
s = sigma_func(loc-x, y)
print(s)
# [16.65817044316178, 6.830458938511113]
import scipy.stats as stats
for si in s:
assert np.allclose(stats.norm.pdf(x, loc=loc, scale=si), y)
在您给出的示例中,使用y = 0.025,sigma 没有解决方案:
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
x = 30.0
loc = 40.0
y = 0.025
s = np.linspace(5, 20, 100)
plt.plot(s, stats.norm.pdf(x, loc=loc, scale=s))
plt.hlines(y, 4, 20, color='red') # the horizontal line y = 0.025
plt.ylabel('pdf')
plt.xlabel('sigma')
plt.show()
所以sigma_func(40-30, 0.025) 返回一个空列表:
In [93]: sigma_func(40-30, 0.025)
Out [93]: []
上面的情节是典型的,当y太大时,零
解决方案,在曲线的最大值处(我们称之为y_max)有一个
解决方案
In [199]: y_max = np.nextafter(np.sqrt(1/(np.exp(1)*2*np.pi*(10)**2)), -np.inf)
In [200]: y_max
Out[200]: 0.024197072451914336
In [201]: sigma_func(40-30, y_max)
Out[201]: [9.9999999776424]
对于小于 y_max 的 y,有两种解决方案。