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

时间:2021-01-24 23:42:18

标签: python

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

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

4 个答案:

答案 0 :(得分:1)

您是否考虑过使用 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 可能有点矫枉过正,有更简单的方法可以放大样本量,在这种情况下,我很想了解它们。

答案 1 :(得分:1)

更新

我刚刚注意到您提到了 np.random.multivariate_normal...它一下子就相当于下面的 gen_like()

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

  1. 您可以将经验分布的均值和协方差与(旋转、缩放、平移)正态相匹配;
  2. 为了更好地匹配更高的时刻,您应该查看连接词。

原答案

由于您只对匹配两个第一矩(均值、方差)感兴趣,您可以使用简单的 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)

答案 2 :(得分:0)

答案 3 :(得分:0)

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

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

相关问题