You’ll need the “magick” package installed. Use the package manager in RStudio (on the right side of the screen by default) or type packages.install(‘magick’) in the Console.
One of the most useful features in R is this colon trick for creating a list (or vector) of consecutive integers:
v <- 4:9
print(v)
## [1] 4 5 6 7 8 9
This can be used in many interesting ways. Once you have the vector, you can do arithmetic on it, like this:
w <- v / 10 + 5
print(w)
## [1] 5.4 5.5 5.6 5.7 5.8 5.9
An alternative way to create the same vector is to type the entries individually and collect them with the important c command:
w <- c(5.4,5.5,5.6,5.7,5.8,5.9)
print(w)
## [1] 5.4 5.5 5.6 5.7 5.8 5.9
Warning: at some point you will be tempted to create a variable with the name c, the third lower case letter. Do not do this!!!! That overwrites the important “collect” function. The same goes for lower case t, which is the “transpose” function.
If you have two vectors of the same length, you can make a plot!
x <- (0:50) * pi / 50 # numbers from zero to pi
y <- sin(x) # create some data
y[20] <- 0.2 # put an outlier in the 20th spot
plot(x,y, main="A simple plot")
x <- 0*(1:6)
x[3:5] <- 2
Use the colon syntax to create a list of 101 evenly spaced numbers, starting at -1 and ending at 3.
Create the vector \((2,2,2,2,5,6,7,8,9,-1)\).
Suppose we want to build this matrix: \[ A = \begin{pmatrix} 0.2&0.8&0.8&1.0\\ 0.6&0.6&1.0&0.0\\ 0.2&0.0&0.0&0.5 \end{pmatrix}\]
We can do this by creating a vector with all of the entries, and then making it 2D by giving it the desired dimensions. Note that we start with all of the elements in the first column, then the second, and so on.
A <- c(0.2,0.6,0.2,0.8,0.6,0.0,0.8,1.0,0.0,1.0,0.0,0.5)
dim(A) <- c(3,4)
print(A)
## [,1] [,2] [,3] [,4]
## [1,] 0.2 0.8 0.8 1.0
## [2,] 0.6 0.6 1.0 0.0
## [3,] 0.2 0.0 0.0 0.5
It’s kind of annoying to type every entry in the matrix, though. For matrices with more structure (such as large regions with identical entries) there are shortcuts. Here’s an example:
\[ B = \begin{pmatrix}0&0&0&0&0&0&0&0&0&0&0\\ 0&0&0&0&0&0&0&0&2&0&0\\ 0&1&1&1&1&1&0&0&0&0&0\\ 0&1&1&1&1&1&0&0&0&4&4\\ 0&1&1&1&1&1&0&0&0&4&4\\ 0&0&0&0&0&0&0&0&0&4&4\end{pmatrix}\]
To create \(B\), we’ll start with a matrix containing all zeros. Then we’ll try to change whole blocks of entries simultaneously:
B <- matrix(0,6,11)
B[3:5,2:6] <- 1
B[2,9] <- 2
B[4:6,10:11] <- 4
print(B)
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
## [1,] 0 0 0 0 0 0 0 0 0 0 0
## [2,] 0 0 0 0 0 0 0 0 2 0 0
## [3,] 0 1 1 1 1 1 0 0 0 0 0
## [4,] 0 1 1 1 1 1 0 0 0 4 4
## [5,] 0 1 1 1 1 1 0 0 0 4 4
## [6,] 0 0 0 0 0 0 0 0 0 4 4
It can also useful be useful to create matrices with randomly chosen entries. Here are two versions, first with uniformly distributed entries (between 0 and 1) and next with normally distributed entries (with mean zero and standard deviation 1). In both cases we start with a vector and then use the matrix command to reshape it into a… matrix with given dimensions.
r1 <- matrix(runif(20),4,5)
print(r1)
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0.78246258 0.5627484 0.7143992 0.2295002 0.6962577
## [2,] 0.77806028 0.6500179 0.3925120 0.3032248 0.4019285
## [3,] 0.28726713 0.2898856 0.6142789 0.8259216 0.5040752
## [4,] 0.03599414 0.6167250 0.0506471 0.4745903 0.3565068
r2 <- matrix(rnorm(12),4,3)
print(r2)
## [,1] [,2] [,3]
## [1,] -0.9348628 1.0830267 0.09197187
## [2,] -0.7276327 0.3961837 0.91745541
## [3,] -1.1021032 0.8448018 0.44164386
## [4,] -0.5232243 -0.2735097 0.04969432
One more trick: diagonal matrices! Check this out:
vec <- c(4,5,6,7,8,9)
C = diag(vec)
print(C)
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 4 0 0 0 0 0
## [2,] 0 5 0 0 0 0
## [3,] 0 0 6 0 0 0
## [4,] 0 0 0 7 0 0
## [5,] 0 0 0 0 8 0
## [6,] 0 0 0 0 0 9
\[ W = \begin{pmatrix} 16&-2&\pi\\0&0&0\\1&-1&12\\0&0&1 \end{pmatrix} \]
# your code here: end with something like "print(W)"
\[ X = \begin{pmatrix} 2&2&2&2\\ 2&2&2&1\\ 2&2&1&1\\ 2&1&1&1\\ 1&1&1&1\\ \end{pmatrix} \]
\[ Y = \begin{pmatrix} 4&3&3&2&2&2&2&2&2\\ 3&4&3&2&2&2&2&2&2\\ 3&3&4&2&2&2&2&2&2\\ 0&0&0&4&3&3&2&2&2\\ 0&0&0&3&4&3&2&2&2\\ 0&0&0&3&3&4&2&2&2\\ 0&0&0&0&0&0&4&3&3\\ 0&0&0&0&0&0&3&4&3\\ 0&0&0&0&0&0&3&3&4\\ \end{pmatrix} \]
Now let’s create some images! To make a color image, we’ll create three matrices (one for each of the Red, Green and Blue color channels). Here are the three matrices for our first example:
myRed <- matrix(0,500,500) # a 500x500 grid of zeros
myRed[100:300,100:300] <- 1 # change some entries into ones
myGreen <- matrix(0,500,500)
myGreen[200:400,150:350] <- 1
myBlue <- matrix(0,500,500)
myBlue[150:250,200:500] <- 1
We could use any values between zero and one in these matrices.
Next, we combine them into a 3D array:
data <- array(c(myRed,myGreen,myBlue),dim = c(500,500,3))
We load the library ‘magick’, which we previously installed:
library('magick')
## Linking to ImageMagick 6.9.12.3
## Enabled features: cairo, fontconfig, freetype, heic, lcms, pango, raw, rsvg, webp
## Disabled features: fftw, ghostscript, x11
Then we can display the image and also save it in our working directory:
im <- image_read(data)
print(im) # this displays the image in RStudio and knitted documents
## format width height colorspace matte filesize density
## 1 PNG 500 500 sRGB FALSE 0 72x72
image_write(im,"mycolors.png") # this saves the image as a separate file
When you’re done, save it in this Google doc. I’ll print them (in color) and they can become name cards for our class.
Is runif or rnorm more useful?
You can do this by using exactly the same 2D matrix for all three color channels.
# before uncommenting, save an image to your working directory!
# this assumes the image is called "trees.png"
#a = image_read("trees.png")
#A = as.integer(a[[1]])
#dim(A)
#print(a)
Here is an example of a rank-one matrix, meaning it contains all possible products of entries in two vectors. Check that you understand how the entries in \(A\) come from the entries of \(a_1\) and \(a_2\) here:
a1 <- c(4,5,6,7)
print(a1)
## [1] 4 5 6 7
a2 <- c(1,0,-2)
print(a2)
## [1] 1 0 -2
A <- a1 %*% t(a2)
print(A)
## [,1] [,2] [,3]
## [1,] 4 0 -8
## [2,] 5 0 -10
## [3,] 6 0 -12
## [4,] 7 0 -14
Rank-one matrices are interesting because they contain lots of numbers, but not a lot of information (just the information contained in a pair of vectors). A low-rank matrix can be written as a sum of a small number of rank-one matrices. If you have a large data set, it is extremely wonderful if you can find a low-rank approximation for it. We will explore some methods for doing this later in the course.
Here is a much larger example of a rank-one matrix turned into an image:
# create an interesting vector: like a sine wave
t = (0:500) / 500 # this is a vector of 501 numbers ranging from 0 to 1
x = sin(3*pi*t^2)^2 * (0.85+0.15*runif(501))
y = sin(5*pi*t)^2 * (0.85+0.15*runif(501))
A = x %*% t(y) # creates a rank-one matrix from two vectors
data = array(c(A,A,A),c(501,501,3))
print(image_read(data)) # this displays the image in RStudio and knitted documents
## format width height colorspace matte filesize density
## 1 PNG 501 501 sRGB FALSE 0 72x72
Do you see how this image is related to the graphs of the vectors x and y?
plot(x)
plot(y)