Comment réorganiser les données 2D pour obtenir une corrélation donnée?

9

J'ai le jeu de données simple suivant avec deux variables continues; c'est à dire:

d = data.frame(x=runif(100,0,100),y = runif(100,0,100))
plot(d$x,d$y)
abline(lm(y~x,d), col="red")
cor(d$x,d$y) # = 0.2135273

Distribution de base

J'ai besoin de réorganiser les données de manière à ce que la corrélation entre les variables soit ~ 0,6. Je dois garder les moyennes et autres statistiques descriptives (sd, min, max, etc.) des deux variables constantes.

Je sais qu'il est possible de faire presque n'importe quelle corrélation avec les données fournies, à savoir:

d2 = with(d,data.frame(x=sort(x),y=sort(y)))
plot(d2$x,d2$y)
abline(lm(y~x,d2), col="red")
cor(d2$x,d2$y) # i.e. 0.9965585

entrez la description de l'image ici

Si j'essaie d'utiliser la samplefonction pour cette tâche:

cor.results = c()
for(i in 1:1000){
    set.seed(i)
    d3 = with(d,data.frame(x=sample(x),y=sample(y)))
    cor.results =  c(cor.results,cor(d3$x,d3$y))
}

J'obtiens un large éventail de corrélations:

> summary(cor.results)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
-0.281600 -0.038330 -0.002498 -0.001506  0.034380  0.288800

mais cette plage dépend du nombre de lignes dans le bloc de données et diminue avec l'augmentation de la taille.

> d = data.frame(x=runif(1000,0,100),y = runif(1000,0,100))
> cor.results = c()
> for(i in 1:1000){
+ set.seed(i)
+ d3 = with(d,data.frame(x=sample(x),y=sample(y)))
+ cor.results =  c(cor.results,cor(d3$x,d3$y))
+ }
> summary(cor.results)
      Min.    1st Qu.     Median       Mean    3rd Qu.       Max. 
-0.1030000 -0.0231300 -0.0005248 -0.0005547  0.0207000  0.1095000

Ma question est:

Comment réorganiser un tel ensemble de données pour obtenir une corrélation donnée (ie 0,7)? (Il sera également utile que la méthode supprime la dépendance à la taille de l'ensemble de données)

Yuriy Petrovskiy
la source

Réponses:

6

Voici une façon de réorganiser les données en fonction de la génération de nombres aléatoires supplémentaires.

Nous tirons des échantillons d'une distribution normale bivariée avec une corrélation spécifiée. Ensuite, nous calculons les rangs des valeurs et nous obtenons. Ces classements sont utilisés pour classer les valeurs d'origine. Pour cette approche, nous avons trié à la fois les valeurs d' origine et .XyXy

Tout d'abord, nous créons l'ensemble de données réel (comme dans votre exemple).

set.seed(1)
d <- data.frame(x = runif(100, 0, 100), y = runif(100, 0, 100))

cor(d$x, d$y)
# [1] 0.01703215

Maintenant, nous spécifions une matrice de corrélation.

corr <- 0.7  # target correlation
corr_mat <- matrix(corr, ncol = 2, nrow = 2)
diag(corr_mat) <- 1
corr_mat
#      [,1] [,2]
# [1,]  1.0  0.7
# [2,]  0.7  1.0

Nous générons des données aléatoires suivant une distribution normale bivariée avec , (pour les deux variables) et la corrélation spécifiée. Dans R, cela peut être fait avec la fonction du package. Nous utilisons pour indiquer que la corrélation est la corrélation empirique (pas la corrélation de population).μ=0σ=1mvrnormMASSempirical = TRUE

library(MASS)
mvdat <- mvrnorm(n = nrow(d), mu = c(0, 0), Sigma = corr_mat, empirical = TRUE)

cor(mvdat)
#      [,1] [,2]
# [1,]  1.0  0.7
# [2,]  0.7  1.0

Les données aléatoires correspondent parfaitement à la corrélation spécifiée.

Ensuite, nous calculons les rangs des données aléatoires.

rx <- rank(mvdat[ , 1], ties.method = "first")
ry <- rank(mvdat[ , 2], ties.method = "first")

Pour utiliser les classements des données d'origine dans d, nous devons trier les données d'origine.

dx_sorted <- sort(d$x)
dy_sorted <- sort(d$y)

Maintenant, nous pouvons utiliser les classements pour spécifier l'ordre des données triées.

cor(dx_sorted[rx], dy_sorted[ry])
# [1] 0.6868986

La corrélation obtenue ne correspond pas parfaitement à celle spécifiée, mais la différence est relativement faible.

Ici, dx_sorted[rx]et dy_sorted[ry]sont des versions rééchantillonnées des données d'origine dans d.

Sven Hohenstein
la source
2
+1 C'est assez cool. Les étapes sont 1) générer des données normales avec la bonne corrélation de Pearson, 2) faire en sorte que les données d'origine et les données générées correspondent exactement aux corrélations de rang, 3) les données d'origine ont à peu près la même corrélation de Pearson maintenant. Pourquoi ça marche? Y a-t-il un résultat analytique indiquant que c'est le cas? Inégalités limitantes gardant les différentes mesures de corrélation proches les unes des autres pour des distributions bien comportées ou quelque chose?
Bill
1
@Bill je ne peux pas expliquer l'approche analytiquement. C'est juste une idée qui m'est venue à l'esprit. Cependant, vous avez bien résumé les étapes. Merci.
Sven Hohenstein
2

Pour générer deux distributions uniformes avec une corrélation spécifiée, l'algorithme de Ruscio et Kaczetow (2008) fonctionnera. Ils fournissent le code R . Vous pouvez ensuite transformer avec une simple fonction linéaire pour obtenir votre cible min, max, moyenne et SD.

Algorithme Ruscio et Kaczetow

XoOuioX1Oui1X1Oui1X0Oui0X1,Oui1X2Oui2

Notez que cela est très similaire à la solution de @Sven Hohenstein, sauf qu'elle est itérative, de sorte que la corrélation intermédiaire se rapprochera de plus en plus de la corrélation cible jusqu'à ce qu'elles soient indiscernables. Notez également que cet algorithme peut être utilisé pour générer une grande population (par exemple, N = 1 million) à partir de laquelle tirer des échantillons plus petits - ce qui est utile si vous devez avoir une erreur d'échantillonnage.

Pour un poste connexe: corrélation et distributions non normales

Préserver les statistiques descriptives

Il n'y a aucune garantie que l'algorithme produira exactement les mêmes descriptifs. Cependant, comme la moyenne et l'écart-type d'une distribution uniforme sont déterminés par ses valeurs min et max, vous pouvez simplement ajuster les valeurs min et max pour tout corriger.

XgOuigXFOuiFXOui

XF=(Xg-mjen(X))(muneX(X)-mjen(X))/(muneX(Xg)-mjen(Xg))

OuiF

Référence:

Ruscio, J. et Kaczetow, W. (2008). Simulation de données non normales multivariées à l'aide d'un algorithme itératif. Multivariate Behavioral Research, 43, 355–381. doi: 10.1080 / 00273170802285693

Anthony
la source
1

Je suppose que lorsque vous dites «rééchantillonner», vous voulez dire «simuler», ce qui est plus général. Ce qui suit est le moyen le plus concis que je connaisse pour simuler des données normales à deux variables avec une corrélation spécifiée. Remplacez vos propres valeurs souhaitées par r et n.

r = .6
n = 1000
x = rnorm(n) 
z = rnorm(n) 
y = (r/(1-r^2)^.5)*x + z

cor(x,y)
plot(x,y)
abline(lm(y~x), col="red")
rolando2
la source
3
Non, je veux vraiment dire "rééchantillonner". J'ai besoin de garder constantes les moyennes et autres statistiques descriptives (sd, min, max) des deux variables. Mis à jour la question.
Yuriy Petrovskiy