vector (технічно atomic vector) у R - це набір значень одного й того самого 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 типово використовує індексацію з одиниці. Це звично для наукових мов (Fortran, Matlab, Julia), але відрізняється від індексації з нуля в C та багатьох його наступниках.
Діапазони можна використовувати, як у прикладі вище, а також у дуже гнучкій функції 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 при цьому не змінюється.
Якщо застосувати порівняння до вектора, отримаємо вектор булевих значень (англ. Boolean) такої самої довжини.
Це буває дуже зручно всередині таких функцій, як 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
Для складніших логічних виразів важливо використовувати оператори & та |, які працюють з векторами.
Двосимвольні && (логічне і) та || (логічне або) призначені лише для порівняння окремих значень, як ідеться в [концепції «Booleans»][concept-boolean].
v <- c(4, 7, 10)
v %% 2 == 0 & v > 5
#> [1] FALSE FALSE TRUE
До цієї теми ми ще повернемося в наступних розділах. А поки що звернімо увагу на формат вихідних даних у консолі:
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]?
У R немає справжніх скалярних значень!
Те, що має вигляд одного числа чи літери, насправді є вектором довжини 1.
Ми вже бачили натяки на це в концепціях «Basics» та «Booleans» із числовими та логічними типами.
Описані вище вектори формально називають atomic vectors. Річ у тім, що вони можуть містити лише прості, неподільні (звідси «атомарні») значення, як-от numeric, character, boolean. Складніші структури утворюють recursive vectors, про які йтиметься, коли ми розглянемо lists.