【问题标题】:Fine tuning stat_ellipse() in ggplot2在 ggplot2 中微调 stat_ellipse()
【发布时间】:2014-12-09 15:07:05
【问题描述】:

我想创建一个具有 95%“精确”置信椭圆的二元正态分布散点图。

library(mvtnorm)
library(ggplot2)
set.seed(1)
n <- 1e3
c95 <- qchisq(.95, df=2) 
rho <- 0.8  #correlation 
Sigma <- matrix(c(1, rho, rho, 1), 2, 2) # Covariance matrix

我从均值为零且方差 =Sigma 的双变量正态生成了 1000 个观察值

x <- rmvnorm(n, mean=c(0, 0), Sigma)
z  <- p95 <- rep(NA, n)
for(i in 1:n){
  z[i] <- x[i, ] %*% solve(Sigma, x[i, ])
  p95[i] <- (z[i] < c95)
}

我们可以使用stat_ellipse 在生成数据的散点图顶部轻松绘制 95% 置信椭圆。在您注意到几个红点位于置信椭圆内之前,结果图是完全令人满意的。我猜这种差异来自对某些参数的估计,并且随着样本量的增大而消失。

data <- data.frame(x, z, p95)
p <- ggplot(data, aes(X1, X2)) + geom_point(aes(colour = p95))
p + stat_ellipse(type = "norm")

有什么方法可以微调stat_ellipse(),使其描绘出使用“手工”ellips 函数创建的“精确”置信椭圆,如下图所示?

ellips <- function(center = c(0,0), c=c95, rho=-0.8, npoints = 100){
  t <- seq(0, 2*pi, len=npoints)
  Sigma <- matrix(c(1, rho, rho, 1), 2, 2)
  a <- sqrt(c*eigen(Sigma)$values[2])
  b <- sqrt(c*eigen(Sigma)$values[1])
  x <- center[1] + a*cos(t)
  y <- center[2] + b*sin(t)
  X <- cbind(x, y)
  R <- eigen(Sigma)$vectors
  data.frame(X%*%R)
}
dat <- ellips(center=c(0, 0), c=c95, rho, npoints=100)
p + geom_path(data=dat, aes(x=X1, y=X2), colour='blue')

【问题讨论】:

  • 您不会真的期望随机样本具有与用于生成它的参数相同的参数,对吗?
  • 不,不,我只需要准确的置信区间来评估双变量正态生成的 Gibbs 采样的准确性。
  • 我认为您将中心添加到 xy 值中为时过早。当它们不为零时,它们最终会与特征向量相乘。

标签: r ggplot2


【解决方案1】:

这不是一个真正的答案,但它可能会有所帮助。

通过使用以下命令探索stat_ellipse

stat_ellipse
ls(ggplot2:::StatEllipse)
ggplot2:::StatEllipse$calculate
ggplot2:::calculate_ellipse
?cov.wt

似乎cov.wt 正在根据模拟数据估计协方差矩阵:

cov.wt(data[, c(1, 2)])$cov
#           X1        X2
# X1 1.1120267 0.8593946
# X2 0.8593946 1.0372208

# True covariance matrix:
Sigma
#      [,1] [,2]
# [1,]  1.0  0.8
# [2,]  0.8  1.0

您可以考虑使用估计的协方差矩阵计算您的p95 值。或者只是坚持使用您自己执行良好的椭圆绘图代码。

【讨论】:

    【解决方案2】:

    原问题中提出的椭圆代码是错误的。它在 X1 和 X2 变量的平均值为 0 且标准差为 1 时有效,但在一般情况下无效。

    这是一个替代实现,改编自stat_ellipse 源代码。它以均值向量、协方差矩阵、半径(例如用置信度计算)和形状的段数作为参数。

    calculate_ellipse <- function(center, shape, radius, segments){
    # Adapted from https://github.com/tidyverse/ggplot2/blob/master/R/stat-ellipse.R
        chol_decomp <- chol(shape)
        angles <- (0:segments) * 2 * pi/segments
        unit.circle <- cbind(cos(angles), sin(angles))
        ellipse <- t(center + radius * t(unit.circle %*% chol_decomp))
        colnames(ellipse) <- c("X1","X2")
        as.data.frame(ellipse)
    }
    

    让我们比较两个实现:

    library(ggplot2)
    library(MASS)  # mvrnorm function, to sample multivariate normal variables
    set.seed(42)
    mu = c(10, 20)  # vector of means
    rho = -0.7      # correlation coefficient
    correlation = matrix(c(1, rho, rho, 1), 2)  # correlation matrix
    std = c(1, 10)  # vector of standard deviations
    sigma =  diag(std) %*% correlation %*% diag(std)  # covariance matrix
    N = 1000  # number of points
    confidence = 0.95  # confidence level for the ellipse
    
    df = data.frame(mvrnorm(n=N, mu=mu, Sigma=sigma))
    
    radius = sqrt(2 * stats::qf(confidence, 2, Inf))  # radius of the ellipse
    
    ellips <- function(center = c(0,0), c=c95, rho=-0.8, npoints = 100){
    # Original proposal
        t <- seq(0, 2*pi, len=npoints)
        Sigma <- matrix(c(1, rho, rho, 1), 2, 2)
        a <- sqrt(c*eigen(Sigma)$values[2])
        b <- sqrt(c*eigen(Sigma)$values[1])
        x <- center[1] + a*cos(t)
        y <- center[2] + b*sin(t)
        X <- cbind(x, y)
        R <- eigen(Sigma)$vectors
        data.frame(X%*%R)
    }
    
    calculate_ellipse <- function(center, shape, radius, segments){
    # Adapted from https://github.com/tidyverse/ggplot2/blob/master/R/stat-ellipse.R
        chol_decomp <- chol(shape)
        angles <- (0:segments) * 2 * pi/segments
        unit.circle <- cbind(cos(angles), sin(angles))
        ellipse <- t(center + radius * t(unit.circle %*% chol_decomp))
        colnames(ellipse) <- c("X1","X2")
        as.data.frame(ellipse)
    }
    
    ggplot(df) +
        aes(x=X1, y=X2) +
        theme_bw() +
        geom_point() +
        geom_path(aes(color="new implementation"), data=calculate_ellipse(mu, sigma, radius, 100)) +
        geom_path(aes(color="original implementation"), data=ellips(mu, confidence, rho, 100))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-20
      • 1970-01-01
      相关资源
      最近更新 更多