Функція getwd() говорить, в якій директорії ми знаходимось:
getwd()
Функція setwd() говорить, в яку директорію ми хочемо перейти:
setwd("~/")
Функція file.exists("data") - перевіряє чи існує папка із назвою data:
file.exists("data")
## [1] TRUE
Функція dir.create("data") - створює папку із відповідною назвою. Якщо в директорії не має папки із назвою data, то створити її:
if (!file.exists("data")) {
dir.create("data")
}
Перевіримо чи створилась папка data:
file.exists("data")
## [1] TRUE
Розглянемо функцію download.file() - завантаження даних із інтернету. Основні аргументи функції:
url - адреса файлу; destfile - файл, в який запишемо дані; method - метод завантаження даних.
В якості прикладу, отримаємо дані зі стаціонарних камер спостереження міста Балтімор. Ці камери фіксують порушення швидкісного режиму. Потрібно завантажити ці дані і дізнатись, де знаходяться ці камери:
# https://data.baltimorecity.gov/Transportation/Baltimore-Fixed-Speed-Cameras/dz54-2aru
# --> export --> csv (правою кнопкою миші скопіюємо адресу файлу)
file.url <- "https://data.baltimorecity.gov/api/views/dz54-2aru/rows.csv?accessType=DOWNLOAD"
# помістимо вказані дані в cameras.csv в папку data
# використовується method = "curl", бо сайт із даними від камер використовує захищений протокод HTTPS
download.file(url = file.url, destfile = "./data/cameras.csv", method = "curl")
Перевіримо чи стоворився файл, попросимо R перерахувати файли в папці ./data:
list.files("./data")
## [1] "cameras.csv" "cameras.xlsx"
Оскільки дані на сайті можуть змінюватись(наприклад, додадуть певну кількість камер), то необхідно зафіксувати дату на момент вивантаження. Це можна зробити за допомогою функції date()
date.downloaded <- date()
date.downloaded
## [1] "Mon Mar 21 17:52:19 2016"
Якщо адресне посилання починається із HTTP, то ніяких додаткових аргументів у фукнцію download.file() додавати не потрібно. Навіть якщо URL починається із HTTPS під Windows все має працювати без проблем.
Функція read.table() - завантаження файлу в R, считає дані напряму в RAM (тому можуть виникнути проблеми із особливо “тяжкими” файлами, окрім випадків, коли хочеться обробляти файл частинами). read.table - не найкращий варіант читання великих фалів в R. Важливі параметри:
file - ім“я файлу, який потрібно завантажити, header - наявність заголовку, sep - тип розділювача, row.names - наявність імен у рядків, nrows - кількість рядків, які ми хочемо обробити. dec - десятковий розділювач:”.“,”,“.
Функції read.csv() і read.csv2() считуються файли відповідних типів.
Прочитаємо файл із камер:
list.files("./data")
## [1] "cameras.csv" "cameras.xlsx"
#camera.data <- read.table(file = "./data/cameras.csv")
# і зразу отримаємо помилку, бо в read.table по замовчуванню розділювач - знак табуляції, а у файлі cameras.csv - кома
#head(camera.data)
Додамо аргумент sep = ",", а також вкажемо, що у даних є заголовок:
camera.data <- read.table(file = "./data/cameras.csv", sep = ",", header = T)
head(camera.data)
## address direction street crossStreet
## 1 S CATON AVE & BENSON AVE N/B Caton Ave Benson Ave
## 2 S CATON AVE & BENSON AVE S/B Caton Ave Benson Ave
## 3 WILKENS AVE & PINE HEIGHTS AVE E/B Wilkens Ave Pine Heights
## 4 THE ALAMEDA & E 33RD ST S/B The Alameda 33rd St
## 5 E 33RD ST & THE ALAMEDA E/B E 33rd The Alameda
## 6 ERDMAN AVE & N MACON ST E/B Erdman Macon St
## intersection Location.1
## 1 Caton Ave & Benson Ave (39.2693779962, -76.6688185297)
## 2 Caton Ave & Benson Ave (39.2693157898, -76.6689698176)
## 3 Wilkens Ave & Pine Heights (39.2720252302, -76.676960806)
## 4 The Alameda & 33rd St (39.3285013141, -76.5953545714)
## 5 E 33rd & The Alameda (39.3283410623, -76.5953594625)
## 6 Erdman & Macon St (39.3068045671, -76.5593167803)
Можна використати функцію read.csv(), в якій автоматично встановлені аргументи sep = "," i header = TRUE.
Існують ще параметри, які можна передати в read.table():
quote - можна вказати, якщо у файлі зустрічаються певні символи (", ', *), то їх варто розцінити як символи, а не розділювачі. Якщо в цей аргумент підставити пусті кавички (quote = ""), то жоден символ не буде рахуватись розділювачем даних. na.strings - говорить R, який символ або сукупність символів замінюють пропущені значення (найчастіще - це NA). nrows - говорить, скільки рядків файлу потрібно завантажити. skip - говорить, скільки символів потрібно пропустити перш ніж прочитати файл.
Так, наприклад, якщо потрібно прочитати файл із 3-го по 13-й рядок, то потрібо вказати парамети skip = 2, nrows = 10.
Завантажимо excel версію даних по балтиморських камер (скопіюємо посилання на excel файл):
file.url.excel <- "https://data.baltimorecity.gov/api/views/dz54-2aru/rows.xlsx?accessType=DOWNLOAD"
download.file(url = file.url.excel,
destfile = "./data/cameras.xlsx",
method = "curl")
# зафіксуємо дату завантаження
date.downloaded.xlsx <- date()
list.files("./data")
## [1] "cameras.csv" "cameras.xlsx"
Для обробки excel файлів в R використаємо пакет "xlsx".
# install.packages("xlsx")
library(xlsx)
## Loading required package: rJava
## Loading required package: xlsxjars
Запустимо функцію read.xlsx() для считування excel файлу в R.
sheetIndex - номер листа, на якому знаходяться наші дані, header - вказуємо, що є рядок заголовків.
camera.data.excel <- read.xlsx(file = "./data/cameras.xlsx",
sheetIndex = 1,
header = TRUE)
head(camera.data.excel)
## address direction street crossStreet
## 1 S CATON AVE & BENSON AVE N/B Caton Ave Benson Ave
## 2 S CATON AVE & BENSON AVE S/B Caton Ave Benson Ave
## 3 WILKENS AVE & PINE HEIGHTS AVE E/B Wilkens Ave Pine Heights
## 4 THE ALAMEDA & E 33RD ST S/B The Alameda 33rd St
## 5 E 33RD ST & THE ALAMEDA E/B E 33rd The Alameda
## 6 ERDMAN AVE & N MACON ST E/B Erdman Macon St
## intersection Location.1
## 1 Caton Ave & Benson Ave (39.2693779962, -76.6688185297)
## 2 Caton Ave & Benson Ave (39.2693157898, -76.6689698176)
## 3 Wilkens Ave & Pine Heights (39.2720252302, -76.676960806)
## 4 The Alameda & 33rd St (39.3285013141, -76.5953545714)
## 5 E 33rd & The Alameda (39.3283410623, -76.5953594625)
## 6 Erdman & Macon St (39.3068045671, -76.5593167803)
Якщо потрібно скачати, наприклад, тільки 2-й та 3-й стовбці, а також рядки із 1-ї по 4-у, можна це вказати через аргументи colIndex i rowIndex.
Можна використати команду write.xlsx() для зберігання файлу у відповідному форматі.Пакет XLConnect має більше можливостей для маніпуляції із excel файлами.
XML - мова розміток (широко використовується в інтернет-додатках). XML документ складається із двух частин: розмітка документа - спосіб додавання міток таким чином, щоб файл був структуйований. Зміст документа - просто текст, який знаходиться між мітками, які задають структуру документа. Обговоримо теги, елементи і атрибути. Теги - це мітки, які застосовуються до певних частин документа для того, щоб надати йому структуру.
Елементи - це індивідуальні теги.
Атрибути - це елементи тега. Можна додавати до тега певні компоненти. Наприклад, є тег із зображенням і вказано джерело зображення.
Прочитаємо дані за допомогою пакета XML:
# install.packages("XML")
library(XML)
file.url.xml <- "http://www.w3schools.com/xml/simple.xml"
# считаємо xml файл за допомогою функцію xmlTreeParse(), яка завантажує файл в пам"ять R таким чином, щоб можна було аналізувати його і мати доступ до різних його частин.
# в пам"яті R він все ще як структуйований файл, тому щоб перейти до певного розділу, потрібно використовувати певні команди.
doc <- xmlTreeParse(file = file.url.xml, useInternal = TRUE)
Перше, що нам потрібно - це корневий вузол, який можна отримати за допомогою функції xmlRoot(). Корневий вузол - це обгортка для всього документа. Перший елемент у всьому документі. Команда xmlRoot визначає цей вузол і відкриває до нього доступ.
root.node <- xmlRoot(doc)
Щоб отримати ім“я вузла потрібно використати команду xmlName():
xmlName(root.node)
## [1] "breakfast_menu"
Можна використати функцію names(), щоб отримати всі імена всередині корневого вузла.
names(root.node)
## food food food food food
## "food" "food" "food" "food" "food"
Тобто тег breakfast_menu охоплює весь документ - це і є корневий вузол - меню на сніданок. Він складається із 5-ти різних страв, звернутих в окремий елемент food.
Можна звернутись по частин XML документа, використовуючи корневий вузел, так як це робилось зі списками.
root.node[[1]] # отримали перший елемент меню
## <food>
## <name>Belgian Waffles</name>
## <price>$5.95</price>
## <description>Two of our famous Belgian Waffles with plenty of real maple syrup</description>
## <calories>650</calories>
## </food>
root.node[[1]][[1]]
## <name>Belgian Waffles</name>
root.node[[1]][[2]]
## <price>$5.95</price>
Можна отримати окремі частини файла, використовуючі функцію xmlSApply() із бажаною функцією xmlValue - команда перебере всі елементи корневого вузла і отримає їх значення.
xmlSApply(root.node, xmlValue)
## food
## "Belgian Waffles$5.95Two of our famous Belgian Waffles with plenty of real maple syrup650"
## food
## "Strawberry Belgian Waffles$7.95Light Belgian waffles covered with strawberries and whipped cream900"
## food
## "Berry-Berry Belgian Waffles$8.95Light Belgian waffles covered with an assortment of fresh berries and whipped cream900"
## food
## "French Toast$4.50Thick slices made from our homemade sourdough bread600"
## food
## "Homestyle Breakfast$6.95Two eggs, bacon or sausage, toast, and our ever-popular hash browns950"
Можна отримати окрему складову документа за допомогою мови XML-запитів XPath. Перше, що нам потрібне - це вузли верхнього рівня для кожного елементу. Їх можна отримати за допомогою /node (де node - це назва тега). Вузол будь-якого рівня шукається як //node. Потім можна вичленити певні вузли із заданими атрибувати.
Перше, що ми хочемо отримати - це позиції меню та їх ціни. Використаємо функцію xpathSApply і явним чином вкажемо, які дані ми хочемо отримати. Щоб отримати назву страв задамо //name. Ця команда пробіжить весь документ і збере всі вузли, які мають тег name, а потім отримає їх значення.
xpathSApply(root.node, "//name", xmlValue)
## [1] "Belgian Waffles" "Strawberry Belgian Waffles"
## [3] "Berry-Berry Belgian Waffles" "French Toast"
## [5] "Homestyle Breakfast"
Зробимо те ж саме, тільки для цін.
xpathSApply(root.node, "//price", xmlValue)
## [1] "$5.95" "$7.95" "$8.95" "$4.50" "$6.95"
Використаємо інший приклад. Дані із сайту Балтіморської команди.
file.url.xml.2 <- "http://espn.go.com/nfl/team/schedule/_/name/bal"
# оскільки в нас тепер не xml файл, а html, то для парсінга потрібно використовувати htmlTreeParse()
doc.2 <- htmlTreeParse(file.url.xml.2,
useInternalNodes = T # аргумент використовуєтсья, щоб отримати доступ до списка вузлів
)
# Будемо шукати елементи, які є частиною списка, звідси позначка "li", і які відносяться до певного класу.
# Мені потірбно дізнатись рахунок, тому я задаю параметр class = 'score'
scores <- xpathSApply(doc.2,"//li[@class='score']", xmlValue)
# Отримаємо список із іменами команд
teams <- xpathSApply(doc.2, "//li[@class='team-name']", xmlValue)
scores
## [1] "19-13" "37-33" "28-24" "23-20 OT" "33-30 OT" "25-20"
## [7] "26-18" "29-26" "22-20" "16-13" "33-27" "15-13"
## [13] "35-6" "34-14" "20-17" "24-16" "30-27" "40-17"
## [19] "31-13" "20-19"
teams
## [1] "Denver" "Oakland" "Cincinnati" "Pittsburgh"
## [5] "Cleveland" "San Francisco" "Arizona" "San Diego"
## [9] "Jacksonville" "Los Angeles" "Cleveland" "Miami"
## [13] "Seattle" "Kansas City" "Pittsburgh" "Cincinnati"
## [17] "New Orleans" "Philadelphia" "Washington" "Atlanta"
JSON - Java Script Object Notation.
#install.packages("jsonlite")
library(jsonlite)
# Щоб зчитати дані в форматі JSON, спочатку необхідно отримати посилання, по якому знаходиться документ із JSON кодом.
# В даному випадку це посилання на API Github.
json.data <- fromJSON("https://api.github.com/users/jtleek/repos")
# json.data - ми отримали структуйовану таблицю з даними.
# подивимось на заголовки таблиці - це все змінні верхнього рівня.
names(json.data)
## [1] "id" "name" "full_name"
## [4] "owner" "private" "html_url"
## [7] "description" "fork" "url"
## [10] "forks_url" "keys_url" "collaborators_url"
## [13] "teams_url" "hooks_url" "issue_events_url"
## [16] "events_url" "assignees_url" "branches_url"
## [19] "tags_url" "blobs_url" "git_tags_url"
## [22] "git_refs_url" "trees_url" "statuses_url"
## [25] "languages_url" "stargazers_url" "contributors_url"
## [28] "subscribers_url" "subscription_url" "commits_url"
## [31] "git_commits_url" "comments_url" "issue_comment_url"
## [34] "contents_url" "compare_url" "merges_url"
## [37] "archive_url" "downloads_url" "issues_url"
## [40] "pulls_url" "milestones_url" "notifications_url"
## [43] "labels_url" "releases_url" "deployments_url"
## [46] "created_at" "updated_at" "pushed_at"
## [49] "git_url" "ssh_url" "clone_url"
## [52] "svn_url" "homepage" "size"
## [55] "stargazers_count" "watchers_count" "language"
## [58] "has_issues" "has_downloads" "has_wiki"
## [61] "has_pages" "forks_count" "mirror_url"
## [64] "open_issues_count" "forks" "open_issues"
## [67] "watchers" "default_branch"
# Одна із таких змінних owner, вона являється однією із компонентів нашого масиву.
# Можна піти далі і подивитись, із чого вона складається.
names(json.data$owner)
## [1] "login" "id" "avatar_url"
## [4] "gravatar_id" "url" "html_url"
## [7] "followers_url" "following_url" "gists_url"
## [10] "starred_url" "subscriptions_url" "organizations_url"
## [13] "repos_url" "events_url" "received_events_url"
## [16] "type" "site_admin"
# Можна піти ще далі і отримати значення login для всіх репозиторіїв вказаних в змінній owner.
json.data$owner$login
## [1] "jtleek" "jtleek" "jtleek" "jtleek" "jtleek" "jtleek" "jtleek"
## [8] "jtleek" "jtleek" "jtleek" "jtleek" "jtleek" "jtleek" "jtleek"
## [15] "jtleek" "jtleek" "jtleek" "jtleek" "jtleek" "jtleek" "jtleek"
## [22] "jtleek" "jtleek" "jtleek" "jtleek" "jtleek" "jtleek" "jtleek"
## [29] "jtleek" "jtleek"
Наступне, що ми можемо зробити - це взяти вбудований набір даних із R і перетворити його в формат JSON за допомогою функції toJSON().
my.json <- toJSON(head(iris),
pretty = TRUE # робить вивід результату "гарним"
)
cat(my.json)
## [
## {
## "Sepal.Length": 5.1,
## "Sepal.Width": 3.5,
## "Petal.Length": 1.4,
## "Petal.Width": 0.2,
## "Species": "setosa"
## },
## {
## "Sepal.Length": 4.9,
## "Sepal.Width": 3,
## "Petal.Length": 1.4,
## "Petal.Width": 0.2,
## "Species": "setosa"
## },
## {
## "Sepal.Length": 4.7,
## "Sepal.Width": 3.2,
## "Petal.Length": 1.3,
## "Petal.Width": 0.2,
## "Species": "setosa"
## },
## {
## "Sepal.Length": 4.6,
## "Sepal.Width": 3.1,
## "Petal.Length": 1.5,
## "Petal.Width": 0.2,
## "Species": "setosa"
## },
## {
## "Sepal.Length": 5,
## "Sepal.Width": 3.6,
## "Petal.Length": 1.4,
## "Petal.Width": 0.2,
## "Species": "setosa"
## },
## {
## "Sepal.Length": 5.4,
## "Sepal.Width": 3.9,
## "Petal.Length": 1.7,
## "Petal.Width": 0.4,
## "Species": "setosa"
## }
## ]
А тепер із створено JSON документу отримаємо дані:
iris.2 <- fromJSON(my.json)
iris.2
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
data.table (написано на C) - це розширення для data.frame (більш швидка та ефективна).
Всі функції, які працюють із data.frame, працюють із data.table.
library(data.table)
# Створимо таблицю data.frame
DF <- data.frame(x = rnorm(9),
y = rep(c("a", "b", "c"), each = 3),
z = rnorm(9))
head(DF, 3)
## x y z
## 1 -0.2010776 a -0.28832833
## 2 -0.3740112 a 0.65247701
## 3 -0.3385747 a 0.02999506
# Створимо таблицю data.table
DT <- data.table(x = rnorm(9),
y = rep(c("a", "b", "c"), each = 3),
z = rnorm(9))
head(DT, 3)
## x y z
## 1: 0.3561851 a -1.9480528
## 2: 0.1912162 a 1.6667593
## 3: -0.4699330 a 0.5992123
# Подивимось на всі таблиці з даними, які на даний момент знаходяться в пам"яті за допомогою функції tables().
tables()
## NAME NROW NCOL MB COLS KEY
## [1,] DT 9 3 1 x,y,z
## Total: 1MB
# Зробимо вибірку по рядках
DT[c(2,3)]
## x y z
## 1: 0.1912162 a 1.6667593
## 2: -0.4699330 a 0.5992123
# Ось такі звернення до стобців в data.table не працюють:
DT[, c(2,3)]
## [1] 2 3
# Приклади роботи із data.table.
DT[, .(mean(x), sum(z))]
## V1 V2
## 1: 0.5332977 0.8015358
DT[, table(y)]
## y
## a b c
## 3 3 3
# Додамо нову колонку
DT[, w := z^2]
# При зміні даних в основній таблиці у всіх інших копіях також
# впроваджуються зміни як в основній таблиці, бо DT2 залежить від DT.
DT2 <- DT
DT[, y := 2]
head(DT, 3)
## x y z w
## 1: 0.3561851 2 -1.9480528 3.7949096
## 2: 0.1912162 2 1.6667593 2.7780865
## 3: -0.4699330 2 0.5992123 0.3590553
head(DT2, 3)
## x y z w
## 1: 0.3561851 2 -1.9480528 3.7949096
## 2: 0.1912162 2 1.6667593 2.7780865
## 3: -0.4699330 2 0.5992123 0.3590553
# Якщо зробити "правильну" копію за допомогою фукнції copy(), то зміни не будуть поширюватись на копію.
DT3 <- copy(DT)
DT[, a := x^3]
head(DT)
## x y z w a
## 1: 0.3561851 2 -1.948052781 3.794910e+00 0.045188414
## 2: 0.1912162 2 1.666759272 2.778086e+00 0.006991554
## 3: -0.4699330 2 0.599212271 3.590553e-01 -0.103778621
## 4: 1.9757681 2 0.166499801 2.772218e-02 7.712726473
## 5: 0.5330412 2 -0.806718678 6.507950e-01 0.151454541
## 6: 1.0797692 2 -0.007192137 5.172684e-05 1.258904452
head(DT2)
## x y z w a
## 1: 0.3561851 2 -1.948052781 3.794910e+00 0.045188414
## 2: 0.1912162 2 1.666759272 2.778086e+00 0.006991554
## 3: -0.4699330 2 0.599212271 3.590553e-01 -0.103778621
## 4: 1.9757681 2 0.166499801 2.772218e-02 7.712726473
## 5: 0.5330412 2 -0.806718678 6.507950e-01 0.151454541
## 6: 1.0797692 2 -0.007192137 5.172684e-05 1.258904452
head(DT3)
## x y z w
## 1: 0.3561851 2 -1.948052781 3.794910e+00
## 2: 0.1912162 2 1.666759272 2.778086e+00
## 3: -0.4699330 2 0.599212271 3.590553e-01
## 4: 1.9757681 2 0.166499801 2.772218e-02
## 5: 0.5330412 2 -0.806718678 6.507950e-01
## 6: 1.0797692 2 -0.007192137 5.172684e-05
Зробимо багатоетапну функцію при створенні нових змінних. Спочатку створюється тимчасова змінна tmp я к сума х і у, а потім візьмемо логарифм. В даному випадку повертається операція, яка виконується останньою:
DT[, m := {tmp <- (x + z); log2(tmp + 5)}]
head(DT,3)
## x y z w a m
## 1: 0.3561851 2 -1.9480528 3.7949096 0.045188414 1.768981
## 2: 0.1912162 2 1.6667593 2.7780865 0.006991554 2.777783
## 3: -0.4699330 2 0.5992123 0.3590553 -0.103778621 2.358756
# Змінна може бути логічною.
DT[, aa := x > 0]
head(DT,3)
## x y z w a m aa
## 1: 0.3561851 2 -1.9480528 3.7949096 0.045188414 1.768981 TRUE
## 2: 0.1912162 2 1.6667593 2.7780865 0.006991554 2.777783 TRUE
## 3: -0.4699330 2 0.5992123 0.3590553 -0.103778621 2.358756 FALSE
# Використаємо оператор by для групування.
DT[, b:=mean(x + w), by = aa]
head(DT,3)
## x y z w a m aa
## 1: 0.3561851 2 -1.9480528 3.7949096 0.045188414 1.768981 TRUE
## 2: 0.1912162 2 1.6667593 2.7780865 0.006991554 2.777783 TRUE
## 3: -0.4699330 2 0.5992123 0.3590553 -0.103778621 2.358756 FALSE
## b
## 1: 2.0609105
## 2: 2.0609105
## 3: -0.3937686
# Створимо нову таблицю з даними.
set.seed(123)
DT <- data.table(x = sample(letters[1:3], 1E5, replace = T))
# Обрахуємо кількість кожної букви
DT[, .(Count = .N), by=x]
## x Count
## 1: a 33387
## 2: c 33201
## 3: b 33412
# Ключі
DT <- data.table(x = rep(c("a", "b", "c"), each = 100), y = rnorm(300))
# Встановлюємо ключ.
setkey(DT, x)
# Тепер, після встановлення ключа, вказавши, наприклад, 'а',
# data.table буде знати, що ми шукаємо по ключу.
head(DT['a'])
## x y
## 1: a 0.2595897
## 2: a 0.9175107
## 3: a -0.7223183
## 4: a -0.8082840
## 5: a -0.1413520
## 6: a 2.2570135
# JOIN
DT1 <- data.table(x = c('a', 'a', 'b', 'dt1'), y = 1:4)
DT2 <- data.table(x = c('a', 'b', 'dt2'), z = 5:7)
setkey(DT1, x)
setkey(DT2, x)
merge(DT1, DT2)
## x y z
## 1: a 1 5
## 2: a 2 5
## 3: b 3 6