2.17 Other Atomic Vector Types

So far, we have covered the most commonly used atomic vector types, including logical, integer, numeric, and character, when we order by complexity. In this section, we will introduce two more atomic vector types: complex vectors and raw vectors.

2.17.1 Complex Vectors

Another atomic vector type in R is complex, which stores complex numbers. Let’s create a complex vector using the c() function.

my_complex <- c(1 + 2i, 3 + 4i, -3 - 4i)
my_complex
#> [1]  1+2i  3+4i -3-4i
class(my_complex)
#> [1] "complex"
typeof(my_complex)
#> [1] "complex"
str(my_complex)
#>  cplx [1:3] 1+2i 3+4i -3-4i

You can do operations on complex vectors. Like integers and doubles, the operations are performed componentwisely. Keep in mind that \(i \times i = -1\).

my_complex_2 <- c(2 + 1i,   2i, 2 + 3i)
my_complex + my_complex_2
#> [1]  3+3i  3+6i -1-1i
my_complex * my_complex_2
#> [1]  0+ 5i -8+ 6i  6-17i
my_complex / my_complex_2
#> [1]  0.800000+0.600000i  2.000000-1.500000i -1.384615+0.076923i

It is worth to emphasize that for complex vectors, is.numeric() returns FALSE and is.complex() returns TRUE.

is.numeric(my_complex)
#> [1] FALSE
is.complex(my_complex)
#> [1] TRUE

For a complex vector \(z = x + i y\) with real \(x\) and \(y\), we have the following table for variable components along with the functions in R.

Operation Explanation Values
Re(z) the real part \(x\)
Im(z) the imaginary part \(y\)
Mod(z) the modulus \(\sqrt{x^2 + y^2}\)
Arg(z) the argument \(arccos(\frac{x}{\sqrt{x^2 + y^2}})\)
Conj(z) the conjugate vector \(x - i y\)

Here, the modulus and argument are also called the polar coordinates. For \(r = Mod(z)\), and \(\phi = Arg(z)\), we have \(x = r*\cos(\phi)\) and \(y = r*\sin(\phi)\).

Now, let’s try these functions on our complex vector my_complex.

Re(my_complex)
#> [1]  1  3 -3
Im(my_complex)
#> [1]  2  4 -4
Mod(my_complex)
#> [1] 2.236068 5.000000 5.000000
Arg(my_complex)
#> [1]  1.1071487  0.9272952 -2.2142974
Conj(my_complex)
#> [1]  1-2i  3-4i -3+4i

Another way to create complex vectors is to use the complex() function where you can specify two out of the four arguments, namely the real part, the imaginary part, the modulus, and the argument. Usually, we use a combination of real and imaginary, or modulus and argument.

complex(real = 1:5, imaginary = 5:1)
#> [1] 1+5i 2+4i 3+3i 4+2i 5+1i
complex(modulus = 1:5, argument = 1:5)
#> [1]  0.5403023+0.841471i -0.8322937+1.818595i -2.9699775+0.423360i
#> [4] -2.6145745-3.027210i  1.4183109-4.794621i

Finally, you can use the vector(mode, length) function to create a complex vector of certain length.

vector("complex", 4)
#> [1] 0+0i 0+0i 0+0i 0+0i

Note that the default value is 0+0i.

2.17.2 Raw Vectors

The raw type of vector holds raw bytes. To create a raw vector, you can use the raw() function with the desired length as its argument, which will be initialized as a zero vector. You can check the class and storage type of the created object are indeed raw.

my_raw <- raw(2)
my_raw
#> [1] 00 00
class(my_raw)
#> [1] "raw"
typeof(my_raw)
#> [1] "raw"

Working with the raw vector type is not as easy as the other types we have covered so far. In particular, the implicit coercion doesn’t work here and leads to an error if you try to assign a number to a raw type vector.

my_raw[1] <- 20
#> Error in my_raw[1] <- 20: incompatible types (from double to raw) in subassignment type fix

You may be wondering, how do we modify the values of a raw type vector? To do that, you can use the as.raw() function with an integer vector as its argument.

as.raw(c(0:20, 135, 255))
#>  [1] 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 87 ff

It is worth to explain the mechanism of this explicit coercion. The input integer is converted to a length-two string-like value, taking the numeric values 0-9, and the lower case letters a-f. In fact, the representation can be viewed as a Hexadecimal System, which is the numbering system with base 16. The numeric values 0-9 coincide with the corresponding numbers 0-9, while the lower case letters a-f correspond to numbers 10-15. Let’s look at a few examples. \[\begin{align} 0b &= 0*16 + 11 = 11\\ 87 &= 8*16 + 7 = 135\\ ff &= 15*16 + 15 = 255 \end{align}\]

Note that when the input is less than 0 or larger than 255, the output will be always 00 with a warning displayed.

as.raw(-1)
#> Warning: out-of-range values treated as 0 in coercion to raw
#> [1] 00
as.raw(256)
#> Warning: out-of-range values treated as 0 in coercion to raw
#> [1] 00

Once you have a raw vector, you can use as.integer() to coerce it to an integer vector. In addition, you can use is.raw() to check whether the input is a raw vector.

my_raw[1] <- as.raw(200)
as.integer(my_raw)
#> [1] 200   0
is.raw(my_raw)
#> [1] TRUE
is.raw(200)
#> [1] FALSE

Interesting, the integer values 0-255 has a correspondence with the ASCII characters. However, note that the first 32 values correspond to ASCII control characters, which are not printable. Therefore, the most useful ones are values 32-255. For more details, you can find the corresponding table of the ASCII code and the characters via https://www.ascii-code.com/.

You can use the function charToRaw() to convert a string to a raw vector. And its inverse function rawToChar() will decode a raw vector into the corresponding string. This pair of operations can be viewed as an interesting example of encryption and decryption.

r02pro_str <- "I love r02pro!"
r02pro_raw <- charToRaw(r02pro_str)
r02pro_raw
#>  [1] 49 20 6c 6f 76 65 20 72 30 32 70 72 6f 21
rawToChar(r02pro_raw)
#> [1] "I love r02pro!"

For example, the character I corresponds to raw value 49, and number 111, the space corresponds to raw value 20, and number 32, and the ! corresponds to raw value 21, and number 33.

Now, let’s try something interesting.

rawToChar(as.raw(32:126))
#> [1] " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"

You can see that the numbers, lower and upper case letters, and commonly used symbols correspond to values from 32 to 126.

2.17.3 Full Coercion Ordering

In Section 2.4, we learned the following coercion rule: \[\mbox{logical} < \mbox{integer} < \mbox{double} < \mbox{character}\]

You may be wondering where do the complex and raw vectors lie in this sequence. Let’s first present the rule and show some examples.

\[\mbox{raw} < \mbox{logical} < \mbox{integer} < \mbox{double} < \mbox{complex} < \mbox{character}\]

Let’s mix my_raw with logical, integer, and character values, respectively.

c(my_raw, TRUE)
#> [1]  TRUE FALSE  TRUE
c(my_raw, 2L)
#> [1] 200   0   2
c(my_raw, "test")
#> [1] "c8"   "00"   "test"

Now, let’s mix my_complex with raw, logical, integer, and character, respectively.

c(my_complex, my_raw)
#> [1]   1+2i   3+4i  -3-4i 200+0i   0+0i
c(my_complex, TRUE)
#> [1]  1+2i  3+4i -3-4i  1+0i
c(my_complex, 8L)
#> [1]  1+2i  3+4i -3-4i  8+0i
c(my_complex, 3.14)
#> [1]  1.00+2i  3.00+4i -3.00-4i  3.14+0i
c(my_complex, "test")
#> [1] "1+2i"  "3+4i"  "-3-4i" "test"

2.17.4 Exercises

  1. Use complex vectors to verify Euler’s formula: \[e^{iy} = \cos(y) + i sin(y).\] Hint: for a sequence of y values (eg. y_seq <- pi *seq(from = -2, to = 2, by = 0.1)), compute the left hand side and right hand side respectively, and compare them.

  2. Tom wants to send out a message and he want to encypt it using raw vectors. He first convert the message into raw vectors, and then convert the raw vector into an integer vector. Suppose he got the following integer vector: 72, 97, 118, 101, 32, 97, 32, 110, 105, 99, 101, 32, 100, 97, 121, 33, please write R code to recover the message.

  3. Suppose we have the following vectors of different types.

my_raw <- as.raw(62)
my_logi <- TRUE
my_int <- 6L
my_complex <- 3 + 14i
my_char <- "Good luck!"

Guess the values of the following operations and confirm them in R.

c(c(my_raw, my_logi), my_int)
c(my_raw, c(my_logi, my_int))
c(c(my_raw, my_logi), my_char)
c(my_raw, c(my_logi, my_char))
c(c(my_raw, my_complex), my_char)
c(my_raw, my_complex, my_char)
c(c(my_raw, my_logi), c(my_int, my_complex), my_char)
c(c(my_raw, my_logi), my_int, my_complex, my_char)
c(my_raw, my_logi, my_int, my_complex, my_char)
c(my_raw, my_logi, c(my_int, my_complex), my_char)