Assignment

One of the most useful applications for linear algebra in data science is image manipulation. We often need to compress, expand, warp, skew, etc. images. To do so, we left multiply a transformation matrix by each of the point vectors.

For this assignment, build the first letters for both your first and last name using point plots in R.

Then, write R code that will left multiply (%>%) a square matrix (x) against each of the vectors of points (y). Initially, that square matrix will be the Identity matrix.

Use a loop that changes the transformation matrix incrementally to demonstrate 1) shear, 2) scaling, 3) rotation , and 4) projection in animated fashion.




Library

The gifski library allows us to turn a series of plots into a GIF. A note about implementation, we’ve added the following code to the top of our code blocks that we wanted to see animated as a GIF: {r, animation.hook = 'gifski'}

library("gifski")



Build Letters

Here we build the letters, ‘P’ & ‘K’.

Every line needs an x and a y entry. Think of each line in terms of a box on the x-y plane where you specify the beginning and ending value of X in the box for the x entry and the beginning and ending value of Y in the box for the y entry. You also have to specify the length which is the number of dots (actually small circles) drawn between those two points. The number of dots must be consistent between each x and y entry. Vertical lines only specify the one x-coordinate value using the rep function. Horizontal lines only specify the one y-coordinate using the rep function.

I had the benefit of reviewing Diana Plunkett’s work to learn how to make these lines. rpubs.com/dplunkett/996546

lo <- 20

x=c(rep(0,lo),
    seq(0,.5,length.out=lo/2),
    seq(0,.5,length.out=lo/2),
    rep(.5,lo/2),
    rep(.8,lo),
    seq(.8,1.3,length.out=lo),
    seq(1,1.3,length.out=lo/2))

y=c(seq(0,2,length.out=lo),
    rep(1,lo/2),
    rep(2,lo/2),
    seq(1,2,length.out=lo/2),
    seq(0,2,length.out=lo),
    seq(.7,2,length.out=lo),
    seq(1,0,length.out=lo/2))

z=rbind(x,y)

plot(y~x, xlim=c(-2,4), ylim=c(-3,5))




Matrix Transformations

In all of the image translations below, we are multiplying the x, y-coordinates of a dot in an image by a matrix so that it has new x, y-coordinates.

We intentionally used a low number of vector dots to represent the lines so that the individual

I wasn’t able to independently originate these concepts. I tried for hours, cooked myself a steak and garlic mushroom lunch to fend off despair, used right-nostril ayurvedic breathing to stimulate my left-brain. But nothing. I ended up borrowing generously from Catherine Cho’s work here: rpubs.com/catcho1632/996294.

To compensate for the generous borrowing, I’ve doubled the examples for each transformation type to demonstrate an ability to use the concepts and tried to format and explain in a way that would teach someone if they referred to this.


Shear Matrix Transformation

This is a transformation of all points where for each one, one coordinate stays fixed and the other is shifted in portion to how far away it is.


X-Axis Shear

With x-axis shear we are magnifying each dot’s x-coordinate, the y-coordinate remains unchanged, based on the magnitude of the dot’s y-coordinate. Notice how the x-coordinate is changing in the GIF below for each dot but the y-coordinate stays the same.

Note that this matrix is the identity matrix with a 1 in the bottom-left element, corresponding to the x-coordinate. In the animation below, that 1 is replaced with incrementally larger numbers.

\[ XShear:\ \left[ {\begin{array}{cc} 1 & 0 \\ 1 & 1 \\ \end{array} } \right] \]

for (i in seq(0,1.5,length.out=10)) {
  z_xshear <- apply(z,2,function(x) x %*% matrix(c(1,i,0,1),ncol=2))
  plot(z_xshear[2,]~z_xshear[1,], xlim=c(-2,4), ylim=c(-3,5), col='black', xlab="x-axis", ylab="y-axis", main="X-Axis Shear")
}


Y-Axis Shear

With y-axis shear we are magnifying each dot’s y-coordinate, the x-coordinate remains unchanged, based on the magnitude of the dot’s x-coordinate. Notice how the y-coordinate is changing in the GIF below for each dot but the x-coordinate stays the same.

Note that this matrix is the identity matrix with a 1 in the top-right element, corresponding to the y-coordinate. In the animation below, that 1 is replaced with incrementally larger numbers.

\[ YShear:\ \left[ {\begin{array}{cc} 1 & 1 \\ 0 & 1 \\ \end{array} } \right] \]

for (i in seq(0,2,length.out=10)) {
  z_yshear <- apply(z,2,function(x) x %*% matrix(c(1,0,i,1),ncol=2))
  plot(z_yshear[2,]~z_yshear[1,], xlim=c(-2,4), ylim=c(-3,5), col='black', xlab="x-axis", ylab="y-axis", main="Y-Axis Shear")
}


Scaling Matrix Transformation

This is a transformation of all points where both coordinates are magnified in the same proportion so that the image stays fixed, it just gets bigger or smaller.


Increase Scaling

Note that if a in the matrix below were 1, this would be the identity matrix. In the animation below, a is incrementally larger numbers starting with 1.

\[ Scale:\ \left[ {\begin{array}{cc} a & 0 \\ 0 & a \\ \end{array} } \right] \]

for(i in seq(1,1.5,length.out = 10)){
  z_scale_up<-apply(z,2,function(x) x %*% matrix(c(2*i,0,0,2*i),ncol=2))
  plot(z_scale_up[2,]~z_scale_up[1,],xlim=c(-2,4),ylim=c(-3,5),col='black', xlab="x-axis", ylab="y-axis", main="Increase Scaling")
}


Decrease Scaling

Here a is being replaced with incrementally smaller fractions so we see the letters shrink in the GIF.

\[ Scale:\ \left[ {\begin{array}{cc} a & 0 \\ 0 & a \\ \end{array} } \right] \]

for(i in seq(1,2,length.out = 10)){
  z_scale_down<-apply(z,2,function(x) x %*% matrix(c(1/i,0,0,1/i),ncol=2))
  plot(z_scale_down[2,]~z_scale_down[1,],xlim=c(-2,4),ylim=c(-3,5),col='black', xlab="x-axis", ylab="y-axis", main="Decrease Scaling")
}


Rotation Matrix Transformation

If you think of each dot in our letters as a vector from (0,0) then the rotational matrix rotates all of the vectors by the same angle as measured from (0,0).

While the rotations are true rotations, we’ve had to adjust the size of these two plots so that we have a more accurate one-to-one correspondence between the x and y-axis unit length when represented on the screen so that the true rotation is evident.


Counterclockwise Rotation

Here is the matrix for Counterclockwise Rotation. If you imagine a unit-circle you can work out how the sines and cosines work together to rotate the vectors.

\[ Counterclockwise \ Rotation:\ \left[ {\begin{array}{cc} cos(a) & sin(a) \\ -sin(a) & cos(a) \\ \end{array} } \right] \]

for(i in seq(0,2*pi,length.out = 16)){
  z_rotate_counter <- apply(z,2,function(x) x %*% matrix(c(cos(i),-sin(i),sin(i),cos(i)),ncol=2))
  plot(z_rotate_counter[2,]~z_rotate_counter[1,],xlim=c(-5,5),ylim=c(-3,3),col='black', xlab="x-axis", ylab="y-axis", main="Counterclockwise Rotation")
}


Clockwise Rotation

Here is the matrix for Clockwise Rotation. If you imagine a unit-circle you can work out how the sines and cosines work together to rotate the vectors.

\[ Clockwise \ Rotation:\ \left[ {\begin{array}{cc} cos(a) & -sin(a) \\ sin(a) & cos(a) \\ \end{array} } \right] \]

for(i in seq(0,2*pi,length.out = 16)){
  z_rotate_clockwise <- apply(z,2,function(x) x %*% matrix(c(cos(i),sin(i),-sin(i),cos(i)),nrow=2,ncol=2))
  plot(z_rotate_clockwise[2,]~z_rotate_clockwise[1,],xlim=c(-5,5),ylim=c(-3,3),col='black', xlab="x-axis", ylab="y-axis", main="Clockwise Rotation")
}


Projection

When you project a vector, it always reduces the dimension of the vector. In this case we are projecting the vectors from two dimensions onto one.


X-Axis Projection

Projection onto the x-axis occurs when we replace the y-coordinate with zero which can be accomplished with the following matrix. As such our animation is only two animations: start and finish. Incremental steps would be a form of single-axis scaling.

\[ XAxis \ Projection:\ \left[ {\begin{array}{cc} 1 & 0 \\ 0 & 0 \\ \end{array} } \right] \]

for (i in 0:1) {
  z_project_x <- apply(z,2,function(x) x %*% matrix(c(1,0,0,(1-i)),nrow=2,ncol=2))
  plot(z_project_x[2,]~z_project_x[1,], xlim=c(-2,4), ylim=c(-3,5), col='black', xlab="x-axis", ylab="y-axis", main="X-Axis Projection")
}


Y-Axis Projection

Projection onto the y-axis occurs when we replace the x-coordinate with zero which can be accomplished with the following matrix. As such our animation is only two animations: start and finish. Incremental steps would be a form of single-axis scaling.

\[ YAxis \ Projection:\ \left[ {\begin{array}{cc} 0 & 0 \\ 0 & 1 \\ \end{array} } \right] \]

for (i in 0:1) {
  z_project_y <- apply(z,2,function(x) x %*% matrix(c((1-i),0,0,1),nrow=2,ncol=2))
  plot(z_project_y[2,]~z_project_y[1,], xlim=c(-2,4), ylim=c(-3,5), col='black', xlab="x-axis", ylab="y-axis", main="Y-Axis Projection")
}