1. Завантаження фалів із мережі.

Функція 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 все має працювати без проблем.

2. Обробка локальних безструктурних файлів.

Функція 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.

3. Читання excel файлів.

Завантажимо 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 файлами.

4. Читання XML.

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"

5. Робота з JSON.

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

6. Робота з data.table.

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