向量

向量 在 R

1 個練習

關於 向量

R 裡的vector(嚴格來說是 atomic vector)是一組相同type的值:常見的有 integer、numeric、character、logical;少見的有 complex 或 raw。 它們在記憶體中是連續儲存的,類似於 C 的陣列。

如果輸入混合了不同的型別,R 會悄悄地把他們全部強制轉成同一種型別,通常是 character,這可能會導致意想不到的結果。

v <- c(2, 5.4, "txt")
v
#> [1] "2"   "5.4" "txt"
typeof(v)
#> [1] "character"

建立向量

如果你已經有所有的值,可以使用 c()(「concatenate」的縮寫):

x <- 10
v <- c(4, 7, x)

c() 函式非常有彈性,輸入可以包含其他向量和範圍。 c() 可以接受任意數量與種類的輸入,並將它們攤平成單一向量。

如果向量一開始很小,但在執行期間會大幅成長(例如在迴圈中加入值),預先配置一個長向量、之後再填入實際資料,速度可能會快上許多:

v <- vector(length = 100) 

這樣可以盡量減少複製物件:對長向量來說,複製既慢又耗費記憶體。

範圍、序列、重複

對一連串連續的值(遞增或遞減),可以使用 : 記法

v <- 1:5 # equivalent to c(1, 2, 3, 4, 5)
w <- 10:7 # c(10, 9, 8, 7)

在 R 中,範圍並不是一種獨立的型別,只是一種建構向量的簡寫方式。

seq() 函式提供更細緻的控制:

# specify step size
seq(2, 11, by = 3) # c(2, 5, 8, 11)

# specify number of values, evenly-spaced
seq(2.5, 4, length.out = 5) # c(2.500, 2.875, 3.250, 3.625, 4.000) 

rep() 函式可以輕鬆建立重複的模式:

count <- 3
rep(7, count)
#> [1] 7 7 7

rep(1:3, count)
#> [1] 1 2 3 1 2 3 1 2 3

rep(1:3, each = 2) # the "each" keyword is required in this case
#> [1] 1 1 2 2 3 3

有幾個範圍已經預先定義成內建常數:

letters  # LETTERS is the UPPERCASE equivalent
#> [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"

month.name
#> [1] "January"   "February"  "March"     "April"     "May"       "June"      "July"      "August"   
#> [9] "September" "October"   "November"  "December" 

month.abb
#> [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"

算術運算

運算會自動沿著向量逐元素套用。 其他語言常見的迴圈、列表推導式和遞迴在這裡並不需要,而且(基於效能考量)應該盡量避免。

2 + 1:3     # c(3, 4, 5)
2 * 1:3     # c(2, 4, 6)
12 / 1:3    # c(12  6  4)
2 ^ (1:3)   # c(2, 4, 8) : exponentiation, operator precedence order here needs ()

兩個向量成對也能運作,元素會逐對處理:

v <- 1:3
w <- 5:7

v + w
#> [1]  6  8 10

v * w
#> [1]  5 12 21

v ^ w
#> [1]    1   64 2187

這比乍看之下還要強大,之後的概念會再深入探索。

存取向量元素

和許多語言一樣,我們可以用括號中的索引來存取:

v <- 4:7
v[2]
#> [1] 5
v[2:4]
#> [1] 5 6 7

但要小心:R 預設使用從 1 開始的索引。 這在科學計算語言(Fortran、Matlab、Julia)中很常見,但和 C 及其眾多後繼語言的從 0 開始索引不同。

範圍可以像上面的例子那樣使用,也可以用在極具彈性的 c() 函式中:

v <- c(4, 7, 10)
w <- c(2, v[2:3], 11) # c(2, 7, 10, 11)

vectors(和 R 裡大多數東西一樣)可以被視為可變的,使用類似的彈性語法就能寫入單一或多個元素:

v <- 1:10
v[2] <- 42
v[4:6] <- c(11, 12, 13)
v
#> [1]  1 42  3 11 12 13  7  8  9 10

負索引

熟悉其他語言的程式設計師可能會猜 v[-1] 是用來存取向量最後一個元素的方式。 錯了! 那要用 tail()。

v <- c(2, 4, 6) # defaults to last 6 entries
tail(v)
#> [1] 2 4 6
tail(v, n=1) # just the last entry
#> [1] 6

負索引其實是用來移除元素的方式:

v <- c(2, 4, 6)
v[-2]
#> [1] 2 6  # second element gone

這會回傳一個較短的向量副本。v 本身不會改變。

向量條件判斷

對向量套用比較運算,會回傳一個長度相同、由布林值組成的向量。 這在 all() 和 any() 之類的函式裡非常方便:

v <- c(4, 7, 10)
v >= 6
#> [1] FALSE  TRUE  TRUE
all(v > 6)
#> [1] FALSE    # not all elements match this condition
any(v > 6)
#> [1] TRUE     # at least one element matches

對於比較複雜的邏輯運算式,重要的是使用 & 和 | 運算子,它們可以作用在向量上。 雙字元的 && 和 || 則只適用於比較單一值,這在[布林概念][concept-boolean]中有討論。

v <- c(4, 7, 10)
v %% 2 == 0 & v > 5
#> [1] FALSE FALSE  TRUE

向量在 R 中無所不在

之後的主題還會再回來談這個。 現在只要注意主控台輸出的格式:

1:20
#> [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17
#> [18] 18 19 20

每一行開頭的 [ ] 中的數字代表第一個元素的索引(對於會換行成多行的長向量很有用)。 但為什麼單一值前面會有 [1] 呢?

Note

R 裡沒有真正的純量值!

看起來像單一數字或字母的東西,實際上是一個長度為 1 的向量。

我們在基礎和布林這兩個概念中,關於數值和邏輯型別的內容已經看過一些端倪。

關於記法的一點說明

上面描述的向量,更正式的名稱是 atomic vectors。這是因為它們只能包含簡單、不可分割(因此稱為「atomic」)的值,例如 numeric、character、boolean。更複雜的結構則可以形成 recursive vectors,這會在討論 lists 時介紹。

透過 GitHub 編輯 連結會在新視窗或分頁中開啟

學習 向量