← |
Д. Шабанов, М. Кравченко. «Статистичний оракул»: аналіз даних в зоології та екології |
→ |
||
Робота з даними в R |
||||
«Статистичний оракул»-04 |
«Статистичний оракул»-06 |
Робота з даними в R
5.1. Логічні та арифметичні операції в R
Для непідготовленого читача текст цього розділу нашого підручнику може здатися непридатним для читання: він буде переважно складатися з R-діалогів. Так, спочатку, до отримання досвіду, читати такі діалоги важко. Але сенс нашого курсу саме в тому, щоб навчити студентів працювати з R. Погляд досвідченого R-користувача під час роботи з R швидко вихоплює команди та відповіді на них системи. Саме це показано в R-діалогах в нашому підручнику. Шановні читачі, коли ваші очі (а насправді — ваш мозок) навчиться легко читати діалоги з R, можна буде зробити висновок, що ви суттєво просунулися у оволодінні цією мовою.
До речі, як розібратися в таких R-діалогах, які тут наведені, краще? Один зі шляхів (у разі роботи з електронним підручником) — виділити та скопіювати текст з рамки. Викинути відповіді системи на введені команди (рядки з ними не починаються зі знаку >) — залишаться самі команди. Викинути знаки > на початку строки — їх вставив RStudio, коли демонстрував у консолі, що отримав ці вирази як команди. Вставити залишок в редактор скриптів RStudio, виділити потрібне та нажати Ctrl+Enter або кнопку Run (зелену стрілку). Вийшло так само, як у нашому прикладі? Що можна змінити? Як застосувати це до проблеми, яку ви вирішуєте?
Ми розглянемо ще кілька способів роботи з фреймом PelophylaxExample, а потім перейдемо до більш послідовного огляду можливостей R. У певному сенсі, ми використовуємо «перевернуту» логіку знайомства з R. Ми почали з прикладу на рис. 3.2.1, де показали, як R читає файл з даними, проводить розрахунки, створює і зберігає графіки — і усе це просто завдяки реалізації досить простого скрипту. Після простого прикладу з даними, що уведені просто в вікні R, ми створили фрейм PelophylaxExample і розглянули деякі засоби роботи з ним. Це стало приводом обговорити типи об'єктів і даних в R. Далі буде логічно більш послідовно пройти по можливостях R та розглянути логічні та арифметичні операції, а також деякі особливості векторів, та після цього застосувати їх до роботи з нашим фреймом. Автори сподіваються, що така «перевернута» логіка знайомства з R зробить більш зрозумілим, навіщо можна використовувати ті або інші особливості цього потужного засобу дослідження даних. Така логіка не підходить для довідника, але, сподіваємося, полегшить знайомство з R у разі, якщо студент послідовно пройде пункт за пунктом і тему за темою даного підручника.
Для роботи з файлами даних важливо навчитися застосовувати логічні конструкції. Їх приклади показані в R-діалозі 5.1.1. Забігаючи наперед, вкажемо, що логічні конструкції, наприклад, допомагають вибирати дані, що відповідають певним умовам.
R-діалог 5.1.1. Приклади логічних конструкцій в R
> a <- 2; b <- 5 # Створюємо два об'єкти > a < b # Відношення менше - більше [1] TRUE > a > b [1] FALSE > a <= b [1] TRUE > # Якщо написати a = b, R перевизначить a за b (сприйме як a <- b) > a == b # Перевірка рівності ==, нерівності != [1] FALSE > a != b [1] TRUE > # Знак & означає "водночас", а | — "або", "хоча б один з": > a & b < 4 [1] FALSE > a & b <= 5 # «a та b більше або дорівнює 5» [1] TRUE > a | b == 2 # «хоча б одне з a та b дорівнює 2» [1] TRUE > a<3 & b>3 # Поєднання двох умов [1] TRUE > a<3 & b>5 [1] FALSE > |
І при використанні логічних умов, і, звісно, в безлічі інших ситуацій виконуються арифметичні операції. Деякі з них тривіальні, деякі потребують окремого згадування (R-діалог 5.1.2).
R-діалог 5.1.2. Деякі арифметичні операції: залишки та цілі частини при діленні, піднесення до ступеня, логарифми
> a <- 2; b <- 5 # Створюємо два об'єкти > b %% a # Залишок від ділення [1] 1 > b %/% a # Ціла частина при діленні [1] 2 > a ** b # Піднесення до ступеня (можна також ^) [1] 32 > sqrt(4) # Квадратний корінь [1] 2 > 1000 ** (1/3) [1] 10 > round(a/b) [1] 0 > round(a/b, 2) # Округлення до вказаної кількості знаків [1] 0.4 > abs(a - b) # Абсолютне значення [1] 3 > log(2) # За замовченням рахуються натуральні логарифми (основа — е, число Ейлера) [1] 0.6931472 > log10(1000) # Десятковий логарифм [1] 3 > log(4, base = 2) # Логарифм за вказаною основою [1] 2 > |
5.2. Пропущені значення
У мові R використовується специфічне позначення NA, яке використовується для пропущених значень. Згадайте стовпці з вимірами частин тіла жаб у файлі PelophylaxExamples. В таблиці 2.2.1 нема незаповнених комірок. Але, припустимо, певне значення нам невідомо. Що робити? Ні у якому разі не використовувати значення 0: це буде означати, що ми виміряли певну частину тіла та зареєстрували її нульову довжину. Слід показати, що це — саме відсутність інформації. Найпростіше рішення — залишити відповідну комірку таблиці даних пустою. В R цьому відповідає розміщення у цій «комірці» позначення NA — пропущених даних. До речі, NA — не текстове значення, брати його позначення у лапки не треба. Це — саме відсутність значень.
Ми можемо прочитати файл PelophylaxExamples.csv так, щоб створити файл з пропущеними значеннями. Використаємо для цього аргумент na.strings до функції read.csv(). Створимо для цього іншій об'єкт, ніж наш основний фрейм PE. Наприклад, якщо ми використаємо команду PE1 <- read.csv('PelophylaxExamples.csv', sep = ";", dec = ",", na.strings = '"Dnipro"), функція читання файлів викине згадування про басейн Дніпра. Втім, цікавіше буде, якщо ми втратимо якісь числові значення. Наприклад, викинемо значення 42, що зустрічається у файлі двічі: це довжина вторинної голені у двох жаб, одна з яких — перша (R-діалог 5.2.1).
До речі, якщо ви читаєте *.csv-файл, де є пусті комірки, слід пояснити R, що ці комірки слід розуміти як NA: DATAFRAMENAME <- read.csv('FILENAME', sep = "SIGN", dec = "SIGN", na.strings = '"").
R-діалог 5.2.1. Пропущені значення та деякі операції з ними
> # Штучно створюється фрейм, що містить пропущені значення: > PE1 <- read.csv('PelophylaxExamples.csv', sep = ";", dec = ",", na.strings = 42) > str(PE1$Ci) int [1:57] NA 41 37 38 45 37 37 34 31 33 ... > complete.cases(PE1) # Функція, що відбирає рядки, в яких нема пропущених значень: [1] FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE [15] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE [29] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE TRUE TRUE [33] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE [47] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE > sum(complete.cases(PE1)) # Можна порахувати, адже логічному значенню TRUE відповідає 1 [1] 55 > sum(!complete.cases(PE1)) # Підрахунок рядків, які не є повними [1] 2 > sum(is.na(PE1)) # А це — просто підрахунок кількості пропущених значень у файлі: [1] 2 > mean(PE1$Ci) # Розрахувати середнє (і багато іншого) з NA неможливо: [1] NA > # Але можливо додати аргумент (від NA remove), що примушує ігнорувати пропущені значення: > mean(PE1$Ci, na.rm = TRUE) [1] 37.65455 > |
Зверніть увагу: команда sum(is.na(OBJECT)) підраховує кількість пропущених значень, а sum(!complete.cases(OBJECT)) — кількість рядків, в яких є відсутні значення. Чи завжди команди sum(!complete.cases(OBJECT)) і sum(is.na(OBJECT)) дадуть однаковий результат? Ні. У разі, якщо хоча б в одному рядку є більш, ніж одне пропущене значення, кількість неповних рядків буде меншою за загальну кількість пропущених значень.
А як загалом убрати з фрейму PE1 рядки, де є пропущені значення? Це стане зрозуміло пізніше (R-діалог 5.5.2)...
5.3. Вектори, їх створення та вибір певних елементів
Як ви пам'ятаєте, вектор є базовим для R типом об'єктів. Вектор — це об'єкт, що має ім'я та містить впорядкований набір даних певного типу (навіть якщо цей набір складається з одного елементу). На прикладі векторів можна пояснити властивості об'єктів та функцій R, що допоможуть при роботі і з іншими об'єктами. Ми вже використовували функцію c(), що поєднує об'єкти у вектор.
R-діалог 5.3.1. Створення векторів і деякі операції з ними
> # Короткі команди можна записувати в один рядок через кому з крапкою > # Щоб вивести в консолі створений елемент, можна узяти команду у дужки > # Створимо вектор за допомогою команди rep(), яка повторює певний зміст потрібну кількість разів, поєднаємо з іншим вектором, об'єднаємо їх та виведемо у консоль — і усе це у один рядок > re <- rep(c(1, 0, NA), 2); ad <- c(1, 2, 3, 4); (v <- c(re, ad)) [1] 1 0 NA 1 0 NA 1 2 3 4 > unique(v) # Визначимо унікальні елементи вектора v [1] 1 0 NA 2 3 4 > table(v) # Частоти елементів вектора v v 0 1 2 3 4 2 3 1 1 1 > v1 <- v + 1 > # Можна виконувати аріфметичні операції > v1 # Елементи NA при цьому не змінюються: [1] 2 1 NA 2 1 NA 2 3 4 5 > (v2 <- v * 2) [1] 2 0 NA 2 0 NA 2 4 6 8 > v2 > 5 # Можна виконувати логічні операції [1] FALSE FALSE NA FALSE FALSE NA FALSE FALSE TRUE TRUE > v2[v2 > 5] # Можна вибірати елементи завдяки логічним операціям [1] NA NA 6 8 > v2[v2 > 5 & v2 < 7] # Можна поєднувати різні операції [1] NA NA 6 > |
Як можете побачити, команда rep() забезпечує повторення певної послідовності. Крім неї, для створення векторів може бути корисною команда seq(), що генерує певну арифметичну послідовність. Наприклад, команда se <- seq(from = 1, to = 24, by = 2), або тотожна їй більш скорочена команда se <- seq(1, 24, 2), створюють вектор se, який починається з 1 і складається з елементів, кожен з яких більше попереднього на 2 (останнім буде значення 23, зрозуміло, чому?).
Функція unicue() убирає повтори та лишає унікальні значення; функція table() виводить розподіл значень в об'єкті, який вона аналізує.
Основні характеристики вектора — його тип mode() та довжина lenght(). За допомогою функції lenght() можна не лише узнати довжину вектора, а й змінити її. Команда length(VECTOR) <- 10 змінить довжину вектора до вказаної кількості елементів (якщо їх було більше — відріже зайві; якщо було замало — додасть NA). З використанням вектора v, що було створено в R-діалозі 5.3.1, розглянемо ще деякі способи роботи з векторами (R-діалог 5.3.2).
R-діалог 5.3.2. Індексація векторів та позбавлення від NA
> v [1] 1 0 NA 1 0 NA 1 2 3 4 > v[2] # Щоб отримати елемент вектору, треба вказати номер цього елементу: [1] 0 > v[2:4] # Щоб отримати кілька елементів потрібен вектор (2:4 — це вектор!): [1] 0 NA 1 > # Команда v[2, 5, 11] виконана не буде; R сприйме 2, 5 і 11 як координати > v[c(2, 5, 11)] # Можна створити вектор функцією c() [1] 0 0 NA > v%%2 == 1 # Можна використовувати логічні конструкції [1] TRUE FALSE NA TRUE FALSE NA TRUE FALSE TRUE FALSE > is.na(v) # Така конструкція вибирає пропущені значення [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE > (v3 <- v[!is.na(v)]) # А така — убирає пропущені значення [1] 1 0 1 0 1 2 3 4 > |
У попередньому пункті ми обговорювали пропущені значення на прикладі фрейма PE1. Чи можна убрати з нього пропущені рядки таким же чином, як із вектора? Ні, оскільки фрейм є більш складним об'єктом. Як це зробити, стане зрозумілим після того, як ми обговоримо роботу з об'єктами, що мають більше вимірів, ніж один.
Створені вектори, природно, можна редагувати. У попередньому R-діалозі ми створили вектор v3. Змінимо його (R-діалог 5.3.3).
R-діалог 5.3.3. Додавання та убирання елементів з вектора; найменьші, найбільші, повторювані значення
> v3 [1] 1 0 1 0 1 2 3 4 > ap <- c(8, 8, 2) # Те, що додається до вектора, слід перетворити у інший вектор > (v4 <- append(v3, ap)) # Завдяки дужкам створений вектор виводиться на консоль [1] 1 0 1 0 1 2 3 4 8 8 2 > rem <- c(2:5, 10) # Щоб щось убрати, також слід створити вектор > (v5 <- v4[-rem]) [1] 1 2 3 4 8 2 > which.max(v3) # Вказує номер найбільшого елементу у векторі [1] 8 > which.min(v3) # Вказує номер найменшого елементу у векторі [1] 2 > v3[c(7, 3, 5, 2, 7, 7, 7)] # Задає (вектором!) порядок виводу елементів [1] 3 1 1 0 3 3 3 > v3[9:1] # Номеру відсутнього елемента відповідатиме NA [1] NA 4 3 2 1 0 1 0 1 > (v3[c(-2, -3, -5)]) # Від'ємні індекси — спосіб видалити елемент [1] 1 0 2 3 4 > duplicated(v4) # Пошук значень, що повторюються: [1] FALSE FALSE TRUE TRUE TRUE FALSE FALSE FALSE FALSE TRUE TRUE > (v5 <- v4[!duplicated(v4)]) # Повторювані значення можна убрати [1] 1 0 2 3 4 8 > v6 <- unique(v4) # Інший спосіб зробити те ж саме > v5 == v6 # Перевірка [1] TRUE TRUE TRUE TRUE TRUE TRUE > |
У тому разі, якщо значення у векторі повторюються, функції which.max() та which.min() вкажуть номер першого з найбільшіх або найменших значень. Приклад цього можна побачити у R-діалозі 5.3.3 у випадку застосування функції which.min(): найменше значення, 0, розташовано у векторі v3 на другій та четвертій позиції; відповіддю на команду which.min(v3) є 2.
У багатьох випадках корисними є різні засоби сортування елементів (R-діалог 5.3.4).
R-діалог 5.3.4. Різні способи сортування, впорядкування та ранжування елементів
> v5 # Будемо екпериментувати з цим вектором (R-діалог 5.3.3) [1] 1 2 3 4 8 2 > (v6 <- order(v5)) # Вказує номери елементів у векторі у порядку збільшення [1] 1 2 6 3 4 5 > (v7 <- order(v5, decreasing = TRUE)) # А з таким атрибутом — у порядку зменшення [1] 5 4 3 2 6 1 > (v8 <- sort(v5)) # Сортує елементи вектора у порядку збільшення [1] 1 2 2 3 4 8 > (v9 <- v5[order(v5)]) # Інакше з тим самим результатом, що у попередньому випадку [1] 1 2 2 3 4 8 > (v10 <- rank(v5)) # Вказує ранг елементів; для повторюваних елементів ранг є нецілим числом [1] 1.0 2.5 4.0 5.0 6.0 2.5 > (v11 <- rank(-v5)) # Зворотний порядок ранжування [1] 6.0 4.5 3.0 2.0 1.0 4.5 > |
Порівняйте функції order() та rank(). У разі, якщо у векторі нема повторюваних елементів, вони дають однаковий результат; якщо такі елементи є, вони будуть оброблені по-різному.
У разі, якщо вектор (або інший об'єкт) є текстовим, для редагування можна використовувати функції sub та gsub. Якщо застосувати ці функції до числового вектора, він перетвориться на текстовий, але ніщо не заважає зробити його знову числовим (R-діалог 5.3.5).
R-діалог 5.3.5. Приклади заміни символів у векторі
> # Заміна символів в текстових об'єктах: > (char_obj <- c("comp1", "comp2", "comp3", "comp4")) [1] "comp1" "comp2" "comp3" "comp4" > (char_obj1 <- sub("comp", "comp_", char_obj)) [1] "comp_1" "comp_2" "comp_3" "comp_4" > # Команда sub замінює перший символ, gsub — усі > (char_obj2 <- gsub("comp", "comp_", char_obj)) [1] "comp_1" "comp_2" "comp_3" "comp_4" > (num_obj <- 1:5) [1] 1 2 3 4 5 > (num_obj1 <- sub("2", "3", num_obj)) [1] "1" "3" "3" "4" "5" > # Об'єкт num_obj1 став текстовим, його слід перетворити на числовий! > (num_obj1 <- as.numeric(num_obj1)) [1] 1 3 3 4 5 > # До усіх елементів додано те ж саме; це вже у числа не перетворити: > (num_obj <- paste0(num_obj, "-th")) [1] "1-th" "2-th" "3-th" "4-th" "5-th" > |
5.4. Імена рядків та стовпців у матрицях та фреймах
Ми будемо працювати з даними, що розміщені у певному порядку. В векторах цей порядок лінійний. В двомірних матрицях та фреймах даних розташування певного значення задається двома координатами — рядком та стовпцем. У багатомірних масивах таких вимірів більше (і нема звичних понять для їх позначення). Почнемо з матриць, які, фактично, є двовимірними векторами (R-діалог 5.4.1):
R-діалог 5.4.1. Приклади роботи з іменами рядків та стовпців в матриці
> ma <- 1:28 # Створюємо вектор > dim(ma) <- c(4, 7) # Вказуємо розмірність матриці з вектора > ma # Дивимось, що отримали [,1] [,2] [,3] [,4] [,5] [,6] [,7] [1,] 1 5 9 13 17 21 25 [2,] 2 6 10 14 18 22 26 [3,] 3 7 11 15 19 23 27 [4,] 4 8 12 16 20 24 28 > colnames(ma) # Ця матриця ще не має назв рядків та стовпців NULL > rownames(ma) NULL > # Ці назви можна задати: > (colnames(ma) <- c("One", "Two", "Three", "Four", "Five", "Six", "Seven")) [1] "One" "Two" "Three" "Four" "Five" "Six" "Seven" > (rownames(ma) <- c("First", "Second", "Third", "Fourth")) [1] "First" "Second" "Third" "Fourth" > dimnames(ma) # Дивимось назви рядків та стовпців водночас 1 [1] "First" "Second" "Third" "Fourth" 2 [1] "One" "Two" "Three" "Four" "Five" "Six" "Seven" > ma # Тепер матриця виглядає так One Two Three Four Five Six Seven First 1 5 9 13 17 21 25 Second 2 6 10 14 18 22 26 Third 3 7 11 15 19 23 27 Fourth 4 8 12 16 20 24 28 > |
Фрейм даних можна порівняти зі списком з відносно незалежних (таких, що можуть містити різні дані) векторів (R-діалог 5.4.2):
R-діалог 5.4.2. Приклади роботи з іменами рядків та стовпців в фреймі
> df1 <- c("One", "Two", "Three", "Four", "Five", "Six", "Seven") > df2 <- 8:14 ; df3 <- 15:21; df4 <- 22:28 > (df <- data.frame(df1, df2, df3, df4)) # Створили фрейм df1 df2 df3 df4 1 One 8 15 22 2 Two 9 16 23 3 Three 10 17 24 4 Four 11 18 25 5 Five 12 19 26 6 Six 13 20 27 7 Seven 14 21 28 > dimnames(df) # Імена рядків та стовпців 1 [1] "1" "2" "3" "4" "5" "6" "7" 2 [1] "First" "Second" "Third" "Fourth" > df # За необхідності можна використати colnames() та rownames() First Second Third Fourth 1 One 8 15 22 2 Two 9 16 23 3 Three 10 17 24 4 Four 11 18 25 5 Five 12 19 26 6 Six 13 20 27 7 Seven 14 21 28 > |
5.5. Індексація даних на прикладі фрейму PelophylaxExample
Індексацією в R (та у подібних випадках) називається звертання до певних частин об'єкту. Індекси (способи вибирати певну частину даних) можуть бути числовими, логічними або текстовими. Для статистичного аналізу досить часто необхідно отримувати та порівнювати один з одним різні фрагменти повного фрейму. Для цього необхідні способи індексації, що показані у R-діалозі 5.5.1.
R-діалог 5.5.1.
> PE <- read.csv('PelophylaxExamples.csv', sep = ";", dec = ",") > PE[1, 6] # У прямих дужках: спочатку рядок, потім стовпчик [1] female # У випадку фактора будуть вказано його значення та рівні: Levels: female male > PE[1, 9] # У випадку числової змінної буде вказано просто значення: [1] 603 > PE[2, ] # Якщо не вказувати координату, буде обрано усі значення X Place East North Basin Sex DNA Genotyp L Ltc 2 LL_f_562 Chernetchina 35.13 50.05 Dnipro female 13.95 LL 562 187 Fm T Dp Ci Cs 2 266 249 62 41 152 > PE[2:5, c(9, 11, 13)] # Задати координати можна векторами з номерами елементів L Fm Dp 2 562 266 62 3 592 281 79 4 595 285 75 5 602 287 80 > PE[10, c('Sex', 'Genotyp')] # Можна створити вектор з іменами змінних або рядків Sex Genotyp 10 female LLR > PE[which.max(PE$L), 'L'] # Найбільша зареєстрована у фреймі довжина тіла (L) [1] 930 > max(PE$L) == PE[which.max(PE$L), 'L'] # Простіший спосіб отримати максимум [1] TRUE > PE[which.min(PE$L), 'L'] # Найменше значення змінної L [1] 479 > PE[PE$Place == 'Zamulivka', c(6, 8)] # Можна використовувати умови! Sex Genotyp 14 female LLR 15 female LLR 56 male RR > PE_m <- subset(PE, Sex=="male") # Можна вибрати лише частину даних > # Можна використовувати досить складні логічні конструкції: > PE_f_2n <- subset(PE, Sex=="female" & (Genotyp=="LL" | Genotyp=="LR"| Genotyp=="RR")) > PE_f_2n[c(4, 8, 12), c(1, 2, 6, 8)] # Подивимось, що вийшло: X Place Sex Genotyp 5 LL_f_602 Chernetchina female LL 24 LR_f_877 SuhaGomol`sha female LR 46 RR_f_701 Lipci female RR > |
У R-діалозі 5.3.3 пояснено команди which.max() та which.min(). Оскільки вони дозволяють отримати номер елемента, що є найбільшим або найменшим, їх можна використовувати для отримання самих цих значень, такими, наприклад, командами: PE[which.max(PE$L), 'L']. Втім, максимальне та мінімальне значення можна отримати й простішим способом, командами max(PE$L) та min(PE$L).
Зверніть увагу на команду в R-діалозі 5.5.1: PE[PE$Place == 'Zamulivka, c(6, 8)]. Фактично, це питання: які жаби, за їх статтю та генотипом, походять у досліджуваній вибірці з Замулівки? Перша координата, адреса за рядками, задана тут умовою (походження з певного локалітету), друга координата, адреса за змінними, — задана вектором, який визначає, значення яких ознак слід отримати для випадків, що відповідають заданій умові.
Команда PART <- subset(DATAFRAME, CONDITION) надає найширші можливості для вибору частини даних з фрейму. PE_f_2n <- subset(PE, Sex=="female" & (Genotyp=="LL" | Genotyp=="LR" | Genotyp=="RR")) , фактично, задає для R вказівку сформувати об'єкт, у який входять самиці, що мають один з трьох диплоїдних генотипів (LL, LR або RR).
Раніше (R-діалог 5.2.1) ми створили версію файлу даних, що містила пропущені значення. Ми навчилися убирати пропущені значення з векторів та розраховувати статистики для векторів без врахування пропусків. Під час роботи з матрицями та фреймами досить часто має сенс просто видалити усі рядки, що містять пропущені значення (R-діалог 5.5.2).
R-діалог 5.5.2. Два способи убрати рядки з пропущеними значеннями з фрейму
> PE1 <- read.csv('PelophylaxExamples.csv', sep = ";", dec = ",", na.strings = 42) > # Створюємо фрейм, що містить пропущені значення, NA > PE2 <- PE1[complete.cases(PE1), ] # Першій спосіб убрати рядки, що містять NA > PE3 <- na.omit(PE1) # Другий спосіб убрати рядки, що містять NA > dim(PE1) == dim(PE2) # Отримані результати однакові за розміром [1] TRUE TRUE > > sum(PE2 != PE3) # Кількість відмінностей між двома фреймами [1] 0 # Результат демонструє повну тотожність усіх елементів PE2 і PE3 > |
5.6. Робота зі стовпцями фрейму PelophylaxExample
Важлива частина роботи з даними — створювання, зміни, видалення самих змінних. Розглянемо кілька корисних команд. Перш за все, повернемося до прикладу, що розглядався в пункті 4.4. Там ми створили два вектори: L та Ltc. Щоб об'єднати їх у фрейм даних (припустимо, з назвою temp), слід виконати команду temp <- data.frame(L, Ltc).
Повернемося до фрейму PE. Як ви пам'ятаєте, перший стовпчик в ньому не мав назви. R сам дав йому назву X. До речі, якщо ми введемо команду PE$X (тобто FRAMENAME$VARIABLENAME), R виведе у консолі усі 57 кодів особин. У такому разі цей стовпчик логічно перейменувати: names(PE)[1] <- "Code". Цифра у квадратних дужках — це номер стовпця; так ми повідомлюємо, що змінюємо назву саме першої змінної.
А як видалити назви, якщо вони стануть зайвими? Присвоїти їм спеціальний оператор NULL! Це можна зробити, наприклад, так: colnames(DATAFRAME) <- NULL. Назви перетворяться на NA, і до стовпців можна буде звертатися лише за індексами.
Більш важлива перебудова фрейму PelophylaxExample стосується переходу від абсолютних значень метричних ознак до пропорційних. Річ у тім, що при порівнянні жаб, які мають різні розміри (перш за все — різну довжину тіла, L) нема сенсу порівнювати їх метричні ознаки, що стосуються окремих частин. Жаба, ширина голови у якої (Ltc) ставить 200 мкм (20 мм) — має вузьку голову або широку? У разі, якщо її довжина тіла 50 мм, її голова широка, а якщо її довжина 70 мм — вузька. Тому у більшості аналізів оптимально використовувати довжину тіла у її абсолютному значенні, а усі інші метричні ознаки — у вигляді пропорцій, як частку від ділення абсолютного значення на довжину тіла. До речі, при дослідженні інших тварин може бути доцільним використовувати у якості «модуля» не довжину тіла, а якісь інші ознаки. Наприклад, при дослідженнях птахів та кажанів такою ознакою, відносно якої розраховують пропорції, є довжина плеча.
Існує кілька способів додати до фрейму PE нові змінні з пропорційними ознаками. Найпростіший з них такий. У редакторі скриптів (або відразу у консолі) слід додати та виконати команду PE$Ltc_L <- PE$Ltc / PE$L та усі аналогічні. Коли R отримує таку команду відносно стовпця, якого не існує, він відразу й створює такий стовпець, і виконує необхідні розрахунки.
Створюючі нові стовпці, можна використовувати й логічні вирази. Припустимо, нас цікавить зв'язок ширини голови з іншими ознаками. У стовпці Ltc_L ми розрахували відносну ширину голови, але ми бажаємо створити ще один стовпець, HeadWidth, де розділити жаб на три групи: з головою середньої ширини (middle), вузькою (narrow) та широкою (wide). Як провести межі між цими класами? Один зі способів — за квартилями. Позначимо жаб, що попадають у першій квартиль, як вузькоголових, а у четвертий — як широкоголових. Як це зробити, показано в R-діалозі 5.6.1.
R-діалог 5.6.1. Створення нових стовпців у фреймі PelophylaxExample
> > PE <- read.csv('PelophylaxExamples.csv', sep = ";", dec = ",") > names(PE)[1] <- "Code" # Присвоєння імен рядкам за кодами жаб > PE$Ltc_L <- PE$Ltc / PE$L # Створення стовпців з пропорціями > PE$Fm_L <- PE$Fm/ PE$L > PE$T_L <- PE$Ltc / PE$L > PE$Dp_L <- PE$Dp / PE$L > PE$Ci_L <- PE$Ci / PE$L > PE$Cs_L <- PE$Cs / PE$L > (su <- summary(PE$Ltc_L)) # Детальніше про відносну ширину голови Min. 1st Qu. Median Mean 3rd Qu. Max. 0.2849 0.3345 0.3550 0.3554 0.3738 0.4343 > (as.vector(su)) # Перетворити вивід результатів у вектор [1] 0.2849462 0.3344538 0.3549669 0.3554291 0.3738318 0.4342688 > PE$HeadWidth[PE$Ltc_L >= su[5]] <- "wide" > PE$HeadWidth[PE$Ltc_L < su[5] & PE$Ltc_L > su[2]] <- "middle" > PE$HeadWidth[PE$Ltc_L <= su[2]] <- "narrow" > table(PE$HeadWidth) # Дивимось, що вийшло middle narrow wide 27 15 15 > |
Іноді корисно отримати змінні з порядковими номерами рядків. Як це зробити найпростішим чином? PE$Number <- 1:nrow(PE). До речі, якщо ми виконаємо команду PE$Ones <- 1, ми отримаємо просто стовпчик, що заповнено одиницями.