如何为任意分布生成相关数据

How to generate correlated data for arbitrary distributions

问题:

我希望能够从多个分布中对由给定相关矩阵定义的相关数据进行采样。关于分布,我想尽可能少地做假设。它尤其适用于非连续分布和类似的连续分布。 (当然可以假定均值 0 和方差 1) 相关性不需要是精确的相关性,具有合适的近似值就足够了。 我不知道联合分布,但如果可以在给定单一分布和相关矩阵的情况下创建联合分布,这应该可以解决我的问题。

我尝试了什么:

我尝试首先从分布中提取,并尝试找到越来越匹配所需相关性的重排。我的出发点是当前最好的解决方案,然后我对样本的一小部分进行了混洗,然后检查结果是否更匹配。如果是这样,这就成了我新的起点。但结果却相当令人失望。所以可能有更好的方法来做到这一点。

我的代码(未提供所需的结果):

import numpy as np
import scipy 
import random

#the desired correlation matrix
des_cov1=np.array([[1,0.5,-0.2],[0.5,1,0.2],[-0.2,0.2,1]])

#data drawn from 3 different distributions
data1=np.array([np.random.normal(loc=-3.0, scale=1.0, size=1000), np.random.binomial(1000, 0.2, size=1000),np.random.normal(loc=5.0, scale=7.0, size=1000)])

#A helper function foudn on Whosebug to shuffle part of the data
#shuffle a given percentage of the data (on average)
def pashuffle(data, perc=10):
    for index, letter in enumerate(data):
        if random.randrange(0, 100) < perc/2:
            new_index = random.randrange(0, len(data))
            data[index], data[new_index] = data[new_index], data[index]
    return data

#Try to correlate the data accoridng to a desired covariation matrix
#shuffle a given percentage of the data (on average)
def pashuffle(data, perc=10):
    for index, letter in enumerate(data):
        if random.randrange(0, 100) < perc/2:
            new_index = random.randrange(0, len(data))
            data[index], data[new_index] = data[new_index], data[index]
    return data

#Try to correlate the data accoridng to a desired covariation matrix
def correlate_data_sets(data, des_cov, nr_trials=100, perc=10, decrease_factor=0.999):
    data=np.array(data, dtype=float)
    #save means and variance to reconsturct the old data
    means= np.mean(data,axis=1)
    standard_deviation= scipy.stats.tstd(data, axis=1)
    for i in range(len(data)):
        bd_mean=means[i]
        bd_std=standard_deviation[i]
        #normalize data
        data[i]=[(j-bd_mean)/bd_std for j in data[i]]
    
    best_data=np.array(data)
    #initialize best deviation with a value that will for sure be beat
    best_deviation=4*len(des_cov)*len(des_cov)
    #try to find a good solution by starting with the current best
    for i in range(nr_trials):
        #shuffle a part of the data, to get a posibly better solution
        for j in range(len(data)-1):
            data[j]=pashuffle(best_data[j],perc)
        #calulate the squared deviation
        deviation_matrix=np.cov(data)-des_cov
        squared_cov_deviation=sum(sum(deviation_matrix*deviation_matrix))
        #replace current data arrangement if new arragnement is better
        if squared_cov_deviation < best_deviation:
            best_data=data.copy()
            best_deviation=squared_cov_deviation
        #decrease the amount of data to shuffle
        perc*=decrease_factor
    
    correlations=np.cov(best_data)
    #transform data back
    for i in range(len(data)):
        bd_mean=means[i]
        bd_std=standard_deviation[i]
        best_data[i]=[(j*bd_std+bd_mean) for j in best_data[i]]
    print(correlations)

    return(best_data)

#Apply the function on the data
correlate_data_sets(data=data1, des_cov=des_cov1)

这是一个棘手的问题,但您可以通过 (1) 找到您需要的斯皮尔曼等级相关性,(2) 从具有这种成对相关性的均匀分布生成值,然后 (3) 使用此样本中的值作为任意分布中的等级,以从这些分布中生成值。请参阅我在 http://ee.hawaii.edu/~mfripp/papers/Fripp_2011_Wind_Reserves.pdf(第 2.2 节)中使用此技术的论文。

如果您需要的不仅仅是正确的成对等级相关性,您可以通过生成均匀分布的元组(每个随机变量一个元素),然后使用某种技术将它们推入正确的相关性来实现结构,然后将它们用作任意分布的等级。那是在 copula 方法领域。