Data Structures

Огляд

Викладання: 40 хв
Вправи: 15 хв
Питання
  • How can I read data in R?

  • What are the basic data types in R?

  • How do I represent categorical information in R?

Цілі
  • To be aware of the different types of data.

  • To begin exploring data frames, and understand how they are related to vectors, factors and lists.

  • To be able to ask questions from R about the type, class, and structure of an object.

Однією з найпотужніших функцій R є здатність мати справи з табличними даними - такими, які ви вже можете мати в електронній таблиці або в файлі CSV. Почнемо з завантаження і читання у файлі nordic-data.csv. Ми збережемо ці дані, як об’єкт під назвою nordic:

nordic <- read.csv("data/nordic-data.csv")

Функція read.table використовуєтьяс для читання табличних даних, що зберігаються в текстовому файлі, де стовпці даних розділені пунктуаційними символами, такими як файли CSV (csv = значення, розділені комами). Табуляція і коми - найбільш поширені пунктуаційні символи, що використовуються для розділення або розмежування точок даних у файлах csv. Для зручності R надає 2 інші версії read.table. Це: read.csv для файлів, де дані розділені комами і read.delim для файлів, де дані розділені табуляцією. Із цих трьох функцій read.csv найчастіше використовується. Якщо необхідно, можна перевизначити стандартне розмежування знаків пунктуації для read.csv і read.delim.

Ми можемо почати вивчати наш набір даних прямо зараз, витягуючи стовпці, вказуючи оператор $:

nordic$country
[1] "Denmark" "Sweden"  "Norway" 
nordic$lifeExp
[1] 77.2 80.0 79.0

Ми можемо робити різні операції зі стовпцями. Наприклад, якщо ми виявили, що тривалість життя на два роки вища:

nordic$lifeExp + 2
[1] 79.2 82.0 81.0

А як що до цього:

nordic$lifeExp + nordic$country
Error in nordic$lifeExp + nordic$country: non-numeric argument to binary operator

Розуміння того, що тут сталося - це ключ до успішного аналізу даних в R.

Типи даних

Якщо ви здогадались, що остання командаповерне помилку, тому що 77.2 + "Denmark" це нісенітниця, і ви праві - і y вас вже є якась інтуїція для важливої концепції під назвою data classes. Ми можемо запитати який це клас даних:

class(nordic$lifeExp)
[1] "numeric"

У R є 6 основних типів даних: numeric, integer, complex, logical, character, and factor.

class(3.14)
[1] "numeric"
class(1L) # The L suffix forces the number to be an integer, since by default R uses float numbers
[1] "integer"
class(1+1i)
[1] "complex"
class(TRUE)
[1] "logical"
class('banana')
[1] "character"
class(factor('banana'))
[1] "factor"

Не важливо від того, наскільки складним стає наш аналіз, всі дані в R інтерпретуються конкретним класом даних. Ця строгість має деякі дійсно важливі наслідки.

Користувач додав нові деталі у тривалості віку. Ця інформація у файлі data/nordic-data-2.csv.

Завантажте нові дані як nordic_2, і перевірте, який клас даних у колонці lifeExp:

nordic_2 <- read.csv("data/nordic-data-2.csv")
class(nordic_2$lifeExp)
[1] "character"

О ні, наша очікувана тривалість життя lifeExp більше не є числовим типом! Якщо ми спробуємо обчислити їх так само, як і раніше, ми зіткнемося з проблемами:

nordic_2$lifeExp + 2
Error in nordic_2$lifeExp + 2: non-numeric argument to binary operator

Що сталося? Коли R зчитує файл csv в одну з цих таблиць, він наполягає на тому, щоб усе в стовпці було одного класу; якщо він не може зрозуміти що все в стовпці є числами, тоді нічого в стовпці не може бути числом. Таблиця, у яку R завантажив наші дані, називається фреймом даних, і це наш перший приклад чогось, що називається структура даних - тобто структура, яку R знає, як побудувати з основних типів даних.

Ми можемо побачити, що це фрейм даних, викликавши на ньому функцію class():

class(nordic)
[1] "data.frame"

Щоб успішно використовувати наші дані в R, нам потрібно зрозуміти, що таке основні структури даних і як вони поводяться.

Вектори та тип примусу

Щоб краще зрозуміти цю поведінку, давайте познайомимося з іншою структурою даних: вектор.

my_vector <- vector(length = 3)
my_vector
[1] FALSE FALSE FALSE

Вектор у R - це, по суті, впорядковний список речей, з особливою умовою, що все у векторі має бути того самого типу даних. Якщо ви не виберете тип даних, за умовчуванням буде logical; або, ви можете оголосити порожній вектор будь-якого типу, який вам подобається.

another_vector <- vector(mode = 'character', length = 3)
another_vector
[1] "" "" ""

Ви можете перевірити, чи щось є вектором:

str(another_vector)
 chr [1:3] "" "" ""

Дещо загадковий вихід цієї команди вказує на базовий тип даних, хнайдений у цьому векторі - у цьому випадку chr, символ; вказівка на кількість речей у векторі - власне, індекси вектора, в даному випадку [1:3]; і кілька випадків того, що насправді є у векторі - у цьому випадку порожні рядки символів. Якщо ми робимо так само

str(nordic$lifeExp)
 num [1:3] 77.2 80 79

ми бачимо, що nordic$lifeExp це теж вектор - усі стовпці даних, які ми завантажуємо у фрейми даних R, є векторами, і це головна причина, чому R змушує, щоб все в стовпці мало той самий базовий тип даних.

Обговорення 1

Чому R так самовпевнено ставиться до того, що ми розміщуємо в наших стовпцях даних Як нам це допомагає?

Обговорення 1

Зберігаючи все в стовпці однаковим, ми дозволяємо собі робити прості припущення щодо наших даних; якщо ви можете інтерпретувати один запис у стовпці як число, тоді ви можете інтерпретувати усі як числа, тому нам не потрібно перевіряти кожного разу. Саме цю послідовність мають на увазі люди, коли говорять про очищені дані; Зрештою, сувора послідовність значною мірою полегшує наше життя в R.

Ви також можете створювати вектори з явним вмістом за допомогою функції об’єднання:

combine_vector <- c(2, 6, 3)
combine_vector
[1] 2 6 3

З огляду на те, що ми вже вивчили, яким, на вашу думку, буде результат наступного?

quiz_vector <- c(2, 6, '3')

Це те, що називається примусом типу , і це джерело багатьох сюрпризів і причина, чому ми повинні знати про основні типи даних і те, як R  їх інтерпретує. Коли R зустрічає комбінацію типів (числових і символьних) які потрібно об’єднати в єдиний вектор, він змусить їх усіх бути одного типу. Розглянемо:

coercion_vector <- c('a', TRUE)
coercion_vector
[1] "a"    "TRUE"
another_coercion_vector <- c(0, TRUE)
another_coercion_vector
[1] 0 1

Правила примусу виглядають так: logical -> integer -> numeric -> complex -> character, де -> можна читати як, перетворюється на. Ви можете спробувати змусити цей потік за допомогою функції as.:

character_vector_example <- c('0', '2', '4')
character_vector_example
[1] "0" "2" "4"
character_coerced_to_numeric <- as.numeric(character_vector_example)
character_coerced_to_numeric
[1] 0 2 4
numeric_coerced_to_logical <- as.logical(character_coerced_to_numeric)
numeric_coerced_to_logical
[1] FALSE  TRUE  TRUE

Як ви можете бачити, деякі дивовижні речі можуть статися, коли R примусово перетворює один базовий  тип даних в інший! Залишаючи в стороні дрібниці примусу типів, головне: якщо ваші дані виглядають не так, як ви думали, виною може бути примус типів; переконайтеся, що все має однаковий тип у ваших векторах і стовпцях кадрів даних, інакше ви отримаєте неприємні сюрпризи!

Завдання 1

Враховуючи те, що ви тепер знаєте про перетворення типів, подивіться на клас даних у nordic_2$lifeExpта порівняйте його з nordic$lifeExp. Чому ці стовпці є різними класами?

Розв’язання

str(nordic_2$lifeExp)
 chr [1:3] "77.2" "80" "79.0 or 83"
str(nordic$lifeExp)
 num [1:3] 77.2 80 79

Дані в nordic_2$lifeExp зберігаються як множники, а не числові. Це через рядок символів “або” в третій точці даних. “Фактор” — це спеціальний термін R для категоричних даних. Пізніше, на цьому семінарі, ми будемо більше працювати з факторними даними.

Функція комбінування `c(), також додаватиме елементи до існуючого вектора:

ab_vector <- c('a', 'b')
ab_vector
[1] "a" "b"
combine_example <- c(ab_vector, 'DC')
combine_example
[1] "a"  "b"  "DC"

Ви також можете скласти ряд чисел:

my_series <- 1:10
my_series
 [1]  1  2  3  4  5  6  7  8  9 10
seq(10)
 [1]  1  2  3  4  5  6  7  8  9 10
seq(1,10, by = 0.1)
 [1]  1.0  1.1  1.2  1.3  1.4  1.5  1.6  1.7  1.8  1.9  2.0  2.1  2.2  2.3  2.4
[16]  2.5  2.6  2.7  2.8  2.9  3.0  3.1  3.2  3.3  3.4  3.5  3.6  3.7  3.8  3.9
[31]  4.0  4.1  4.2  4.3  4.4  4.5  4.6  4.7  4.8  4.9  5.0  5.1  5.2  5.3  5.4
[46]  5.5  5.6  5.7  5.8  5.9  6.0  6.1  6.2  6.3  6.4  6.5  6.6  6.7  6.8  6.9
[61]  7.0  7.1  7.2  7.3  7.4  7.5  7.6  7.7  7.8  7.9  8.0  8.1  8.2  8.3  8.4
[76]  8.5  8.6  8.7  8.8  8.9  9.0  9.1  9.2  9.3  9.4  9.5  9.6  9.7  9.8  9.9
[91] 10.0

Ми можемо поставити кілька запитань про вектори:

sequence_example <- seq(10)
head(sequence_example,n = 2)
[1] 1 2
tail(sequence_example, n = 4)
[1]  7  8  9 10
length(sequence_example)
[1] 10
class(sequence_example)
[1] "integer"

Нарешті, ви можете дати назви елементам у вашому векторі: 

my_example <- 5:8
names(my_example) <- c("a", "b", "c", "d")
my_example
a b c d 
5 6 7 8 
names(my_example)
[1] "a" "b" "c" "d"

Завдання 2

Почніть із створення вектора з числами від 1 до 26. Помножте вектор на 2 і дайте отриманому вектору назви від A до Z (підказка: є вбудований вектор під назвою  LETTERS)

Розв’язання до завдання 2

x <- 1:26
x <- x * 2
names(x) <- LETTERS

Фактори

Ми сказали, що стовпці в кадрах даних є векторами:

str(nordic$lifeExp)
 num [1:3] 77.2 80 79
str(nordic$year)
 int [1:3] 2002 2002 2002

Це має сенс. Але як що до цього

str(nordic$country)
 chr [1:3] "Denmark" "Sweden" "Norway"

Ще одна важлива структура даних називається фактором. Фактори виглядають як символьні дані, але використовуються для представлення категоріальної інформації. Наприклад, створимо вектор рядків, що позначає скандинавські країни для всіх країн нашого дослідження:

nordic_countries <- c('Norway', 'Finland', 'Denmark', 'Iceland', 'Sweden')
nordic_countries
[1] "Norway"  "Finland" "Denmark" "Iceland" "Sweden" 
str(nordic_countries)
 chr [1:5] "Norway" "Finland" "Denmark" "Iceland" "Sweden"

Ми можемо перетворити вектор у множник таким способом:

categories <- factor(nordic_countries)
class(categories)
[1] "factor"
str(categories)
 Factor w/ 5 levels "Denmark","Finland",..: 4 2 1 3 5

Тепер R помітив, що в наших даних є 5 можливих категорій, але він також зробив дещо дивовижне; замість того, щоб роздрукувати рядки, які ми йому дали, натомість ми отримали купу чисел. R замінив наші зрозумілі людині категорії нумерованими індексами під капотом, це необхідно, оскільки багато статистичних розрахунків використовують такі числові представлення категоріальних даних:

class(nordic_countries)
[1] "character"
class(categories)
[1] "factor"

Завдання

Чи можете ви здогадатися, чому ці цифри використовуються для позначення цих країн?

Розв’язання

Вони відсортовані в алфавітному порядку

Завдання 3

Чи є фактор у нашому nordic кадрі даних? як воно називається? Спробуйте використати ?read.csv щоб зрозуміти, як зберегти текстові стовпці як символьні вектори замість факторів; потім напишіть одну-дві команди, щоб показати, що множник nordic насправді є символьним вектором, коли завантажується таким чином.

Розв’язання до завдання 3

Одним із рішень є використання аргументу stringAsFactors:

nordic <- read.csv(file = "data/nordic-data.csv", stringsAsFactors = FALSE)
str(nordic$country)

Іншим рішенням є використання аргументу colClasses який дозволяє точніше контролювати.

nordic <- read.csv(file="data/nordic-data.csv", colClasses=c(NA, NA, "character"))
str(nordic$country)

Примітка: новачкам важко зрозуміти файли довідки; не забудьте повідомити їм, що це типово, і заохочуйте їх робити найкращі припущення на основі семантичного значення, навіть якщо вони не впевнені.

Виконуючи статистичне моделювання, важливо знати, що таке базові рівні.  Передбачається, що це перший фактор, але за замовчуванням фактори  позначені в алфавітному порядку. Ви можете змінити це, вказавши рівні:

mydata <- c("case", "control", "control", "case")
factor_ordering_example <- factor(mydata, levels = c("control", "case"))
str(factor_ordering_example)
 Factor w/ 2 levels "control","case": 2 1 1 2

У цьому випадку ми чітко сказали R, що “control” має бути представлений 1, і “case” - 2. Це позначення може бути дуже важливим для інтерпретації результатів статистичних моделей!

Списки

Інша структура даних, яка вам знадобиться у вашій сумці хитрощів, це list. Список дещо простіший, ніж інші типи, тому що ви можете додати до нього все, що завгодно:

list_example <- list(1, "a", TRUE, c(2, 6, 7))
list_example
[[1]]
[1] 1

[[2]]
[1] "a"

[[3]]
[1] TRUE

[[4]]
[1] 2 6 7
another_list <- list(title = "Numbers", numbers = 1:10, data = TRUE )
another_list
$title
[1] "Numbers"

$numbers
 [1]  1  2  3  4  5  6  7  8  9 10

$data
[1] TRUE

Тепер ми можемо зрозуміти дещо дивовижне у нашому кадрі даних; що станеться, якщо порівняти str(nordic) і str(another_list):

str(nordic)
'data.frame':\t3 obs. of  3 variables:
 $ country: chr  "Denmark" "Sweden" "Norway"
 $ year   : int  2002 2002 2002
 $ lifeExp: num  77.2 80 79
str(another_list)
List of 3
 $ title  : chr "Numbers"
 $ numbers: int [1:10] 1 2 3 4 5 6 7 8 9 10
 $ data   : logi TRUE

Ми бачимо, що вихід для цих двох об’єктів виглядає дуже схожим. Це тому, що фрейми даних є списками ‘under the hood’. Фрейми даних є окремим випадком списків, де кожен елемент (стовпці фрейму даних) мають однакову довжину.

У нашому прикладі nordic, ми маємо ціле число, подвійну та логічну змінну. Як ми вже бачили, кожен стовпець кадру даних є вектором.

nordic$country
[1] "Denmark" "Sweden"  "Norway" 
nordic[, 1]
[1] "Denmark" "Sweden"  "Norway" 
class(nordic[, 1])
[1] "character"
str(nordic[, 1])
 chr [1:3] "Denmark" "Sweden" "Norway"

Кожен рядок є спостереженням різних змінних, сам по собі є фреймом даних, і тому може складатися з елементів різних типів.

nordic[1, ]
  country year lifeExp
1 Denmark 2002    77.2
class(nordic[1, ])
[1] "data.frame"
str(nordic[1, ])
'data.frame':\t1 obs. of  3 variables:
 $ country: chr "Denmark"
 $ year   : int 2002
 $ lifeExp: num 77.2

Завдання 4

Існує кілька тонко різних способів виклику змінних, спостережень і елементів із фрейму даних:

  • nordic[1]
  • nordic[[1]]
  • nordic$country
  • nordic["country"]
  • nordic[1, 1]
  • nordic[, 1]
  • nordic[1, ]

Спробуйте ці приклади та поясніть, що повертає кожен із них.

Підказка: скористайтеся функцією class() щоб перевірити, що повертається в кожному випадку.

Розв’язання до завдання 4

nordic[1]
  country
1 Denmark
2  Sweden
3  Norway

Ми можемо розглядати фрейм даних як список векторів. Одинарна дужка [1] повертає перший фрагмент списку як інший список. У цьому випадку це перший стовпець фрейму даних.

nordic[[1]]
[1] "Denmark" "Sweden"  "Norway" 

Подвійна дужка [[1]] повертає вміст елемента списку. У цьому випадку це вміст першого стовпця,, a vector типу factor.

nordic$country
[1] "Denmark" "Sweden"  "Norway" 

У цьому прикладі використовується символ $ для адресації елементів за назвою. coat є першим стовпцем фрейму даних, знову vector типу factor. X

nordic["country"]
  country
1 Denmark
2  Sweden
3  Norway

Тут ми використовуємо одну дужку, ["country"] яка замінює номер індексу назвою стовпця. Як у прикладі 1, повернутий об’єкт - list.

nordic[1, 1]
[1] "Denmark"

У цьому прикладі використовується одна дужка, але цього разу ми надаємо координати рядків і стовпців. Повернутий об’єкт – це значення в рядку 1, стовпці 1. Об’єкт є integer, але оскільки він є частиною vector типу factor, R відображає мітку “Denmark” пов’язану з цілим значенням.

nordic[, 1]
[1] "Denmark" "Sweden"  "Norway" 

Як і в попередньому прикладі, ми використовуємо одинарні дужки та надаємо координати рядків і стовпців. Координата рядка не вказана, R інтерпретує це відсутнє значення як усі елементи цього column vector.

nordic[1, ]
  country year lifeExp
1 Denmark 2002    77.2

Знову ми використовуємо одну фігурну дужку з координатами рядків і стовпців. Координата колони не вказана. Повернене значення - це list, що містить усі значення в першому рядку.

Ключові моменти

  • Use read.csv to read tabular data in R.

  • The basic data types in R are double, integer, complex, logical, and character.

  • Use factors to represent categories in R.