source file: http://1drv.ms/1GpekJi

Если исходный файл показывается с крокозяброй вместо русского текста, нужно сменить кодировку c cp1251(Windows) на UTF-8(Unicode) в RStudio.

If Russian text seems to be srambled in source file, please, change encoding from cp1251(Windows) to UTF-8(Unicode) in RStudio.

1. menu Tools -> Global option / General / Default text encoding (UTF-8)
2. close source file (this file) and than reopen it.

Простые таблицы данных

Зададим таблицы данны (dataframe) для простых примеров соединения таблиц. Представьте себе: на скамейке перед подъездом сидят бабушки. Они хотят обсудить, какие мальчики и девочки могли бы составить пары. Они открыли ноутбук, запустили RStudio и … После подключения пакета dplyr мы создадим 2 таблицы mal (мальчики) и dev (девочки).

library(dplyr)
## 
## Attaching package: 'dplyr'
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
mal <-  data_frame(mal.name="Ваня", mal.age=40, mal.hobby = "велосипед")
# базовая функция rbind (bind by row) соединяет 2 таблицы ставя строки второй сразу после строк первой
# базовая функция cbind (bind by column) соединяет 2 таблицы ставя столбцы второй сразу после столбцов первой
row  <-  data_frame(mal.name="Миша", mal.age=19, mal.hobby = "лыжи")
mal  <-  rbind( mal,row) # добавим вторую строку
mal
## Source: local data frame [2 x 3]
## 
##   mal.name mal.age mal.hobby
## 1     Ваня      40 велосипед
## 2     Миша      19      лыжи

И еще 2 строки другим способом

mal[3,]  <-  data_frame("Дима", 35, "велосипед")
mal[4,]   <- data_frame("Гаврила",29, "мотоцикл")

и еще одну строку

row  <-  data_frame(mal.name="Юра", mal.age=44, mal.hobby = "лыжи")
mal  <-  rbind( mal,row) # добавим вторую строку
mal
## Source: local data frame [5 x 3]
## 
##   mal.name mal.age mal.hobby
## 1     Ваня      40 велосипед
## 2     Миша      19      лыжи
## 3     Дима      35 велосипед
## 4  Гаврила      29  мотоцикл
## 5      Юра      44      лыжи

Замечание 1 в отличие от языка SQL, в языке R точка – это обычный символ в имени, а не разделитель между именем таблицы и столбца. Мы тут используем такие приставки dev. и mal. чтобы имена столбцов были уникальными в 2 соединямемых таблицах.

Замечание 2 на семинаре мы пользовались командами

mal  <-  data.frame()
fix(mal)

и в визуальном редакторе таблиц определяли имена столбцов и их типы и вводили значения ячеек. С точки зрения воспроизводимости примеров такой способ не годится.

Теперь вводим таблицу девочек

dev  <-  data_frame(dev.name="Оля", dev.age=28, dev.hobby="лыжи")
dev[2,]  <-  data_frame("Зина", 19, "вышивание")
dev[3,]  <-  list("Лена" , 19, "велосипед")
row <-  data_frame(dev.name="Ира", dev.age=31, dev.hobby="лыжи")
dev<- rbind(dev,row) 

dev
## Source: local data frame [4 x 3]
## 
##   dev.name dev.age dev.hobby
## 1      Оля      28      лыжи
## 2     Зина      19 вышивание
## 3     Лена      19 велосипед
## 4      Ира      31      лыжи

Соединение таблиц (операция join)

Операция join – это одна из фундаментальных операций современных баз данных. Соединяя при пгомощи этой оперрации таблицы, мы получаем в качестве результата операции join таблицу в которой часть столбцов взята из первой таблицы, а часть стобцов из второй таблицы
Строки результата образуют не комбинации произвольных строк левой и правой таблицы, а только строк соответствующих друг другу. Как определяется это соответствие? Обычно накладывается условие равенства друг другу значений некоторых полей в соединяемых таблицах.

Внутреннее соединение таблиц

Например, бабушки у подъезда хотят рассмотреть в первую очередь те потенциальные пары, которые объединяют общие интересы ( mal.hobby == dev.hobby) Для этого подходит операция inner_join(левая_таблица, правая_таблица, by = условие_соединения)

inner_join(dev, mal,by=c("dev.hobby"="mal.hobby"))
## Source: local data frame [6 x 5]
## 
##   dev.name dev.age dev.hobby mal.name mal.age
## 1      Оля      28      лыжи     Миша      19
## 2      Оля      28      лыжи      Юра      44
## 3     Лена      19 велосипед     Ваня      40
## 4     Лена      19 велосипед     Дима      35
## 5      Ира      31      лыжи     Миша      19
## 6      Ира      31      лыжи      Юра      44

Вы видите в результате несколько раз посторяются строки из таблицы девочки и таблицы мальчики, у которых хобби позволяет им быть участниками нескольких потенциальных пар. С другой стороны, мальчик Гаврила и девочка Зина имеют более редкие увлечения и они совсем не упомянуты в результате, потому что операция inner_join исключает строки, которым нет соответсвия в другой табице из результата.

Результат соединения можно использовать далее как обычную таблицу (data frame) и применять к ним обычные операции filter(), arrange(),select(), summarise() и т.п.

Например бабушки захотели посмотреть пары, где возраст не отличается более чем на 15 лет и результат для них нужно упорядочить по имени девочки:

inner_join(dev, mal,by=c("dev.hobby"="mal.hobby")) %>%
  filter(abs(mal.age-dev.age)<=15) %>%
  arrange(dev.name)
## Source: local data frame [3 x 5]
## 
##   dev.name dev.age dev.hobby mal.name mal.age
## 1      Ира      31      лыжи     Миша      19
## 2      Ира      31      лыжи      Юра      44
## 3      Оля      28      лыжи     Миша      19

Внешние соединения таблиц

Иногда требуется включать строки из одной из таблиц в результат, даже если им нет соответсвия в другой таблице. Тогда используютс операции left_join(), right_join() и full_join(). Такие соединения назвают внешними

Если мы хотим левую таблицу сделать более приоритетной, то есть включать ее строки в результат, даже если для них нет соответствия в правой таблице, то мы используем операцию left_join()

left_join(dev, mal,by=c("dev.hobby"="mal.hobby")) 
## Source: local data frame [7 x 5]
## 
##   dev.name dev.age dev.hobby mal.name mal.age
## 1      Оля      28      лыжи     Миша      19
## 2      Оля      28      лыжи      Юра      44
## 3     Зина      19 вышивание       NA      NA
## 4     Лена      19 велосипед     Ваня      40
## 5     Лена      19 велосипед     Дима      35
## 6      Ира      31      лыжи     Миша      19
## 7      Ира      31      лыжи      Юра      44

Зина появилась в списке, а дефицит мальчиков с таким же хобби вышивание был “восполнен” “пустым мальчиком” то есть в этой строке все поля из таблицы mal заполненны пустыми значениями NA

В правом внешнем соединении появляется Гаврила на мотоцикле с “пустой девочкой”.

right_join(dev, mal,by=c("dev.hobby"="mal.hobby")) 
## Source: local data frame [7 x 5]
## 
##   dev.name dev.age dev.hobby mal.name mal.age
## 1     Лена      19 велосипед     Ваня      40
## 2      Оля      28      лыжи     Миша      19
## 3      Ира      31      лыжи     Миша      19
## 4     Лена      19 велосипед     Дима      35
## 5       NA      NA  мотоцикл  Гаврила      29
## 6      Оля      28      лыжи      Юра      44
## 7      Ира      31      лыжи      Юра      44

В полном внешнем соединении есть и Гаврила на мотоцикле с “пустой девочкой” и вышивающая Зина с “пустым мальчиком”.

full_join(dev, mal,by=c("dev.hobby"="mal.hobby")) 
## Source: local data frame [8 x 5]
## 
##   dev.name dev.age dev.hobby mal.name mal.age
## 1      Оля      28      лыжи     Миша      19
## 2      Оля      28      лыжи      Юра      44
## 3     Зина      19 вышивание       NA      NA
## 4     Лена      19 велосипед     Ваня      40
## 5     Лена      19 велосипед     Дима      35
## 6      Ира      31      лыжи     Миша      19
## 7      Ира      31      лыжи      Юра      44
## 8       NA      NA  мотоцикл  Гаврила      29

Соединения для отбора строк

Операции semi_join() и anti_join() в результат помешают строки только первой таблицы, а вторую таблицу используют для отбора строк первой.

Операция semi_join() выдаёт строки левой таблицы, для которых есть соответствующие строки в правой.

semi_join(dev, mal,by=c("dev.hobby"="mal.hobby")) 
## Source: local data frame [3 x 3]
## 
##   dev.name dev.age dev.hobby
## 1     Лена      19 велосипед
## 2      Оля      28      лыжи
## 3      Ира      31      лыжи

А операция anti_join() – наоборот, выдаёт строки левой таблицы, для которых нет соответствующих строк в правой.

anti_join(dev, mal,by=c("dev.hobby"="mal.hobby")) 
## Source: local data frame [1 x 3]
## 
##   dev.name dev.age dev.hobby
## 1     Зина      19 вышивание