Введение

До этого мы работали с одной таблицей. Но это бывает очень редко, так как данные хранятся в разных таблицах. Поэтому важно уметь комбинировать их. Все эти таблицы связаны друг с другом и называются реляционными данными. Реляционные данные - совокупность взаимосвязанных таблиц. Такие данные хранятся в СУБД(система управления базами данных). Для их управления используется SQL. То, что мы будем проходить на этом занятии очень похоже на SQL, но немного проще. Все нужные нам функции находятся в пакете dplyr.

Пример реляционных данных

Мы рассмотрим как работать с реляционными данными на примере знакомых нам данных из пакета nycflights13.

Главной таблицей в этом пакете является flights, которую мы использовали на предыдущем занятии. Также имеется 4 таблице, связанных с ней:

  • airlines - позволяет найти полное название перевозчика по его сокращенному коду.
  • airports - предоставляет информацию о каждом аэропорте, идентифицируемом с помощью кода faa.
  • planes - информация о каждом самолете, идентифицируемом по его бортовому номеру.
  • weather - предоставляет почасовые сведения о погодных условиях в каждом из аэропортов Нью-Йорка.
## # A tibble: 6 x 2
##   carrier name                    
##   <chr>   <chr>                   
## 1 9E      Endeavor Air Inc.       
## 2 AA      American Airlines Inc.  
## 3 AS      Alaska Airlines Inc.    
## 4 B6      JetBlue Airways         
## 5 DL      Delta Air Lines Inc.    
## 6 EV      ExpressJet Airlines Inc.
## # A tibble: 6 x 8
##   faa   name                       lat   lon   alt    tz dst   tzone       
##   <chr> <chr>                    <dbl> <dbl> <dbl> <dbl> <chr> <chr>       
## 1 04G   Lansdowne Airport         41.1 -80.6  1044    -5 A     America/New…
## 2 06A   Moton Field Municipal A…  32.5 -85.7   264    -6 A     America/Chi…
## 3 06C   Schaumburg Regional       42.0 -88.1   801    -6 A     America/Chi…
## 4 06N   Randall Airport           41.4 -74.4   523    -5 A     America/New…
## 5 09J   Jekyll Island Airport     31.1 -81.4    11    -5 A     America/New…
## 6 0A9   Elizabethton Municipal …  36.4 -82.2  1593    -5 A     America/New…
## # A tibble: 6 x 9
##   tailnum  year type       manufacturer   model  engines seats speed engine
##   <chr>   <int> <chr>      <chr>          <chr>    <int> <int> <int> <chr> 
## 1 N10156   2004 Fixed win… EMBRAER        EMB-1…       2    55    NA Turbo…
## 2 N102UW   1998 Fixed win… AIRBUS INDUST… A320-…       2   182    NA Turbo…
## 3 N103US   1999 Fixed win… AIRBUS INDUST… A320-…       2   182    NA Turbo…
## 4 N104UW   1999 Fixed win… AIRBUS INDUST… A320-…       2   182    NA Turbo…
## 5 N10575   2002 Fixed win… EMBRAER        EMB-1…       2    55    NA Turbo…
## 6 N105UW   1999 Fixed win… AIRBUS INDUST… A320-…       2   182    NA Turbo…
## # A tibble: 6 x 15
##   origin  year month   day  hour  temp  dewp humid wind_dir wind_speed
##   <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>
## 1 EWR     2013     1     1     1  39.0  26.1  59.4      270      10.4 
## 2 EWR     2013     1     1     2  39.0  27.0  61.6      250       8.06
## 3 EWR     2013     1     1     3  39.0  28.0  64.4      240      11.5 
## 4 EWR     2013     1     1     4  39.9  28.0  62.2      250      12.7 
## 5 EWR     2013     1     1     5  39.0  28.0  64.4      260      12.7 
## 6 EWR     2013     1     1     6  37.9  28.0  67.2      240      11.5 
## # … with 5 more variables: wind_gust <dbl>, precip <dbl>, pressure <dbl>,
## #   visib <dbl>, time_hour <dttm>

Взаимосвязь этих таблиц можно с помощью картинки.

Мы видим, что для пакета nycflights13:

  • flights соединяется с planes посредством переменной tailnum
  • flights соединяется с airlines посредством переменной carrier
  • flights соединяется с airports посредством двух переменных origin и dest
  • flights соединяется с weather посредством переменных origin, year, month, day и hour

Переменные, используемые для установления связи между каждой парой таблиц, называются ключами. Ключ - это переменная(или набор переменных), которая однозначно идентифицирует наблюдение. Например, каждый самолет можно однозначно идентифицировать по его бортовому номеру(tailnum). Но для идентификации наблюдения в таблице погодных условий(weather), требуются пять переменных: origin, year, month, day и hour.

Ключи бывают двух типов:

  • первичный ключ - идентифицирует наблюдение в собственной таблице(например, planes$tailnum - первичный ключ, поскольку он однозначно идентифицирует каждый самолет в таблице planes)
  • внешний ключ - однозначно идентифицирует наблюдение в другой таблице(например, flights$tailnum - внешний ключ, так как он устанавливает для каждого авиарейса однозначно определяемый самолет в таблице flights)

Переменная может служить одновременно первичным и внешним ключом(например переменная origin является частью первичного ключа для таблицы weather и одновременно внешним ключом для таблицы airports).

Как только вы определите первичные ключи в своих таблицах, целесообразно проверить, что они действительно однозначно определяют каждое значение.

Можно это сделать с помощью следующей команды:

## # A tibble: 0 x 2
## # … with 2 variables: tailnum <chr>, n <int>

Видим, что фрейм пустой, это означает, что нет таких значений первичного ключа, которые появлялись дважды. Следовательно, они уникальны.

## # A tibble: 1,092 x 5
##     year month   day origin     n
##    <int> <int> <int> <chr>  <int>
##  1  2013     1     1 EWR       22
##  2  2013     1     1 JFK       22
##  3  2013     1     1 LGA       23
##  4  2013     1     2 EWR       24
##  5  2013     1     2 JFK       24
##  6  2013     1     2 LGA       24
##  7  2013     1     3 EWR       24
##  8  2013     1     3 JFK       24
##  9  2013     1     3 LGA       24
## 10  2013     1     4 EWR       24
## # … with 1,082 more rows

В данном случае видим, что для каждого набора origin, year, month и day имеется несколько значений. Поэтому этот набор не является первичным ключом. Нужно добавить переменную hour, чтобы определить правильно первичный ключ.

## # A tibble: 3 x 6
##    year month   day  hour origin     n
##   <int> <int> <int> <int> <chr>  <int>
## 1  2013    11     3     1 EWR        2
## 2  2013    11     3     1 JFK        2
## 3  2013    11     3     1 LGA        2

Иногда таблица не имееет явного первичного ключа: каждая строка является наблюдением, но ни одна комбинация переменных не обеспечивает ее надежную информацию. Например, что является первичным ключом в таблице flights? Можно подумать, что мы могли бы использовать комбинацию даты и номера авиарейса или бортового номера. Но это не дает результатов.

## # A tibble: 29,768 x 5
##     year month   day flight     n
##    <int> <int> <int>  <int> <int>
##  1  2013     1     1      1     2
##  2  2013     1     1      3     2
##  3  2013     1     1      4     2
##  4  2013     1     1     11     3
##  5  2013     1     1     15     2
##  6  2013     1     1     21     2
##  7  2013     1     1     27     4
##  8  2013     1     1     31     2
##  9  2013     1     1     32     2
## 10  2013     1     1     35     2
## # … with 29,758 more rows
## # A tibble: 64,928 x 5
##     year month   day tailnum     n
##    <int> <int> <int> <chr>   <int>
##  1  2013     1     1 N0EGMQ      2
##  2  2013     1     1 N11189      2
##  3  2013     1     1 N11536      2
##  4  2013     1     1 N11544      3
##  5  2013     1     1 N11551      2
##  6  2013     1     1 N12540      2
##  7  2013     1     1 N12567      2
##  8  2013     1     1 N13123      2
##  9  2013     1     1 N13538      3
## 10  2013     1     1 N13566      3
## # … with 64,918 more rows

То есть каждый номер авиарейса используется несколько раз за день, как и некоторые самолеты совершают несколько авиарейсов в день. Можно дать ключ таким данным просто пронумеровав их с помощью функции row_number.

## # A tibble: 336,776 x 5
##      key  year month   day flight
##    <int> <int> <int> <int>  <int>
##  1   160  2013     1     1      1
##  2   292  2013     1     1      1
##  3   113  2013     1     1      3
##  4   296  2013     1     1      3
##  5   202  2013     1     1      4
##  6   488  2013     1     1      4
##  7   709  2013     1     1      6
##  8   645  2013     1     1      7
##  9   475  2013     1     1      8
## 10   625  2013     1     1      9
## # … with 336,766 more rows

Первичный ключ и соотвествующий внешний ключ в другой таблице образуют отношение. Как правило, такие отношения относятся к типу “один ко многим”(например, каждому авиарейсу соотвествует один самолёт, но каждому самолёту соотвествует множество авиарейсов). Встречаются отношения “один к одному” и “многие ко многим”(между таблицами airlines и airports: каждая авиакомпания совершает полеты во многие аэропорты, и каждый аэропорт принимает самолеты многих авиакомпаний).

Мутирующие соединения

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

Предположим, что мы хотим добавить полное название авиакомпании для каждого перелета в таблице flights2. Фреймы данных airlines и flighths2 можно соединить с помощью функции left_join.

## # A tibble: 336,776 x 7
##     year month   day  hour tailnum carrier name                    
##    <int> <int> <int> <dbl> <chr>   <chr>   <chr>                   
##  1  2013     1     1     5 N14228  UA      United Air Lines Inc.   
##  2  2013     1     1     5 N24211  UA      United Air Lines Inc.   
##  3  2013     1     1     5 N619AA  AA      American Airlines Inc.  
##  4  2013     1     1     5 N804JB  B6      JetBlue Airways         
##  5  2013     1     1     6 N668DN  DL      Delta Air Lines Inc.    
##  6  2013     1     1     5 N39463  UA      United Air Lines Inc.   
##  7  2013     1     1     6 N516JB  B6      JetBlue Airways         
##  8  2013     1     1     6 N829AS  EV      ExpressJet Airlines Inc.
##  9  2013     1     1     6 N593JB  B6      JetBlue Airways         
## 10  2013     1     1     6 N3ALAA  AA      American Airlines Inc.  
## # … with 336,766 more rows

Появилась новая переменная name, в которой теперь хранится полное название компании.

Давайте разберем этот и другие виды соединений на более простых примерах. Возьмем вот такие 2 таблицы

Также будем использовать визуальное представление таких соединений, для пониманию. Цветные столбцы представляют переменную key: они используются для установления соответствия между строками обеих таблиц. Серый столбец - это столбец значений.

Соединение это способ связывания каждой строки x с нулем, одной или несколькими строками y.

В фактическом соединении совпадения будут обозначаться точками. Количество точек = количеству совпадений = количеству строк в результате.

Такой тип соединения называется внутренним(inner_join). Оно устаналивает соответствие между парами наблюдений, если их ключи совпадают.

Результатом внутреннего соединения является новый дата фрейм, который содержит ключ, значения x и значения y. Для того, чтобы сообщить какая из переменных является ключом, мы используем параметр by.

## # A tibble: 2 x 3
##     key val_x val_y
##   <dbl> <chr> <chr>
## 1     1 x1    y1   
## 2     2 x2    y2

Несовподающие строки не включаются в результат. Это означает, что, как праивло, при таком соединении теряются наблюдения. С этим нужно быть аккуратным.

Еще одним видом являются внешние соединения. Внутреннее соединение сохраняет наблюдения, которые встречаются в обеих таблицах. Внешнее соединение сохраняет наблюдения, встречающиеся хотя бы в одной из таблиц. Различают три типа внешних соединений:

  • левое соединение(left_join) - сохраняет все наблюдения в x
  • правое соединение(right_join) - сохраняет все наблюдения в y
  • полное соединение(full_join) - сохраняет все наблюдения в x и y

Эти соединения работают, добавляя дополнительные “виртуальное” наблюдение в каждую таблицу.ю Данное наблюдение имеет ключ, который совпадает с любым ключом(если никакой другой ключ не совпадает), и значение NA.

Представим это графически.

Или можно это представить в виде множеств.

Чаще всего выбирается левое соединение, так как ,обычно, у вас имеется одна таблица(например,для регрессии), которая “наполняется” с помощью других таблиц. Старайтесь использовать именно его.

Рассмотрим ситуации, когда у нас неуникальные ключи;

  1. Неуникальные ключи имеются в одной таблице. Это может быть полезным, если вы хотите добавить дополнительную информацию в тех случаях, когда имеется типичное отношение “один ко многим”.

## # A tibble: 4 x 3
##     key val_x val_y
##   <dbl> <chr> <chr>
## 1     1 x1    y1   
## 2     2 x2    y2   
## 3     2 x3    y2   
## 4     1 x4    y1
  1. Неуникальные ключи имеются в обеих таблицах. Обычно это является ошибкой! При такой ситуации вы получаете всевозможные ситуации, то есть декартово произведение.

## # A tibble: 6 x 3
##     key val_x val_y
##   <dbl> <chr> <chr>
## 1     1 x1    y1   
## 2     2 x2    y2   
## 3     2 x2    y3   
## 4     2 x3    y2   
## 5     2 x3    y3   
## 6     3 x4    y4

Если названия переменных в ваших таблицах не совпадает, то можно передать массив соответствия.

## # A tibble: 336,776 x 15
##     year month   day  hour origin dest  tailnum carrier name    lat   lon
##    <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>   <chr> <dbl> <dbl>
##  1  2013     1     1     5 EWR    IAH   N14228  UA      Geor…  30.0 -95.3
##  2  2013     1     1     5 LGA    IAH   N24211  UA      Geor…  30.0 -95.3
##  3  2013     1     1     5 JFK    MIA   N619AA  AA      Miam…  25.8 -80.3
##  4  2013     1     1     5 JFK    BQN   N804JB  B6      <NA>   NA    NA  
##  5  2013     1     1     6 LGA    ATL   N668DN  DL      Hart…  33.6 -84.4
##  6  2013     1     1     5 EWR    ORD   N39463  UA      Chic…  42.0 -87.9
##  7  2013     1     1     6 EWR    FLL   N516JB  B6      Fort…  26.1 -80.2
##  8  2013     1     1     6 LGA    IAD   N829AS  EV      Wash…  38.9 -77.5
##  9  2013     1     1     6 JFK    MCO   N593JB  B6      Orla…  28.4 -81.3
## 10  2013     1     1     6 LGA    ORD   N3ALAA  AA      Chic…  42.0 -87.9
## # … with 336,766 more rows, and 4 more variables: alt <dbl>, tz <dbl>,
## #   dst <chr>, tzone <chr>
## # A tibble: 336,776 x 15
##     year month   day  hour origin dest  tailnum carrier name    lat   lon
##    <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>   <chr> <dbl> <dbl>
##  1  2013     1     1     5 EWR    IAH   N14228  UA      Newa…  40.7 -74.2
##  2  2013     1     1     5 LGA    IAH   N24211  UA      La G…  40.8 -73.9
##  3  2013     1     1     5 JFK    MIA   N619AA  AA      John…  40.6 -73.8
##  4  2013     1     1     5 JFK    BQN   N804JB  B6      John…  40.6 -73.8
##  5  2013     1     1     6 LGA    ATL   N668DN  DL      La G…  40.8 -73.9
##  6  2013     1     1     5 EWR    ORD   N39463  UA      Newa…  40.7 -74.2
##  7  2013     1     1     6 EWR    FLL   N516JB  B6      Newa…  40.7 -74.2
##  8  2013     1     1     6 LGA    IAD   N829AS  EV      La G…  40.8 -73.9
##  9  2013     1     1     6 JFK    MCO   N593JB  B6      John…  40.6 -73.8
## 10  2013     1     1     6 LGA    ORD   N3ALAA  AA      La G…  40.8 -73.9
## # … with 336,766 more rows, and 4 more variables: alt <dbl>, tz <dbl>,
## #   dst <chr>, tzone <chr>

Фильтрующие соединения

Теперь рассмотрим фильтрующие соединения. Они сопоставляют наблюдения точно так же, как и мутирующие, но влияют на наблюдения, а не на переменные. Они бывают двух типов:

  • semi_join(x, y) - сохраняет все наблюдения в x, для которых имеется сопоставление в y.
  • anti_join(x, y) - отбрасывает все наблюдения в x, для которых имеется совпадение в y.

Предположим, например, мы нашлим топ-10 популярных пунктов назначения.

После чего вы хотели бы увидеть, только те перелеты, где пункт назначения был в топ-10. Это можно сделать следующим образом.

## # A tibble: 141,145 x 8
##     year month   day  hour origin dest  tailnum carrier
##    <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>  
##  1  2013     1     1     5 JFK    MIA   N619AA  AA     
##  2  2013     1     1     6 LGA    ATL   N668DN  DL     
##  3  2013     1     1     5 EWR    ORD   N39463  UA     
##  4  2013     1     1     6 EWR    FLL   N516JB  B6     
##  5  2013     1     1     6 JFK    MCO   N593JB  B6     
##  6  2013     1     1     6 LGA    ORD   N3ALAA  AA     
##  7  2013     1     1     6 JFK    LAX   N29129  UA     
##  8  2013     1     1     6 EWR    SFO   N53441  UA     
##  9  2013     1     1     5 JFK    BOS   N708JB  B6     
## 10  2013     1     1     6 LGA    FLL   N595JB  B6     
## # … with 141,135 more rows

Ту же операцию может сделать и semi_join.

## # A tibble: 141,145 x 8
##     year month   day  hour origin dest  tailnum carrier
##    <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>  
##  1  2013     1     1     5 JFK    MIA   N619AA  AA     
##  2  2013     1     1     6 LGA    ATL   N668DN  DL     
##  3  2013     1     1     5 EWR    ORD   N39463  UA     
##  4  2013     1     1     6 EWR    FLL   N516JB  B6     
##  5  2013     1     1     6 JFK    MCO   N593JB  B6     
##  6  2013     1     1     6 LGA    ORD   N3ALAA  AA     
##  7  2013     1     1     6 JFK    LAX   N29129  UA     
##  8  2013     1     1     6 EWR    SFO   N53441  UA     
##  9  2013     1     1     5 JFK    BOS   N708JB  B6     
## 10  2013     1     1     6 LGA    FLL   N595JB  B6     
## # … with 141,135 more rows

Возникает вопрос, а зачем вообще нужен semi_join, если это можно сделать через filter? Представьте, что вы используете не один фильтр а несколько. Например давайте найдем 10 дней с наибольшей задержкой отправления.

А теперь найдем все авиарейсы, которые были в эти дни.

## # A tibble: 9,164 x 8
##     year month   day  hour origin dest  tailnum carrier
##    <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>  
##  1  2013     1     9    23 JFK    PSE   N603JB  B6     
##  2  2013     1     9    23 JFK    BQN   N563JB  B6     
##  3  2013     1     9     5 EWR    CLT   N566UW  US     
##  4  2013     1     9     5 LGA    IAH   N17627  UA     
##  5  2013     1     9     5 EWR    IAH   N825UA  UA     
##  6  2013     1     9     5 JFK    MIA   N5CSAA  AA     
##  7  2013     1     9     5 JFK    BQN   N571JB  B6     
##  8  2013     1     9     6 LGA    ATL   N542MQ  MQ     
##  9  2013     1     9     6 LGA    BOS   N747UW  US     
## 10  2013     1     9     6 LGA    ATL   N955AT  FL     
## # … with 9,154 more rows

А как бы вы написали такое же через filter?

Визуализация для semi_join представлена ниже.

Важно отметить, что semi_join не дублирует строки.

Функция anti_join делает все наоборот, выводит все наблюдения, которые не соответствуют нашему фильтру.

Например:

## # A tibble: 195,631 x 8
##     year month   day  hour origin dest  tailnum carrier
##    <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>  
##  1  2013     1     1     5 EWR    IAH   N14228  UA     
##  2  2013     1     1     5 LGA    IAH   N24211  UA     
##  3  2013     1     1     5 JFK    BQN   N804JB  B6     
##  4  2013     1     1     6 LGA    IAD   N829AS  EV     
##  5  2013     1     1     6 JFK    PBI   N793JB  B6     
##  6  2013     1     1     6 JFK    TPA   N657JB  B6     
##  7  2013     1     1     6 LGA    DFW   N3DUAA  AA     
##  8  2013     1     1     6 EWR    LAS   N76515  UA     
##  9  2013     1     1     6 EWR    PBI   N644JB  B6     
## 10  2013     1     1     6 LGA    MSP   N971DL  DL     
## # … with 195,621 more rows

Операции над множествами

Последним видом операций явлюятся операции над множествами. Они достаточно редкие, но бывают очень полезны. Приведенные ниже функции принимают на вход две таблицы x и y, которые имеют одни и те же переменные.

  • intersect(x, y) - возвращает только наблюдения, содержащиеся одновременно в x и y (\(x \cap y\))
  • union(x, y) - возращает уникальные наблюдения, которые содержатся в x и y
  • setdiff(x,y) - возвращает наблюдения, которые есть в x, но нет в y (\(x / y\))

Представим, что у нас имеются следующие таблицы.

## # A tibble: 2 x 2
##       x y    
##   <dbl> <chr>
## 1     1 one  
## 2     2 one
## # A tibble: 2 x 2
##       x y    
##   <dbl> <chr>
## 1     1 one  
## 2     1 two

Применим эти функции на них.

## # A tibble: 1 x 2
##       x y    
##   <dbl> <chr>
## 1     1 one
## # A tibble: 3 x 2
##       x y    
##   <dbl> <chr>
## 1     1 one  
## 2     2 one  
## 3     1 two
## # A tibble: 1 x 2
##       x y    
##   <dbl> <chr>
## 1     2 one
## # A tibble: 1 x 2
##       x y    
##   <dbl> <chr>
## 1     1 two

Функция union очень удобна в следующей ситуации. Представьте, что вы скачали данные по доходностям облигаций из Bloomberg и из Cbonds. Каждое агентсво имеет свои данные, у кого-то есть больше информации по одним облигациям, у кого-то по другим, но есть и пересекающиеся облигации. Чтобы выбрать все доступные вам наблюдения, можно использовать функцию union.

Соединение нескольких таблиц

Если данные поступают к вам частями, то скорее всего вам хотелось бы их соединить. Для этого существует 2 функции:

  • bind_rows(x, y) - склеивание по строкам
  • bind_cols(x, y) - склеивание по столбцам
## # A tibble: 2 x 2
##       x y    
##   <dbl> <chr>
## 1     1 one  
## 2     2 one
## # A tibble: 2 x 2
##       x y    
##   <dbl> <chr>
## 1     1 one  
## 2     1 two
## # A tibble: 4 x 2
##       x y    
##   <dbl> <chr>
## 1     1 one  
## 2     2 one  
## 3     1 one  
## 4     1 two
## # A tibble: 2 x 4
##       x y        x1 y1   
##   <dbl> <chr> <dbl> <chr>
## 1     1 one       1 one  
## 2     2 one       1 two

Эти функции удобно использовать, если у вас много таких табличек и все они находятся в листе.

## # A tibble: 4 x 2
##       x y    
##   <dbl> <chr>
## 1     1 one  
## 2     2 one  
## 3     1 one  
## 4     1 two
## # A tibble: 2 x 4
##       x y        x1 y1   
##   <dbl> <chr> <dbl> <chr>
## 1     1 one       1 one  
## 2     2 one       1 two

Другие виды соединения

Иногда бывает, что вам нужно соединить данные не с одинаковым ключом. К примеру даны котировки акции какой-то компании.

## # A tibble: 4 x 2
##   Date     Yield
##   <chr>    <dbl>
## 1 01012019  101 
## 2 02012019  104.
## 3 03012019  107.
## 4 04012019  109.

Предположим, что вы хотите понять как котировка зависит от курса евро. Для построения такой модели вам нужна табличка с переменными Date, Yield и Euro. У вас имеется табличка с курсом евро.

## # A tibble: 3 x 2
##   Date      Euro
##   <chr>    <dbl>
## 1 02012019  75  
## 2 03012019  74.2
## 3 04012019  73.1

Все, что нужно это использовать left_join.

## # A tibble: 4 x 3
##   Date     Yield  Euro
##   <chr>    <dbl> <dbl>
## 1 01012019  101   NA  
## 2 02012019  104.  75  
## 3 03012019  107.  74.2
## 4 04012019  109.  73.1

Можем видеть, что значение курса для 1 января отсутствует. Таких отсутствующих значений может быть очень много. Это означает, что мы теряем большое количество наблюдений. Есть смысл в том, чтобы для курса 1 января взять ближайшее доступное нам значение. К примеру, за 2 января. Эта цифра будет близка к реальному значению. И в итоге получить следующую таблицу.

## # A tibble: 4 x 3
##   Date     Yield  Euro
##   <chr>    <dbl> <dbl>
## 1 01012019  101   75  
## 2 02012019  104.  75  
## 3 03012019  107.  74.2
## 4 04012019  109.  73.1

Различные варианты таких условий можно задавать самому или с помощью пакета fuzzyjoin.