SciPy: 半圆上的 von Mises 分布?

SciPy: von Mises distribution on a half circle?

我正在尝试找出最好的方法来定义包裹在半圆上的 von-Mises 分布(我用它来绘制不同浓度的无方向线)。我目前正在使用 SciPy 的 vonmises.rvs()。本质上,我希望能够输入 pi/2 的平均方向,并将分布截断为不超过 pi/2 任一侧。

我可以使用截断的正态分布,但我会失去 von-mises 的环绕(假设我想要平均方向为 0)

我在研究映射纤维方向的研究论文中看到过这样做,但我不知道如何实现它(在 python 中)。我有点不知道从哪里开始。

如果我的 von Mesis 定义为(来自 numpy.vonmises):

np.exp(kappa*np.cos(x-mu))/(2*np.pi*i0(kappa))

与:

mu, kappa = 0, 4.0

x = np.linspace(-np.pi, np.pi, num=51)

我该如何更改它以使用环绕半圆来代替?

有这方面经验的人可以提供一些指导吗?

您可以通过 numpy 的过滤(theta=theta[(theta>=0)&(theta<=np.pi)],缩短样本数组)丢弃超出所需范围的值。因此,您可以先增加生成样本的数量,然后进行过滤,然后获取所需大小的子数组。

或者您可以 add/subtract pi 将它们全部放入该范围内(通过 theta = np.where(theta < 0, theta + np.pi, np.where(theta > np.pi, theta - np.pi, theta)))。正如@SeverinPappadeux 所指出的,这样会改变分布,可能是不需要的。

import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import numpy as np
from scipy.stats import vonmises

mu = np.pi / 2
kappa = 4

orig_theta = vonmises.rvs(kappa, loc=mu, size=(10000))
fig, axes = plt.subplots(ncols=2, sharex=True, sharey=True, figsize=(12, 4))
for ax in axes:
    theta = orig_theta.copy()
    if ax == axes[0]:
        ax.set_title(f"$Von Mises, \mu={mu:.2f}, \kappa={kappa}$")
    else:
        theta = theta[(theta >= 0) & (theta <= np.pi)]
        print(len(theta))
        ax.set_title(f"$Von Mises, angles\ filtered\ ({100 * len(theta) / (len(orig_theta)):.2f}\ \%)$")
    segs = np.zeros((len(theta), 2, 2))
    segs[:, 1, 0] = np.cos(theta)
    segs[:, 1, 1] = np.sin(theta)
    line_segments = LineCollection(segs, linewidths=.1, colors='blue', alpha=0.5)
    ax.add_collection(line_segments)
    ax.autoscale()
    ax.set_aspect('equal')
plt.show()

直接数值逆 CDF 采样很有用,它对于有界域的分布应该很有用。这是代码示例,构建 PDF 和 CDF 表并使用逆 CDF 方法进行采样。当然可以优化和矢量化

代码,Python3.8,x64Windows10

import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate

def PDF(x, μ, κ):
    return np.exp(κ*np.cos(x - μ))

N = 201

μ = np.pi/2.0
κ = 4.0

xlo = μ - np.pi/2.0
xhi = μ + np.pi/2.0

# PDF normaliztion

I = integrate.quad(lambda x: PDF(x, μ, κ), xlo, xhi)
print(I)
I = I[0]

x = np.linspace(xlo, xhi, N, dtype=np.float64)
step = (xhi-xlo)/(N-1)

p = PDF(x, μ, κ)/I # PDF table

# making CDF table
c = np.zeros(N, dtype=np.float64)

for k in range(1, N):
    c[k] = integrate.quad(lambda x: PDF(x, μ, κ), xlo, x[k])[0] / I

c[N-1] = 1.0 # so random() in [0...1) range would work right

#%%
# sampling from tabular CDF via insverse CDF method

def InvCDFsample(c, x, gen):
    r = gen.random()
    i = np.searchsorted(c, r, side='right')
    q = (r - c[i-1]) / (c[i] - c[i-1])
    return (1.0 - q) * x[i-1] + q * x[i]

# sampling test
RNG = np.random.default_rng()

s = np.empty(20000)

for k in range(0, len(s)):
    s[k] = InvCDFsample(c, x, RNG)

# plotting PDF, CDF and sampling density
plt.plot(x, p, 'b^') # PDF
plt.plot(x, c, 'r.') # CDF
n, bins, patches = plt.hist(s, x, density = True, color ='green', alpha = 0.7)
plt.show()

以及带有 PDF、CDF 和采样直方图的图表