Соединение/сопоставление фреймов данных в R

У меня есть два фрейма данных.

Первый состоит из двух столбцов: x — глубина воды, y — температура на каждой глубине.

Во втором тоже два столбца, x тоже глубина воды, но на другой глубине по сравнению с первой таблицей. Второй столбец z — соленость.

Я хочу объединить две таблицы с помощью x, добавив z к первой таблице. Я научился соединять таблицы с помощью 'key' в tidyr , но это работает, только если ключи идентичны. x в этих двух таблицах не совпадают.

Что я хочу сделать, так это сопоставить глубину x в таблице 2 с глубиной в пределах 10% от глубины в таблице 1 (т.е. сопоставить 1,1 в таблице 2 x с 1,0 в таблице 1 x).

Как я могу это сделать?

Table 1
| x | y  |
|---|----|
| 1 | 25 |
| 2 | 26 |
| 3 | 27 |

Table 2
| x    | z  |
|------|----|
| 1.1  | 30 |
| 2.05 | 35 |
| 3.8  | 34 |

Я хочу

Table 1
| x | y  | z  |
|---|----|----|
| 1 | 25 | 30 |
| 2 | 26 | 35 |
| 3 | 27 | NA |

Первые два значения «x» совпадают (в пределах 10% абсолютной разницы) в Table 2. Третий нет.


person JianghuiDu    schedule 05.06.2018    source источник
comment
Вы видели пакет fuzzyjoin?   -  person Conor Neilson    schedule 05.06.2018
comment
Только что проверил. Кажется, это правильно. Спасибо! Так много пакетов, чтобы следовать!   -  person JianghuiDu    schedule 05.06.2018
comment
@JianghuiDu Я не думаю, что fuzzyjoin вам сильно поможет в этом случае. Лучше было бы предоставить воспроизводимый пример, чтобы можно было предоставить соответствующее решение.   -  person MKR    schedule 05.06.2018
comment
Я добавил пример.   -  person JianghuiDu    schedule 05.06.2018


Ответы (2)


Изменить после предоставления данных:

Взяв определение данных из сообщения @MKR:

library(fuzzyjoin)
fuzzy_left_join(Table_1, Table_2,match_fun = function(x,y)  y> x & y<=1.1*x )
# Joining by: "x"
#   x.x  y  x.y  z
# 1   1 25 1.10 30
# 2   2 26 2.05 35
# 3   3 27   NA NA

общие пояснения к поддельным данным (первый ответ)

поддельные данные

iris1 <- head(iris[1:2])
iris1
#   Sepal.Length Sepal.Width
# 1          5.1         3.5
# 2          4.9         3.0
# 3          4.7         3.2
# 4          4.6         3.1
# 5          5.0         3.6
# 6          5.4         3.9

iris2 <- head(iris[c(1,3)])
set.seed(1)

# add noise
iris2$Sepal.Length <- iris2$Sepal.Length + rnorm(6,sd=0.05)

# shuffle rows
iris2 <- iris2[sample(seq(nrow(iris2))),]

iris2
#   Sepal.Length Petal.Length
# 5     5.016475          1.4
# 2     4.909182          1.4
# 4     4.679764          1.5
# 6     5.358977          1.7
# 3     4.658219          1.3
# 1     5.068677          1.4

код

library(fuzzyjoin)
fuzzy_left_join(iris1,iris2,match_fun= function(x,y) y>0.99*x & y<1.01*x )
# Joining by: "Sepal.Length"
# Sepal.Length.x Sepal.Width Sepal.Length.y Petal.Length
# 1            5.1         3.5       5.068677          1.4
# 2            4.9         3.0       4.909182          1.4
# 3            4.7         3.2       4.679764          1.5
# 4            4.7         3.2       4.658219          1.3
# 5            4.6         3.1             NA           NA
# 6            5.0         3.6       5.016475          1.4
# 7            5.4         3.9       5.358977          1.7

Мы видим, что некоторые строки совпадают очень хорошо, давайте посмотрим на исключения. В строке номер 4 было добавлено слишком много шума в iris2, поэтому она была объединена со строкой 3, в которой есть 2 совпадения. Поскольку я выбрал левое соединение, строка 4 по-прежнему отображается, но с NAs для столбцов iris2.

Как я понимаю:

  • Соединяемые столбцы будут расширены
  • Функция принимает эти длинные столбцы (здесь элементы 6*6==36) в качестве аргументов.
  • Мы применяем векторизованные функции (такие как < или & в данном случае), чтобы вернуть логический вектор, который будет фильтровать эти длинные столбцы, чтобы построить вывод data.frame.

distance_left_join проще в использовании, но это абсолютное расстояние, а не относительное.

person Moody_Mudskipper    schedule 05.06.2018
comment
Большой! Это работает! Еще один сопутствующий вопрос. Что делать, если у меня есть несколько ключей, например, ключ1, ключ2, ключ3. Я хочу точно сопоставить key1 и key2, но только key3 при определенных ограничениях? - person JianghuiDu; 05.06.2018
comment
Во-первых, обратите внимание, что я отредактировал свой ответ, думал, что вывод не изменится, я использовал multi_match_fun неправильно - person Moody_Mudskipper; 05.06.2018
comment
match_fun может быть списком функций, вы должны явно указать аргумент by, чтобы быть в безопасности, а затем вы можете предоставить список функций, которые будут применяться к каждой паре - person Moody_Mudskipper; 05.06.2018
comment
если вы укажете только один, он будет использоваться во всех парах - person Moody_Mudskipper; 05.06.2018

Опция с использованием sqldf может быть достигнута как:

library(sqldf)


sqldf("select T1.x, T1.y, A.z from Table_1 T1
left join (select Table_1.x, Table_1.y, Table_2.z from Table_1 
   left join Table_2 where round((100*abs(Table_1.x - Table_2.x)/Table_1.x),0) <= 10) A 
on T1.x = A.x")

#   x  y  z
# 1 1 25 30
# 2 2 26 35
# 3 3 27 NA

Данные:

Table_1 <- read.table(text = 
"x  y  
1   25  
2   26  
3   27",
header = TRUE)


Table_2 <- read.table(text = 
"x  z  
1.1    30  
2.05   35  
3.8    34",
header = TRUE)
person MKR    schedule 05.06.2018
comment
Может быть, вам следует объяснить логику, стоящую за этим, это не так ясно, тем более что ОП имел в виду некоторые правила. - person Moody_Mudskipper; 05.06.2018
comment
Я думаю, вы ошибаетесь в моем намерении. Я присоединяю Table 2 к Table 1 на основе относительного расстояния до x, а не просто сопоставляю их по порядку. Или, может быть, я запутался, как и где вы вводили критерии соответствия? - person JianghuiDu; 05.06.2018
comment
Я согласен. Я ошибся в понимании вопроса. Я исправляю это. - person MKR; 05.06.2018
comment
Спасибо! Отлично, чтобы попробовать их все. - person JianghuiDu; 06.06.2018