У меня есть два data.frame
S с несколькими общими столбцами (здесь: date
, city
, ctry
, и ( other_
) number
).
Теперь я хотел бы объединить их в приведенных выше столбцах, но допустил бы некоторые различия:
threshold.numbers <- 3
threshold.date <- 5 # in days
Если разница между date
записями составляет > threshold.date
(в днях) или > threshold.numbers
, я не хочу, чтобы строки были объединены. Точно так же, если запись в city
является подстрокой записи другого df
в city
столбце, я хочу, чтобы строки были объединены. [Если у кого есть лучшее представление о том , чтобы проверить на фактическое название городов сходство, я был бы рад услышать об этом.] (И держать первые df
«s вхождений date
, city
и country
но оба ( other_
) number
колонны и все остальные столбцы в разделе df
.
Рассмотрим следующий пример:
df1 <- data.frame(date = c("2003-08-29", "1999-06-12", "2000-08-29", "1999-02-24", "2001-04-17",
"1999-06-30", "1999-03-16", "1999-07-16", "2001-08-29", "2002-07-30"),
city = c("Berlin", "Paris", "London", "Rome", "Bern",
"Copenhagen", "Warsaw", "Moscow", "Tunis", "Vienna"),
ctry = c("Germany", "France", "UK", "Italy", "Switzerland",
"Denmark", "Poland", "Russia", "Tunisia", "Austria"),
number = c(10, 20, 30, 40, 50, 60, 70, 80, 90, 100),
col = c("apple", "banana", "pear", "banana", "lemon", "cucumber", "apple", "peach", "cherry", "cherry"))
df2 <- data.frame(date = c("2003-08-29", "1999-06-12", "2000-08-29", "1999-02-24", "2001-04-17", # all identical to df1
"1999-06-29", "1999-03-14", "1999-07-17", # all 1-2 days different
"2000-01-29", "2002-07-01"), # all very different (> 2 weeks)
city = c("Berlin", "East-Paris", "near London", "Rome", # same or slight differences
"Zurich", # completely different
"Copenhagen", "Warsaw", "Moscow", "Tunis", "Vienna"), # same
ctry = c("Germany", "France", "UK", "Italy", "Switzerland", # all the same
"Denmark", "Poland", "Russia", "Tunisia", "Austria"),
other_number = c(13, 17, 3100, 45, 51, 61, 780, 85, 90, 101), # slightly different to very different
other_col = c("yellow", "green", "blue", "red", "purple", "orange", "blue", "red", "black", "beige"))
Теперь я хотел бы объединить data.frames
и получитьdf
строки где строки объединяются, если вышеуказанные условия выполнены.
(Первый столбец только для вашего удобства: за первой цифрой, которая обозначает исходный регистр, показано, были ли объединены строки (.
) или есть ли строки из df1
( 1
) или df2
( 2
).
date city ctry number other_col other_number other_col2 #comment
1. 2003-08-29 Berlin Germany 10 apple 13 yellow # matched on date, city, number
2. 1999-06-12 Paris France 20 banana 17 green # matched on date, city similar, number - other_number == threshold.numbers
31 2000-08-29 London UK 30 pear <NA> <NA> # not matched: number - other_number > threshold.numbers
32 2000-08-29 near London UK <NA> <NA> 3100 blue #
41 1999-02-24 Rome Italy 40 banana <NA> <NA> # not matched: number - other_number > threshold.numbers
42 1999-02-24 Rome Italy <NA> <NA> 45 red #
51 2001-04-17 Bern Switzerland 50 lemon <NA> <NA> # not matched: cities different (dates okay, numbers okay)
52 2001-04-17 Zurich Switzerland <NA> <NA> 51 purple #
6. 1999-06-30 Copenhagen Denmark 60 cucumber 61 orange # matched: date difference < threshold.date (cities okay, dates okay)
71 1999-03-16 Warsaw Poland 70 apple <NA> <NA> # not matched: number - other_number > threshold.numbers (dates okay)
72 1999-03-14 Warsaw Poland <NA> <NA> 780 blue #
81 1999-07-16 Moscow Russia 80 peach <NA> <NA> # not matched: number - other_number > threshold.numbers (dates okay)
82 1999-07-17 Moscow Russia <NA> <NA> 85 red #
91 2001-08-29 Tunis Tunisia 90 cherry <NA> <NA> # not matched: date difference < threshold.date (cities okay, dates okay)
92 2000-01-29 Tunis Tunisia <NA> <NA> 90 black #
101 2002-07-30 Vienna Austria 100 cherry <NA> <NA> # not matched: date difference < threshold.date (cities okay, dates okay)
102 2002-07-01 Vienna Austria <NA> <NA> 101 beige #
Я пробовал разные реализации их объединения, но не смог реализовать порог.
РЕДАКТИРОВАТЬ Извинения за неясную формулировку - я хотел бы сохранить все строки и получить индикатор того, соответствует ли строка, не соответствует ли и от df1 или не соответствует и от df2.
псевдокод:
if there is a case where abs("date_df2" - "date_df1") <= threshold.date:
if "ctry_df2" == "ctry_df1":
if "city_df2" ~ "city_df1":
if abs("number_df2" - "number_df1") <= threshold.numbers:
merge and go to next row in df2
else:
add row to df1```
.
?Ответы:
Вот решение, которое использует мой пакет safejoin , в этом случае оборачивая пакет fuzzyjoin .
Мы можем использовать
by
аргумент для указания сложного условия, используя функциюX()
для получения значенияdf1
иY()
для получения значения изdf2
.Если ваши реальные таблицы большие, это может быть медленно или невозможно, так как это делает декартово произведение, но здесь это работает хорошо.
Нам нужно полное соединение (сохранить все строки и объединить то, что можно объединить), и мы хотим сохранить первое значение при их присоединении и принять следующее значение по-другому, это означает, что мы хотим справиться с конфликтом столбцы названы одинаково путем объединения, поэтому мы используем аргумент
conflict = dplyr::coalesce
вывод :
Создано в 2019-11-13 пакетом представлением (v0.3.0)
К сожалению, fuzzyjoin принуждает все столбцы в матрице при выполнении мультисоединения , а safejoin оборачивает fuzzyjoin, поэтому мы должны преобразовать переменные в соответствующий тип внутри аргумента by, это объясняет первые строки в
by
аргументе.Подробнее о безопасном присоединении : https://github.com/moodymudskipper/safejoin
источник
Сначала я превратил названия городов в символьные векторы, поскольку (если я правильно понял) вы хотите включить названия городов, содержащиеся в df2.
Затем объедините их по стране:
Библиотека
stringr
позволит вам увидеть, находится ли city.x внутри city.y здесь (см. Последний столбец):Тогда вы можете получить разницу в днях между датами:
и разница в цифрах:
Вот как выглядит итоговый фрейм данных:
Но мы хотим отбросить вещи, где city.x не был найден в city.y, где разность дней больше 5 или разница чисел больше 3:
Остаются три строки, которые у вас были выше (которые содержали точки в столбце 1).
Теперь мы можем отбросить три столбца, которые мы создали, а также дату и город из df2:
источник
Шаг 1: Объедините данные, основанные на "city" и "ctry":
Шаг 2. Удалите строки, если разница между записями даты составляет> threshold.date (в днях):
Шаг 3: Удалить строки, если разница между числами> threshhold.number:
Данные должны быть объединены перед применением условий, если строки не совпадают.
источник
Вариант использования
data.table
(объяснения в строке):вывод:
источник
Вы можете проверить
city
соответствие сgrepl
иctry
просто с==
. Для тех, кто соответствует до здесь, вы можете рассчитать разницу дат, преобразовав вdate
использованиеas.Date
и сравнив его сdifftime
.number
Разница делается таким же образом.источник
Вот гибкий подход, который позволяет вам указать любой набор критериев слияния, которые вы выберете.
Подготовительная работа
Я обеспечил, чтобы все строки были
df1
иdf2
были строками, а не факторами (как отмечено в нескольких других ответах). Я также завернул даты вas.Date
чтобы сделать их реальными.Укажите критерии слияния
Создайте список списков. Каждый элемент основного списка является одним критерием; Члены критерия
final.col.name
: имя столбца, который мы хотим в финальной таблицеcol.name.1
: имя столбца вdf1
col.name.2
: имя столбца вdf2
exact
логическое значение; мы должны сделать точное соответствие в этом столбце?threshold
: Порог (если мы не делаем точное соответствие)match.function
: функция, которая возвращает соответствие строк или нет (для особых случаев, таких как использованиеgrepl
для сопоставления строк; обратите внимание, что эта функция должна быть векторизована)Функция для слияния
Эта функция принимает три аргумента: два фрейма данных, которые мы хотим объединить, и список критериев соответствия. Это происходит следующим образом:
Примените функцию, и мы закончили
источник