最も一般的な解決策を説明します。この一般性の問題を解決することにより、非常にコンパクトなソフトウェア実装を実現できます。2行のR
コードで十分です。
ベクターの選択同じ長さで、、好きな分布に従います。 してみましょうの最小二乗回帰の残差もに対する:これは抽出からコンポーネントを。適切な複数のバック追加することによって、に、我々は、任意の所望の相関を有するベクトル生成することができると。任意の加法定数と正の乗法定数(任意の方法で自由に選択できます)までは、解はY Y ⊥ X Y Y X Y Y ⊥ ρ YXYY⊥XYYXYY⊥ρY
XY;ρ=ρSD(Y⊥)Y+1−ρ2−−−−−√SD(Y)Y⊥.
( " "は標準偏差に比例する計算を表します。)SD
これが作業R
コードです。を指定しない場合、コードは多変量標準正規分布から値を引き出します。X
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)
}
説明のために、コンポーネントを持つランダムを生成し、このとさまざまな指定された相関を持つを生成しました。それらはすべて同じ開始ベクトル作成されました。以下がその散布図です。各パネルの下部にある「ラグプロット」は、共通のベクトルを示しています。Y50XY;ρYX=(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")
ところで、この方法は複数のに容易に一般化できます:数学的に可能な場合、が全体と指定された相関をますセット。通常の最小二乗法を使用して、からすべての効果を取り出し、と残差の適切な線形結合を形成します。(これは、双対基底の観点からこれを行うのに役立ちます。これは、疑似逆行列を計算することによって取得されます。次のコードは、のSVDを使用してそれを達成します。)YXY1,Y2,…,Yk;ρ1,ρ2,…,ρkYiYiXYiYY
ここでは、アルゴリズムのスケッチだR
、行列の列として与えられているが。Yiy
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))