library(HSAUR2)Loading required package: tools
data(USairpollution)SUBMISSION INSTRUCTIONS
Consider plots of \(UD_i\) for four different \(D_i\):
Match the \(D\) to the plot.
A=D4
B=D1
C=D2
D=D3
Consider two different \(D\) matrices:
\[D_1 = \begin{pmatrix} 4 & 0\\ 0 & 1 \end{pmatrix}\] \[D_2 = \begin{pmatrix} 2 & 0\\ 0 & 1 \end{pmatrix}\]
and two different \(V\) matrices:
\[V_1 = \begin{pmatrix} 0.894 & -0.447\\ 0.447 & 0.894 \end{pmatrix}\] \[V_2 = \begin{pmatrix} -0.447 & 0.894\\ 0.894 & 0.447 \end{pmatrix}\]
Consider the following matrix product:
\[UD_i V_j^T, i\in \{1,2\}, j \in \{1,2\}\]
In other words, \(U\) is multiplied by \(D_i V_j^T\) for all four combinations of \(D_i\) and \(V_j\). Match the product to the plot.
A=UD1V1T
B=UD1V2T
C=UD2V1T
D=UD2V2T
Reconsider the US air pollution data set:
library(HSAUR2)Loading required package: tools
data(USairpollution)Perform singular value decomposition of this data matrix. Then create the matrix \(D\). Describe what this matrix looks like.
library(HSAUR2)
data(USairpollution)
X <- as.matrix(USairpollution)
sv <- svd(X)
D <- diag(sv$d)
knitr::kable(round(D, 2), caption = "Matrix D (singular values on the diagonal)")| 7051.95 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
| 0.00 | 931.12 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
| 0.00 | 0.00 | 540.46 | 0.00 | 0.00 | 0.00 | 0.00 |
| 0.00 | 0.00 | 0.00 | 92.71 | 0.00 | 0.00 | 0.00 |
| 0.00 | 0.00 | 0.00 | 0.00 | 85.24 | 0.00 | 0.00 |
| 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 52.95 | 0.00 |
| 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 10.14 |
Verify that \(X=UDV^T\) by plotting all the entries of \(X\) versus all the entries of \(UDV^T\) with the 0/1 line.
X <- as.matrix(USairpollution)
components <- svd(X)
U <- components$u
D <- components$d
V <- components$v
Xrecomp <- U %*% diag(D) %*% t(V)
plot(X, Xrecomp); abline(0, 1)cor(as.vector(X), as.vector(Xrecomp))[1] 1
Consider low-dimensional approximations of the data matrix. What is the fewest number of dimensions required to yield a correlation between the entries of \(X\) and \(\tilde X\) of at least 0.9?
k_values <- 1:ncol(X)
cors <- sapply(k_values, \(k) {
Xtilde <- U[, 1:k, drop = FALSE] %*% diag(D[1:k], nrow = k) %*% t(V[, 1:k, drop = FALSE])
cor(as.vector(X), as.vector(Xtilde))
})
data.frame(k = k_values, correlation = round(cors, 4)) k correlation
1 1 0.9864
2 2 0.9966
3 3 0.9998
4 4 0.9999
5 5 1.0000
6 6 1.0000
7 7 1.0000
min(k_values[cors >= 0.9]) # fewest dimensions with correlation >= 0.9[1] 1
Find \(\Sigma\), the covariance matrix of this data set. Then perform eigen-decomposition of this matrix. Also, mean-center (but don’t unit-scale) \(X\): use scale(X, center = TRUE, scale = FALSE). SVD this mean-centered data matrix. Verify that:
The eigenvectors of \(\Sigma\) equal the columns of \(V\);
The eigenvalues of \(\Sigma\) equal the diagonals of \(D^2/(n-1)\).
library(HSAUR2)
data(USairpollution)
X <- as.matrix(USairpollution)
n <- nrow(X)
# Covariance matrix
Sigma <- cov(X)
round(Sigma, 2) SO2 temp manu popul wind precip predays
SO2 550.95 -73.56 8527.72 6711.99 3.18 15.00 229.93
temp -73.56 52.24 -773.97 -262.35 -3.61 32.86 -82.43
manu 8527.72 -773.97 317502.89 311718.81 191.55 -215.02 1968.96
popul 6711.99 -262.35 311718.81 335371.89 175.93 -178.05 645.99
wind 3.18 -3.61 191.55 175.93 2.04 -0.22 6.21
precip 15.00 32.86 -215.02 -178.05 -0.22 138.57 154.79
predays 229.93 -82.43 1968.96 645.99 6.21 154.79 702.59
# Eigen-decomposition of Sigma
eigenSigma <- eigen(Sigma)
# Mean-center (but don't scale) X, then SVD
Xc <- scale(X, center = TRUE, scale = FALSE)
svdXc <- svd(Xc)
# Check 1: eigenvectors of Sigma vs. columns of V
round(eigenSigma$vectors, 3) [,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 0.017 -0.100 0.209 0.959 -0.152 -0.054 -0.027
[2,] -0.001 0.026 -0.072 -0.110 -0.478 -0.853 -0.164
[3,] 0.697 -0.710 -0.067 -0.073 -0.010 -0.002 0.001
[4,] 0.717 0.693 0.057 0.049 0.011 0.003 0.000
[5,] 0.000 -0.001 0.005 -0.015 0.025 0.177 -0.984
[6,] 0.000 0.001 0.266 -0.163 -0.833 0.453 0.064
[7,] 0.003 -0.069 0.934 -0.185 0.233 -0.184 -0.019
round(svdXc$v, 3) [,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] -0.017 0.100 0.209 -0.959 0.152 -0.054 -0.027
[2,] 0.001 -0.026 -0.072 0.110 0.478 -0.853 -0.164
[3,] -0.697 0.710 -0.067 0.073 0.010 -0.002 0.001
[4,] -0.717 -0.693 0.057 -0.049 -0.011 0.003 0.000
[5,] 0.000 0.001 0.005 0.015 -0.025 0.177 -0.984
[6,] 0.000 -0.001 0.266 0.163 0.833 0.453 0.064
[7,] -0.003 0.069 0.934 0.185 -0.233 -0.184 -0.019
# Check 2: eigenvalues of Sigma vs. D^2 / (n - 1)
round(eigenSigma$values, 3)[1] 638471.962 14812.038 701.960 205.002 116.705 12.057 1.449
round(svdXc$d^2 / (n - 1), 3)[1] 638471.962 14812.038 701.960 205.002 116.705 12.057 1.449
In this problem we explore how “high” a low-dimensional SVD approximation of an image has to be before you can recognize it.
.Rdata objects are essentially R workspace memory snapshots that, when loaded, load any type of R object that you want into your global environment. The command below, when executed, will load three objects into your memory: mysteryU4, mysteryD4, and mysteryV4. These are the first \(k\) vectors and singular values of an SVD I performed on a 700-pixels-tall \(\times\) 600-pixels-wide image of a certain WSU professor who has turned into a zombie. How low-dimensional an approximation of the full image can you get away with, and still identify the professor?
load('Data/zombie_k4.Rdata')Write a function that takes SVD ingredients u, d and v and renders the \(700 \times 600\) image produced by this approximation using functions from the magick package. Use your function to determine whether a 4-dimensional approximation to this image is enough for you to tell who has been zombified. Recall that you will likely need to rescale your recomposed approximation so that all pixels are in [0,1].
library(magick)Linking to ImageMagick 6.9.13.29
Enabled features: cairo, fontconfig, freetype, heic, lcms, pango, raw, rsvg, webp
Disabled features: fftw, ghostscript, x11
render_svd <- \(u, d, v, k = NULL) {
u <- as.matrix(u)
v <- as.matrix(v)
if (is.matrix(d)) d <- diag(d) # if D was given as a matrix, pull out the diagonal
d <- as.numeric(d)
if (is.null(k)) k <- length(d)
recomposed <- u[, 1:k, drop = FALSE] %*% diag(d[1:k], nrow = k) %*% t(v[, 1:k, drop = FALSE])
R <- max(recomposed) - min(recomposed)
recomposed_scaled <- (recomposed - min(recomposed)) / R # rescale so all pixels are in [0, 1]
recomposed_image <- image_read(as.raster(recomposed_scaled))
return(recomposed_image)
}
# 4-dimensional approximation
render_svd(mysteryU4, mysteryD4, mysteryV4, k = 4)I’m giving you slightly higher-dimensional approximations (\(k=10\) and \(k=50\), respectively) in the objects below:
load('Data/zombie_k10.Rdata')
load('Data/zombie_k50.Rdata')Create both of the images produced by these approximations. At what point can you tell who has been zombified?
# k = 10
render_svd(mysteryU10, mysteryD10, mysteryV10, k = 10)# k = 50
render_svd(mysteryU50, mysteryD50, mysteryV50, k = 50)K=50 is when the face becomes fully clear. K=20 and higher slowly improve the picture quality.
How many numbers need to be stored in memory for each of the following:
Full: 700x600=420,000
4D: 700×4 + 4 + 4×600 = 2,800 + 4 + 2,400= 5,204
10D: 700×10 + 10 + 10×600 = 7,000 + 10 + 6,000= 13,010
50D:700×50 + 50 + 50×600 = 35,000 + 50 + 30,000= 65,050