# Initial simple example -------------------------------------------------------
# Get the dataset used in the example of stats::cov.wt()
xy <- cbind(x = 1:10, y = c(1:3, 8:5, 8:10))
# Define non-negative weights (as in example of stats::cov.wt())
wi <- c(0,0,0,1,1,1,1,1,0,0)
# Get the weighted estimate with the default methods
covwt_stats <- stats::cov.wt(xy, wt = wi) # i.e. method = "unbiased"
# Compare unweighted and weighted means
data.frame(uw = colMeans(xy),
select = colMeans(xy[wi == 1, ]),
wg = covwt_stats$center)
# Compare unweighted and weighted covariance matrix
data.frame(uw = c(cov(xy)),
select = c(cov(xy[wi == 1, ])),
wg = c(covwt_stats$cov),
row.names = c(sapply(colnames(cov(xy)), paste0, rownames(cov(xy))))
)
# Examine the internal code of stats::cov.wt() ---------------------------------
cov.wt
# Set up manual computation of cov.wt() ----------------------------------------
# Assign values to the function arguments
x <- xy # data
set.seed(20220314)
wi <- runif(length(wi), min = 0, max = 1)
method <- "ML" # use Maximum Likelihood for estimation
# Assign values to some of the internal objects
n <- nrow(x)
# Normalize weights ------------------------------------------------------------
# Normalise weights (to sum to 1)
wn <- wi / sum(wi)
# Check they sum to 1
sum(wn) == 1
# Compute the weighted means ---------------------------------------------------
# Center on weighted mean if required
center <- colSums(wn * x)
# Center X on the weigthed mean
x_cent <- sweep(x, 2, center, check.margin = FALSE)
# Note that the sweep is subtracting the "center" to each value
all.equal(
sweep(x, 2, center, check.margin = FALSE),
t(apply(x, 1, function (i) i - center))
)
# Compute the weighted covariance matrix ---------------------------------------
# Weight (centered) data
x_weighted <- sqrt(wn) * x_cent
# Compute the ML weigthed covariance matrix manually
covwt_man <- crossprod(x_weighted)
# Print the manual weigthed covariance matrix
covwt_man
# Compute the ML weigthed covariance matrix with stats::cov.wt()
covwt_stats <- cov.wt(xy, wt = wi, method = "ML", center = TRUE)$cov
# Compare manual and stats weigthed covariance matrices
covwt_man - covwt_stats
# Alternative computations of the unbiased weighted covariance mat -------------
# Literal translation of equation
1 / (1 - sum(wn^2)) * t(wn * x_cent) %*% (x_cent)
# Rearrange denominator
t(wn * x_cent) %*% (x_cent) / (1 - sum(wn^2))
# Spread wn
t(sqrt(wn) * x_cent) %*% (sqrt(wn)*x_cent) / (1 - sum(wn^2))
# Replace manual cross-product with R cross-product
crossprod(sqrt(wn) * x_cent)/(1 - sum(wn^2))
# Compute with stats::cov.wt()
cov.wt(xy, wt = wi, method = "unbiased", center = TRUE)$cov
# Alternative computations of the ML weighted covariance mat -------------------
# R manual cross-product using un-normalised weights
1 / sum(wi) * t(wi * x_cent) %*% (x_cent)
# Using the normalised weights
1 / sum(wn) * t(wn * x_cent) %*% (x_cent)
# Dropp the term = 1
t(wn * x_cent) %*% (x_cent)
# Spread wn
t(sqrt(wn) * x_cent) %*% (sqrt(wn) * x_cent)
# Replace manual cross-product with R cross-product
crossprod(sqrt(wn) * x_cent)
# R cross-product matrix
crossprod(x_weighted)
# Compute with stats::cov.wt()
cov.wt(xy, wt = wi, method = "ML", center = TRUE)$cov
# Obtain the matrix of sufficient statistics Tobs ------------------------------
# Define a new weigthing object
set.seed(20220314)
wi <- runif(length(wi), min = 0, max = 1)
wn <- wi / sum(wi)
# Compute the weighted means of X again
center <- colSums(wn * x)
x_cent <- sweep(x, 2, center, check.margin = FALSE)
# "Effective" sample size
n <- sum(wi)
# Number of columns
p <- ncol(x)
# Obtain matrix of sufficient statistics (Tobs)
Tobs_lopp <- matrix(0, p, p)
for(i in 1:nrow(x)){
Tobs_lopp <- Tobs_lopp + wi[i] * (x_cent[i, ]) %*% t(x_cent[i, ])
}
# Obtain matrix of sufficient statistics (Tobs) w/ cross-product shortcut
Tobs_cp <- t(wi * x_cent) %*% x_cent
# Compare loop version and cross-product shortcut
Tobs_lopp - Tobs_cp
# Assign simpler name and print Tobs
(Tobs <- Tobs_cp)
# Convert to a covariance matrix
covmat <- Tobs / n
# Check it's what you were expecting
covmat - cov.wt(xy, wt = wi, method = "ML", center = TRUE)$cov
# Obtain the matrix of sufficient statistics Tobs (normalised weights) ---------
# Convert to a covariance matrix
covmat - t(wn * x_cent) %*% x_cent
# Then, covmat relates to Tobs as
(t(wn * x_cent) %*% x_cent * n) - Tobs_cp
# So we could say
Tobs <- t(wn * x_cent) %*% x_cent * n