向量(vector)為 R 中最基本的資料形態,本章節將介紹向量的相關使用方式。
向量與賦值
在 R 中所有的資料都是以 R 特有的資料結構方式儲存,而最簡單的結構就是數值向量,下面的指令是設定數值向量 x 的內容為 10.4、5.6、3.1、6.4:
x <- c(10.4, 5.6, 3.1, 6.4)
這個命令呼叫函數 c() 建立一個向量並指定給 x,其中 <- 為 R 特有的指定運算子(assignment operator),在大多數的情況下可以用 = 取代,也就是這樣:
x = c(10.4, 5.6, 3.1, 6.4)
以上兩個命令是一樣的,但傳統上 R 的程式設計者習慣都是使用 <- 這種寫法。這裡的 c() 函數(combine)會把它所有的參數首尾相連、形成的一個向量,它可以傳入任意數量參數,而參數型態亦可是向量,例如
y <- c(x, 0, x)
會將兩個 x 向量與 0 連接起來,形成一個新的向量。
若要查看變數內容,直接在命令列輸入變數名稱,即可查看變數內容:
y
[1] 10.4 5.6 3.1 6.4 0.0 10.4 5.6 3.1 6.4
若是直接指定一個數值給變數,R 會把它當成只有一個元素的向量,也就是說以下兩個指令是一樣的
value <- 1.5
value <- c(1.5)
要存取向量的某個元素,則可使用中括號 [] 來指定元素的索引(index),索引以 1 為起始,例如 y 向量的第 2 個元素就是:
y[2]
[1] 5.6
如果要更改向量中的特定元素,也可以直接透過 [] 來更改:
y[3] <- 123.4
y
[1] 10.4 5.6 123.4 6.4 0.0 10.4 5.6 3.1 6.4
向量運算
在算術運算式中使用向量會對該向量的每一個元素逐一進行同樣算術運算,例如
x <- c(1, 2, 3)
y <- c(2, 3, 4)
x * y
[1] 2 6 12
在一個運算式中可能包含多個向量,而這些向量的長度如果不同時,較短的向量會重複自己本身直到與最長的向量長度相同才進行運算,例如:
x <- c(1, 2, 3, 4)
y <- c(5, 6)
x * y
[1] 5 12 15 24
在此運算式中,x 向量長度為 4,y 向量的長度為 2,當 x 與 y 相乘時,因為 y 向量的長度不足,R 會自動將 y 向量重複直到與 x 向量的長度相同為止,這裡的 y 向量重複兩次後變成 c(5, 6, 5, 6),再乘上 x 向量,得到結果為 c(5, 12, 15, 24)。
以下是參雜單一數值的運算,R 在碰到單一數值的時候,都會當成只有一個元素的向量:
x <- c(1, 2, 3)
2 * x + 3
[1] 5 7 9
如果一個運算式中包含長短不一的向量,而最長的向量長度又不是較短向量長度的整數倍,這時候比較短的向量在重複自己本身時,結尾就會有不完整的向量產生,例如:
x <- c(1, 2, 3, 4, 5)
y <- c(6, 7)
x * y
[1] 6 14 18 28 30
這裡的 y 向量會重複自己,直到跟 x 向量的長度相同,但是 x 向量的長度是 5,y 向量的長度是 2,因此 y 在重複處理之後就會產生 c(6, 7, 6, 7, 6)(最後一次的重複就會不完整),接著再乘上 x,然後得出結果。一般來說這樣的運算不會是我們預期的,所以 R 在遇到類似的狀況時,就會產生警告:
Warning message: In x * y : 較長的物件長度並非較短物件長度的倍數
運算子
以下是在 R 中常見的算術運算子。
四則運算
四則運算的運算子有:加 +、減 -、乘 *、除 /。
1 + 2 * 3 / 4
[1] 2.5
冪運算
冪運算的運算子為 ^,例如 2 的 4 次方為:
2 ^ 4
[1] 16
餘數
餘數的運算子為 %%,例如:
5 %% 2
[1] 1
整數除法
整數除法的運算子為 %/%,例如:
5 %/% 2
[1] 2
另外還有一些常用的數學函數,如 log()、 exp()、 sin()、 cos()、 tan()、 sqrt() 等等。
除此之外還有些特別的函數:
max(x):計算向量x的最大值。min(x):計算向量x的最小值。range(x):計算向量x的範圍,即c(min(x), max(x))。length(x):計算向量x的元素個數。sum(x):計算向量x的元素總和。prod(x):計算向量x的元素乘積。sort(x):傳回一個將向量x排序之後的新向量。
以下是一些簡單的統計相關函數:
mean(x):計算向量x的平均值。var(x):計算向量x的樣本變異數(variance),即sum((x-mean(x))^2)/(length(x)-1)。sd(x):計算向量x的樣本標準差(standard deviation),即sqrt(var(x))。
序列的產生
R 有一系列的方式可產生常用的序列(sequence),例如要產生 c(1, 2, 3, ..., 100),則可使用冒號運算子(:):
seq <- 1:100
以下是一些相關的用法,讀者可以比較其中的差異。
seq1 <- 30:1
seq2 <- 1:10 * 2 + 1
n <- 10
seq3 <- 1:n-1
seq3 <- 1:(n-1)
seq() 函數是在產生數列時最為常用的工具,前兩個參數分別指定所產生數列的首尾,如果只是給定這兩個值,則和冒號運運算子的結果完全一樣,例如:seq(2, 10) 就等同於 2:10。
seq() 的參數和其他許多 R 函數的參數一樣都可以用指定名稱的方式給定,在這情況下,參數的順序可以是任意改變的,所以前兩個參數就可以用 from=value 與 to=value 的方式設定,因此以下幾種寫法是完全一樣的:
seq(1, 30)
seq(from = 1, to = 30)
seq(to = 30, from = 1)
1:30
seq() 接下來的兩個參數是 by=value 與 length=value,它們分別表示這個序列的步長和長度,如果二者都沒有設定,那麼預設值就是 by=1(步長為 1)。例如
seq1 <- seq(-5, 5, by = 0.2)
所得到的 seq1 為 c(-5.0, -4.8, -4.6, ..., 4.6, 4.8, 5.0),類似的用法
seq2 <- seq(length = 51, from = -5, by = 0.2)
得到的 seq2 與 seq1 相同。
還有一個相關的函數是 rep()。它可以用各種複雜的方式重複一個物件,簡單的方式就是
rep1 <- rep(x, times = 3)
這種方式會先把 x 複製三次並保持 x 的序列順序,逐一放在 rep1 中,得到 c(1, 2, 3, 1, 2, 3, 1, 2, 3)。另一種方式是
rep2 <- rep(x, each = 3)
這是把 x 中的每個元素都重複三次,然後將重複三次的元素逐一放入,得到 c(1, 1, 1, 2, 2, 2, 3, 3, 3)。
邏輯向量
邏輯向量(logical vector)就是由邏輯元素所構成的向量,每個元素可以被賦予的值有 TRUE、FALSE 與 NA(見下一小節)。
TRUE與FALSE可以分別簡寫為T和F,但要注意的是 R 對T和F的處理方式只是預設將他們設為TRUE和FALSE的變數,它們不是系統保留字(reserved word),可以被使用者複寫,因此建議使用者儘量使用TRUE和FALSE,避免產生不必要的困擾。
指定一個邏輯向量的方式與一般數值向量一樣是使用 c(),例如:
b <- c(TRUE, FALSE, TRUE)
邏輯向量亦可以由條件式(conditions)產生,例如:
x <- c(1, 2, 3, 4)
bseq <- x < 3
得到的 bseq 是一個長度與 x 一致的向量,其中每個元素就是 x 向量中每個元素經過邏輯運算的結果,亦即 c(TRUE, TRUE, FALSE, FALSE)。
R 的比較運算子包含:
<:小於。<=:小於或等於。>:大於。>=:大於或等於。==:等於。!=:不等於。
假設 b1 與 b2 是兩個邏輯向量,以下是 R 的邏輯運算子與使用方式:
&:AND 邏輯運算子,如b1 & b2。|:OR 邏輯運算子,如b1 | b2。!:NOT 邏輯運算子,如!b1。
若將邏輯變數使用於一般的算術運算中,邏輯變數將會被強制轉換成數值變數,FALSE 轉換為 0,而 TRUE 轉換為 1。
遺失值
在某些情況下,向量的某些元素有可能無法得知,當一個元素或是數值在統計上無法獲得(not available)或是遺失(missing value)的時候,則此元素或數值就會以一個特定的值 NA 來表示,任何含有 NA 資料的運算結果都將是 NA,會這樣假設的道理很簡單,如果計算用的資料都是未知的,那麼結果也必然是不可預期的,因此也是不可得到的。
is.na(x) 函數返回一個和 x 同樣長度的邏輯向量,若其某個元素值為 TRUE 則表示在 x 中對應元素為 NA。
z <- c(1:3, NA)
ind <- is.na(z)
這裡特別要注意的一點,邏輯運算式
x == NA與is.na(x)是完全不同的,因為NA不是一個真實的值,而是一個表示某個資料值無法得知的符號,因此x == NA得到的是一個長度和x一樣的向量,而它的所有元素的值都是NA,因為該邏輯運算式本身就包含NA,所以運算結果也都是NA。
另外還要注意數值計算會產生第二種遺失值,也稱為非數值(Not a Number)NaN,例如:
0/0
或者
Inf - Inf
二者都得到 NaN,這是因為它們的結果都無法定義。其中 Inf 代表數學上的無窮大。
若用 is.na() 檢查 NA 與 NaN 的話,結果都會是 TRUE,若需要區分它們,可以使用 is.nan() 來檢查,is.nan() 只有對 NaN 元素會產生 TRUE。
字元向量
字元向量(character vector)是由字元所構成的向量,而字元的表示方式是以雙引號夾著一序列的字元,例如 "x-values"、"New iteration results",要指定一個字元向量也是使用 c() 函數:
str <- c("string 1", "string 2")
在 R 中常常會用到字元向量,例如繪圖時的標題或註解等。
字串輸入的時候也可以用單引號,所以也可以這樣寫:
str <- c('string 1', 'string 2')
但是 R 在輸出的時候則採用雙引號(或者有時不用引號)。
R 對字元的的處理採用 C 語言形式的跳脫序列(escape sequences),因此使用者可以透過跳脫序列輸入一些特殊字元,例如:
\n:換行字元。\t:Tab 字元。\b:退位鍵(backspace)。\":雙引號(")。\\:反斜線(\)。
在 R 中所謂的字元事實上就等於在一般語言中的字串(string),也就是說以下這些在 R 中都稱為字元(character):
"":空字元。"a":包含一個字母的字元。"string":包含多個字母的字元。"Hello World!\n":包含轉義控制序列的字元。
在處理字元時最常用的函數之一就是 paste(),它可以傳入任意多的參數,然後將每個參數轉換成字元,並且把它們一個接一個地串接起來,例如:
x <- 10
str <- paste("x=", x)
paste() 在串接字元時預設使用的分隔符號是一個空白字元,若要指定分隔符號可以使用 sep="string" 參數,例如:
x <- 10
str <- paste("x", x, sep = "-")
sep 也可以指定為空字元,例如:
labs <- paste(c("X", "Y"), 1:10, sep = "")
這裡較短的向量 c("X", "Y") 重複了五次以符合向量 1:10 的長度,得到的 labs 為 c("X1", "Y2", "X3", "Y4", "X5", "Y6", "X7", "Y8", "X9", "Y10")。
索引向量
若要存取向量的某個元素,可透過中括號([])使用索引向量(index vector)的方式存取,索引向量的使用方式有四種,以下分別介紹各種使用方式。
邏輯向量
這種情況下,索引向量與被挑選元素向量的長度必須一致,被挑選元素向量中對應索引向量為 TRUE 的元素將會被選出,而那些對應 FALSE 的元素則被忽略。例如:
x <- c(1, 2, NA,4)
y <- x[!is.na(x)]
這會將 x 向量中非 NA 的元素選出來,依照原本的順序指定給 y,若 x 中包含 NA 元素,則所得到的 y 向量長度就會比 x 向量的長度短。以下是另一個較複雜的例子:
x <- c(0, -1, -2, NA, 4, 6)
x <- x + 1
z <- x[(!is.na(x)) & x > ]
這個例子首先把 x 每個元素都加一,然後選出非 NA 且大於 0 的元素,依照原本的順序指定給 z。
正整數向量
這種情況下,索引向量中的每個元素必須是 {1, 2, ..., length(x)} 的其中一個(length(x) 為向量 x 的長度),索引向量中索引對應的元素將會被選出,並且依照索引向量中的順序傳回,這種索引向量的長度沒有限制,所選出的向量長度會與索引向量的長度相同, 例如:
x[6]
表示 x 的第 6 個元素,此外
x <- 10:20
y <- x[1:5]
z <- x[ c(1, 2, 3, 1, 2, 3) ]
所得到的 y 為 x 的前 5 個元素,而 z 則為 c(10, 11, 12, 10, 11, 12)。更複雜的例子:
labs <- c("x", "y")[rep(c(1, 2, 2, 1), times = 4)]
這樣會產生一個長度為 16、由 "x", "y", "y", "x" 重複四次而構成的向量。rep() 為重複某個向量的函數。
負整數向量
這種索引向量與正整數向量規則一樣,負數的意思是將指定的元素排除,將剩餘的元素選出,例如:
x <- 10:20
y <- x[-(1:5)]
所得到的 y 向量為 c(15, 16, 17, 18, 19, 20)。
字串向量
這種索引向量只能用在可以用 names 屬性區別其元素的向量,在使用之前必須以 names() 函數先設定 names 屬性,例如:
fruit <- c(5, 10, 1, 20)
names(fruit) <- c("orange", "banana", "apple", "peach")
lunch <- fruit["apple"]
使用字串向量的好處就是容易記,這在使用 data frames 的時候效果比較明顯。
索引運算式也可以用在指定向量的元素上,在這種情況下只有那些被索引向量指定的元素會被更動,例如:
x <- c(1, 2, NA, NA, 3)
x[is.na(x)] <- 0
這樣只會將 x 向量中的 NA 元素設為 0,其餘的元素不變。而
y <- c(-1, -2, 0, 1, 2)
y[y < 0] <- -y[y < 0]
是將 y 取絕對值,與下面這個作法相同
y <- c(-1, -2, 0, 1, 2)
y <- abs(y)