Треки
/
R
R
/
Салабус
/
Вектори
Ве

Вектори у R

1 вправа

Про концепцію Вектори

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

Вектори скрізь у 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.

Ми вже бачили натяки на це в концепціях «Basics» та «Booleans» із числовими та логічними типами.

Кілька слів про позначення

Описані вище вектори формально називають atomic vectors. Річ у тім, що вони можуть містити лише прості, неподільні (звідси «атомарні») значення, як-от numeric, character, boolean. Складніші структури утворюють recursive vectors, про які йтиметься, коли ми розглянемо lists.

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

Вивчити концепцію Вектори