对于模拟研究,我必须生成随机变量,这些变量显示与现有变量的预定义(填充)相关性。
我研究了这些R
软件包copula
,CDVine
它们可以生成具有给定依赖关系结构的随机多变量分布。但是,不可能将结果变量之一固定为现有变量。
任何想法和现有功能的链接表示赞赏!
结论: 提出了两个有效的答案,有不同的解决方案:
[@ttnphns的补充:我可以自由地将问题标题从单个固定变量的情况扩展到任意数量的固定变量;即如何生成具有预定义正确性和一些固定的现有变量的变量]
对于模拟研究,我必须生成随机变量,这些变量显示与现有变量的预定义(填充)相关性。
我研究了这些R
软件包copula
,CDVine
它们可以生成具有给定依赖关系结构的随机多变量分布。但是,不可能将结果变量之一固定为现有变量。
任何想法和现有功能的链接表示赞赏!
结论: 提出了两个有效的答案,有不同的解决方案:
[@ttnphns的补充:我可以自由地将问题标题从单个固定变量的情况扩展到任意数量的固定变量;即如何生成具有预定义正确性和一些固定的现有变量的变量]
Answers:
这是另一个:对于均值为0的向量,它们的相关性等于其角度的余弦值。因此,找到具有恰好具有所需相关度r的向量一种方法,该向量与角度θ相对应:
这是代码:
n <- 20 # length of vector
rho <- 0.6 # desired correlation = cos(angle)
theta <- acos(rho) # corresponding angle
x1 <- rnorm(n, 1, 1) # fixed given data
x2 <- rnorm(n, 2, 0.5) # new random data
X <- cbind(x1, x2) # matrix
Xctr <- scale(X, center=TRUE, scale=FALSE) # centered columns (mean 0)
Id <- diag(n) # identity matrix
Q <- qr.Q(qr(Xctr[ , 1, drop=FALSE])) # QR-decomposition, just matrix Q
P <- tcrossprod(Q) # = Q Q' # projection onto space defined by x1
x2o <- (Id-P) %*% Xctr[ , 2] # x2ctr made orthogonal to x1ctr
Xc2 <- cbind(Xctr[ , 1], x2o) # bind to matrix
Y <- Xc2 %*% diag(1/sqrt(colSums(Xc2^2))) # scale columns to length 1
x <- Y[ , 2] + (1 / tan(theta)) * Y[ , 1] # final new vector
cor(x1, x) # check correlation = rho
对于正交投影,我使用Q R分解来提高数值稳定性,因为从那时起,简单地说P = Q Q '。
P <- X %*% solve(t(X) %*% X) %*% t(X)
不会产生r = 0.6,所以这不是解决方法。我还是很困惑。(我很乐意Q <- qr.Q(qr(Xctr[ , 1, drop=FALSE]))
在SPSS中模仿您的表情,但不知道如何。)
Xctr[ , 1] %*% solve(t(Xctr[ , 1]) %*% Xctr[ , 1]) %*% t(Xctr[ , 1])
Xctr
rho=1
我认为有必要做这样的事情:if (isTRUE(all.equal(rho, 1))) rho <- 1-10*.Machine$double.eps
,否则我得到的NaN
小号
我将描述最通用的解决方案。以这种普遍的方式解决问题,使我们能够实现非常紧凑的软件实现:仅需短短两行R
代码即可。
选择一个向量,相同长度的ÿ,根据你喜欢的任何分布。 让ÿ ⊥是最小二乘回归残差的X对Y ^:此提取Ÿ从组件X。通过加回的一个合适的多ý到Ý ⊥,我们可以产生具有任何所需的相关性的矢量ρ与ÿ。高达任意加法常数和正乘法常数-您可以以任何方式自由选择-解决方案是
(“ ”代表与标准偏差成比例的任何计算。)
这是工作R
代码。如果不提供,则代码将从多元标准正态分布中提取其值。
complement <- function(y, rho, x) {
if (missing(x)) x <- rnorm(length(y)) # Optional: supply a default if `x` is not given
y.perp <- residuals(lm(x ~ y))
rho * sd(y.perp) * y + y.perp * sd(y) * sqrt(1 - rho^2)
}
为了说明这一点,我生成了具有50个分量的随机并生成了X Y ;ρ与该Y具有各种特定的相关性。他们都具有相同的起始向量创建X = (1 ,2 ,... ,50 )。这是他们的散点图。每个面板底部的“地毯”显示共同的Y向量。
情节之间有惊人的相似之处,不是:-)。
如果您想尝试,这里是产生这些数据和图形的代码。(我不费吹灰之力地自由移动和缩放结果,这很容易操作。)
y <- rnorm(50, sd=10)
x <- 1:50 # Optional
rho <- seq(0, 1, length.out=6) * rep(c(-1,1), 3)
X <- data.frame(z=as.vector(sapply(rho, function(rho) complement(y, rho, x))),
rho=ordered(rep(signif(rho, 2), each=length(y))),
y=rep(y, length(rho)))
library(ggplot2)
ggplot(X, aes(y,z, group=rho)) +
geom_smooth(method="lm", color="Black") +
geom_rug(sides="b") +
geom_point(aes(fill=rho), alpha=1/2, shape=21) +
facet_wrap(~ rho, scales="free")
顺便说一句,这种方法很容易推广到多个:如果在数学上可行,它将找到X Y 1,Y具有规定的相关性与整个集的ÿ我。只需使用普通最小二乘取出所有的效果ÿ我从X和形成所述的一个合适的线性组合ÿ我和残差。(这样做有助于以的对偶为基础,这是通过计算伪逆获得的。后续代码使用Y的SVD 来完成此操作。)
这是中的算法示意图R
,其中作为矩阵的列给出:y
y <- scale(y) # Makes computations simpler
e <- residuals(lm(x ~ y)) # Take out the columns of matrix `y`
y.dual <- with(svd(y), (n-1)*u %*% diag(ifelse(d > 0, 1/d, 0)) %*% t(v))
sigma2 <- c((1 - rho %*% cov(y.dual) %*% rho) / var(e))
return(y.dual %*% rho + sqrt(sigma2)*e)
对于那些想尝试的人,以下是更完整的实现。
complement <- function(y, rho, x) {
#
# Process the arguments.
#
if(!is.matrix(y)) y <- matrix(y, ncol=1)
if (missing(x)) x <- rnorm(n)
d <- ncol(y)
n <- nrow(y)
y <- scale(y) # Makes computations simpler
#
# Remove the effects of `y` on `x`.
#
e <- residuals(lm(x ~ y))
#
# Calculate the coefficient `sigma` of `e` so that the correlation of
# `y` with the linear combination y.dual %*% rho + sigma*e is the desired
# vector.
#
y.dual <- with(svd(y), (n-1)*u %*% diag(ifelse(d > 0, 1/d, 0)) %*% t(v))
sigma2 <- c((1 - rho %*% cov(y.dual) %*% rho) / var(e))
#
# Return this linear combination.
#
if (sigma2 >= 0) {
sigma <- sqrt(sigma2)
z <- y.dual %*% rho + sigma*e
} else {
warning("Correlations are impossible.")
z <- rep(0, n)
}
return(z)
}
#
# Set up the problem.
#
d <- 3 # Number of given variables
n <- 50 # Dimension of all vectors
x <- 1:n # Optionally: specify `x` or draw from any distribution
y <- matrix(rnorm(d*n), ncol=d) # Create `d` original variables in any way
rho <- c(0.5, -0.5, 0) # Specify the correlations
#
# Verify the results.
#
z <- complement(y, rho, x)
cbind('Actual correlations' = cor(cbind(z, y))[1,-1],
'Target correlations' = rho)
#
# Display them.
#
colnames(y) <- paste0("y.", 1:d)
colnames(z) <- "z"
pairs(cbind(z, y))
BTW, this method readily generalizes to more... Just use ordinary least squares... and form a suitable linear combination
x
并且想要生成一个y
与之相关的新矢量,x
但也希望该y
矢量均匀分布。
这是另一种计算方法(该解决方案摘自Enrico Schumann 的论坛帖子)。根据Wolfgang(请参阅评论),这在计算上与ttnphns提出的解决方案相同。
x
# returns a data frame of two variables which correlate with a population correlation of rho
# If desired, one of both variables can be fixed to an existing variable by specifying x
getBiCop <- function(n, rho, mar.fun=rnorm, x = NULL, ...) {
if (!is.null(x)) {X1 <- x} else {X1 <- mar.fun(n, ...)}
if (!is.null(x) & length(x) != n) warning("Variable x does not have the same length as n!")
C <- matrix(rho, nrow = 2, ncol = 2)
diag(C) <- 1
C <- chol(C)
X2 <- mar.fun(n)
X <- cbind(X1,X2)
# induce correlation (does not change X1)
df <- X %*% C
## if desired: check results
#all.equal(X1,X[,1])
#cor(X)
return(df)
}
该函数还可以通过调整参数使用非正态边际分布mar.fun
。但是请注意,固定一个变量似乎只能与正态分布的变量一起使用x
!(可能与Macro的评论有关)。
另请注意,至少在高斯分布和Pearson相关的情况下,删除了原始帖子中的“小校正因子”,因为它似乎会使所得的相关性产生偏差(另请参见注释)。
rho
。
X2 <- mar.fun(n)
以X2 <- mar.fun(n,mean(x),sd(x))
获得X1和X2之间的相关性所需
更新 2017年11月11日。我今天遇到了这个老话题,并决定通过显示最初讨论的迭代拟合算法来扩展我的答案。
Disclamer:我发现这种迭代解决方案不如基于对偶基础的优秀解决方案,它由@whuber在今天的线程中提出。@whuber的解决方案不是迭代的,对我来说更重要的是,它似乎对输入的“ pig”变量的值的影响要比“ my”算法的影响小(如果任务是“更正”,这将是一种资产)现有变量,而不是从头开始生成随机变量)。尽管如此,我还是出于好奇心发布了我的游戏,因为它可以工作(另请参见脚注)。
(分母在迭代中不变,请预先计算)
在步骤7的,然后在步骤8上更正为:
(再次,分母是已知的)
获得的
不必正态分布。
我想做一些编程,所以我接受了@Adam删除的答案,并决定在R中编写一个不错的实现。我专注于使用面向函数的样式(即lapply样式循环)。总体思路是采用两个向量,将其中一个向量随机置换,直到它们之间达到一定的相关性为止。这种方法是蛮力的,但是易于实现。
首先,我们创建一个随机排列输入向量的函数:
randomly_permute = function(vec) vec[sample.int(length(vec))]
randomly_permute(1:100)
[1] 71 34 8 98 3 86 28 37 5 47 88 35 43 100 68 58 67 82
[19] 13 9 61 10 94 29 81 63 14 48 76 6 78 91 74 69 18 12
[37] 1 97 49 66 44 40 65 59 31 54 90 36 41 93 24 11 77 85
[55] 32 79 84 15 89 45 53 22 17 16 92 55 83 42 96 72 21 95
[73] 33 20 87 60 38 7 4 52 27 2 80 99 26 70 50 75 57 19
[91] 73 62 23 25 64 51 30 46 56 39
...并创建一些示例数据
vec1 = runif(100)
vec2 = runif(100)
...编写一个置换输入向量并将其与参考向量相关的函数:
permute_and_correlate = function(vec, reference_vec) {
perm_vec = randomly_permute(vec)
cor_value = cor(perm_vec, reference_vec)
return(list(vec = perm_vec, cor = cor_value))
}
permute_and_correlate(vec2, vec1)
$vec
[1] 0.79072381 0.23440845 0.35554970 0.95114398 0.77785348 0.74418811
[7] 0.47871491 0.55981826 0.08801319 0.35698405 0.52140366 0.73996913
[13] 0.67369873 0.85240338 0.57461506 0.14830718 0.40796732 0.67532970
[19] 0.71901990 0.52031017 0.41357545 0.91780357 0.82437619 0.89799621
[25] 0.07077250 0.12056045 0.46456652 0.21050067 0.30868672 0.55623242
[31] 0.84776853 0.57217746 0.08626022 0.71740151 0.87959539 0.82931652
[37] 0.93903143 0.74439384 0.25931398 0.99006038 0.08939812 0.69356590
[43] 0.29254936 0.02674156 0.77182339 0.30047034 0.91790830 0.45862163
[49] 0.27077191 0.74445997 0.34622648 0.58727094 0.92285322 0.83244284
[55] 0.61397396 0.40616274 0.32203732 0.84003379 0.81109473 0.50573325
[61] 0.86719899 0.45393971 0.19701975 0.63877904 0.11796154 0.26986325
[67] 0.01581969 0.52571331 0.27087693 0.33821824 0.52590383 0.11261002
[73] 0.89840404 0.82685046 0.83349287 0.46724807 0.15345334 0.60854785
[79] 0.78854984 0.95770015 0.89193212 0.18885955 0.34303707 0.87332019
[85] 0.08890968 0.22376395 0.02641979 0.43377516 0.58667068 0.22736077
[91] 0.75948043 0.49734797 0.25235660 0.40125309 0.72147500 0.92423638
[97] 0.27980561 0.71627101 0.07729027 0.05244047
$cor
[1] 0.1037542
...并重复一千次:
n_iterations = lapply(1:1000, function(x) permute_and_correlate(vec2, vec1))
请注意,R的作用域规则可确保vec1
和vec2
在全局环境中找到,并且位于上面使用的匿名函数之外。因此,排列均与我们生成的原始测试数据集有关。
接下来,我们找到最大的相关性:
cor_values = sapply(n_iterations, '[[', 'cor')
n_iterations[[which.max(cor_values)]]
$vec
[1] 0.89799621 0.67532970 0.46456652 0.75948043 0.30868672 0.83244284
[7] 0.86719899 0.55623242 0.63877904 0.73996913 0.71901990 0.85240338
[13] 0.81109473 0.52571331 0.82931652 0.60854785 0.19701975 0.26986325
[19] 0.58667068 0.52140366 0.40796732 0.22736077 0.74445997 0.40125309
[25] 0.89193212 0.52031017 0.92285322 0.91790830 0.91780357 0.49734797
[31] 0.07729027 0.11796154 0.69356590 0.95770015 0.74418811 0.43377516
[37] 0.55981826 0.93903143 0.30047034 0.84776853 0.32203732 0.25235660
[43] 0.79072381 0.58727094 0.99006038 0.01581969 0.41357545 0.52590383
[49] 0.27980561 0.50573325 0.92423638 0.11261002 0.89840404 0.15345334
[55] 0.61397396 0.27077191 0.12056045 0.45862163 0.18885955 0.77785348
[61] 0.23440845 0.05244047 0.25931398 0.57217746 0.35554970 0.34622648
[67] 0.21050067 0.08890968 0.84003379 0.95114398 0.83349287 0.82437619
[73] 0.46724807 0.02641979 0.71740151 0.74439384 0.14830718 0.82685046
[79] 0.33821824 0.71627101 0.77182339 0.72147500 0.08801319 0.08626022
[85] 0.87332019 0.34303707 0.45393971 0.47871491 0.29254936 0.08939812
[91] 0.35698405 0.67369873 0.27087693 0.78854984 0.87959539 0.22376395
[97] 0.02674156 0.07077250 0.57461506 0.40616274
$cor
[1] 0.3166681
...或找到最接近0.2的值:
n_iterations[[which.min(abs(cor_values - 0.2))]]
$vec
[1] 0.02641979 0.49734797 0.32203732 0.95770015 0.82931652 0.52571331
[7] 0.25931398 0.30047034 0.55981826 0.08801319 0.29254936 0.23440845
[13] 0.12056045 0.89799621 0.57461506 0.99006038 0.27077191 0.08626022
[19] 0.14830718 0.45393971 0.22376395 0.89840404 0.08890968 0.15345334
[25] 0.87332019 0.92285322 0.50573325 0.40796732 0.91780357 0.57217746
[31] 0.52590383 0.84003379 0.52031017 0.67532970 0.83244284 0.95114398
[37] 0.81109473 0.35554970 0.92423638 0.83349287 0.34622648 0.18885955
[43] 0.61397396 0.89193212 0.74445997 0.46724807 0.72147500 0.33821824
[49] 0.71740151 0.75948043 0.52140366 0.69356590 0.41357545 0.21050067
[55] 0.87959539 0.11796154 0.73996913 0.30868672 0.47871491 0.63877904
[61] 0.22736077 0.40125309 0.02674156 0.26986325 0.43377516 0.07077250
[67] 0.79072381 0.08939812 0.86719899 0.55623242 0.60854785 0.71627101
[73] 0.40616274 0.35698405 0.67369873 0.82437619 0.27980561 0.77182339
[79] 0.19701975 0.82685046 0.74418811 0.58667068 0.93903143 0.74439384
[85] 0.46456652 0.85240338 0.34303707 0.45862163 0.91790830 0.84776853
[91] 0.78854984 0.05244047 0.58727094 0.77785348 0.01581969 0.27087693
[97] 0.07729027 0.71901990 0.25235660 0.11261002
$cor
[1] 0.2000199
为了获得更高的相关性,您需要增加迭代次数。
解:
Python代码:
import numpy as np
import math
from scipy.linalg import toeplitz, cholesky
from statsmodels.stats.moment_helpers import cov2corr
# create the large correlation matrix R
p = 4
h = 2/p
v = np.linspace(1,-1+h,p)
R = cov2corr(toeplitz(v))
# create the first variable
T = 1000;
y = np.random.randn(T)
# generate p-1 correlated randoms
X = np.random.randn(T,p)
X[:,0] = y
C = cholesky(R)
Y = np.matmul(X,C)
# check that Y didn't change
print(np.max(np.abs(Y[:,0]-y)))
# check the correlation matrix
print(R)
print(np.corrcoef(np.transpose(Y)))
测试输出:
0.0
[[ 1. 0.5 0. -0.5]
[ 0.5 1. 0.5 0. ]
[ 0. 0.5 1. 0.5]
[-0.5 0. 0.5 1. ]]
[[ 1. 0.50261766 0.02553882 -0.46259665]
[ 0.50261766 1. 0.51162821 0.05748082]
[ 0.02553882 0.51162821 1. 0.51403266]
[-0.46259665 0.05748082 0.51403266 1. ]]
使用给定的SAMPLING协方差矩阵生成正态变量
covsam <- function(nobs,covm, seed=1237) {;
library (expm);
# nons=number of observations, covm = given covariance matrix ;
nvar <- ncol(covm);
tot <- nvar*nobs;
dat <- matrix(rnorm(tot), ncol=nvar);
covmat <- cov(dat);
a2 <- sqrtm(solve(covmat));
m2 <- sqrtm(covm);
dat2 <- dat %*% a2 %*% m2 ;
rc <- cov(dat2);};
cm <- matrix(c(1,0.5,0.1,0.5,1,0.5,0.1,0.5,1),ncol=3);
cm;
res <- covsam(10,cm) ;
res;
使用给定的POPULATION协方差矩阵生成正态变量
covpop <- function(nobs,covm, seed=1237) {;
library (expm);
# nons=number of observations, covm = given covariance matrix;
nvar <- ncol(covm);
tot <- nvar*nobs;
dat <- matrix(rnorm(tot), ncol=nvar);
m2 <- sqrtm(covm);
dat2 <- dat %*% m2;
rc <- cov(dat2); };
cm <- matrix(c(1,0.5,0.1,0.5,1,0.5,0.1,0.5,1),ncol=3);
cm;
res <- covpop(10,cm);
res