如何在Python中从非正态多变量分布生成样本?

  • 本文关键字:分布 变量 样本 Python python
  • 更新时间 :
  • 英文 :


我有一个包含10个变量和100行的输入数据框df_input。这些数据不是正态分布的。我想生成一个具有10个变量和10,000行的输出数据框,使新数据框的协方差矩阵和平均值与原始数据框的协方差矩阵和平均值相同。输出变量不应是正态分布,而应具有与输入变量相似的分布。那就是:Cov(df_output) = Cov(df_input)和Mean (df_output) = Mean (df_input)有没有一个Python函数可以做到这一点?

注意:np.random.multivariate_normal(mean_input,Cov_input,10000)几乎做到了这一点,但输出变量是正态分布的,而我需要它们具有与输入相同(或类似)的分布。

更新

我刚刚注意到你提到的np.random.multivariate_normal…它的速度相当于下面的gen_like()!

我把它留在这里,以帮助人们理解这个机制,但总结一下:

  1. 您可以将经验分布的平均值和协方差与(旋转,缩放,平移)正态相匹配;
  2. 为了更好地匹配更高的矩,你应该看一下copula。

原始回答

由于您只对匹配两个前矩(均值,方差)感兴趣,因此您可以使用简单的PCA来获得初始数据的合适模型。请注意,新生成的数据将是一个正椭球,经过旋转、缩放和平移,以匹配初始数据的经验平均值和协方差。

如果你想更复杂的"复制",那么你应该看看我在评论中说的Copula。

所以,只适用于前两个矩,假设输入数据为d0:

from sklearn.decomposition import PCA
def gen_like(d0, n):
pca = PCA(n_components=d0.shape[1]).fit(d0)
z0 = pca.transform(d0)  # z0 is centered and uncorrelated (cov is diagonal)
z1 = np.random.normal(size=(n, d0.shape[1])) * np.std(z0, 0)
# project back to input space
d1 = pca.inverse_transform(z1)
return d1

的例子:

# generate some random data
# arbitrary transformation matrix
F = np.array([
[1, 2, 3],
[2, 1, 4],
[5, 1, 3],
])
d0 = np.random.normal(2, 4, size=(10000, 3)) @ F.T
np.mean(d0, 0)
# ex: array([12.12791066, 14.10333273, 17.95212292])
np.cov(d0.T)
# ex: array([[225.09691912, 257.39878551, 259.40288019],
#            [257.39878551, 338.34087242, 373.4773562 ],
#            [259.40288019, 373.4773562 , 566.29288861]])
# try to match mean, variance of d0
d1 = gen_like(d0, 10000)
np.allclose(np.mean(d0, 0), np.mean(d1, 0), rtol=0.1)
# often True (but not guaranteed)
np.allclose(np.cov(d0.T), np.cov(d1.T), rtol=0.1)
# often True (but not guaranteed)

有趣的是,你可以在一个圆洞里装一个方形的钉子(即,证明实际上只有平均值,方差是匹配的,而不是更高的矩):

d0 = np.random.uniform(5, 10, size=(1000, 3)) @ F.T
d1 = gen_like(d0, 10000)
np.allclose(np.mean(d0, 0), np.mean(d1, 0), rtol=0.1)
# often True (but not guaranteed)
np.allclose(np.cov(d0.T), np.cov(d1.T), rtol=0.1)
# often True (but not guaranteed)

你试过看NumPy文档吗?:https://numpy.org/doc/stable/reference/random/generated/numpy.random.multivariate_normal.html

你考虑过使用GAN(生成对抗网络)吗?与使用预定义函数相比,这需要更多的努力,但从本质上讲,它确实可以完成您希望完成的任务。原文如下:https://arxiv.org/abs/1406.2661

有许多PyTorch/Tensorflow代码,你可以下载并适合你的目的,例如:https://github.com/eriklindernoren/PyTorch-GAN

这里还有一篇博客文章,我发现它对gan的介绍很有帮助。https://medium.com/ai-society/gans-from-scratch-1-a-deep-introduction-with-code-in-pytorch-and-tensorflow-cb03cdcdba0f

对于这个问题,GAN可能是多余的,有更简单的方法来扩大样本量,在这种情况下,我有兴趣了解它们。

最好的方法确实是使用copula,正如许多人建议的那样。下面的链接提供了一个简单的描述,它也提供了一个简单的python代码。该方法保留了协方差,同时增加了数据。它允许推广到非对称或非正态分布。谢谢你的帮助。

https://sdv.dev/Copulas/tutorials/03_Multivariate_Distributions.html。

最新更新