2.17 Other Atomic Vector Types
So far, we have covered the most commonly used atomic vector types, including logical, integer, numeric, and character, which 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 + (0+2i), 3 + (0+4i), -3 - (0+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 + (0+1i), 0+2i, 2 + (0+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
.
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.
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.
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.
2.17.4 Exercises
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.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.
Suppose we have the following vectors of different types.
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)