Даны два фрейма данных:
df1 = data.frame(CustomerId = c(1:6), Product = c(rep("Toaster", 3), rep("Radio", 3)))
df2 = data.frame(CustomerId = c(2, 4, 6), State = c(rep("Alabama", 2), rep("Ohio", 1)))
df1
# CustomerId Product
# 1 Toaster
# 2 Toaster
# 3 Toaster
# 4 Radio
# 5 Radio
# 6 Radio
df2
# CustomerId State
# 2 Alabama
# 4 Alabama
# 6 Ohio
Как я могу сделать стиль базы данных, то есть, стиль SQL, объединяет ? То есть как я могу получить:
- Внутреннее соединение в
df1
иdf2
:
возвращать только строки , в которых левая таблица имеют соответствующие клавиши в правой таблице. - Внешнее соединение из
df1
иdf2
:
возвращает все строки из обеих таблиц, присоединиться к записи из левой , которые имеют соответствующие ключи в таблице справа. - Левое внешнее соединение (или просто левое соединение) из
df1
иdf2
вернуть все строки из левой таблицы и любые строки с соответствующими ключами из правой таблицы. - Правое внешнее соединение из
df1
иdf2
вернуть все строки из правой таблицы и любые строки с соответствующими ключами из левой таблицы.
Дополнительный кредит:
Как я могу сделать оператор выбора в стиле SQL?
Ответы:
Используя
merge
функцию и ее необязательные параметры:Внутреннее соединение:
merge(df1, df2)
будет работать для этих примеров, потому что R автоматически объединяет кадры по общим именам переменных, но вы, скорее всего, захотите указать,merge(df1, df2, by = "CustomerId")
чтобы убедиться, что вы подходите только по тем полям, которые вы хотите. Вы можете также использоватьby.x
иby.y
параметрыесли совпадающие переменные имеют разные названия в разных кадрах данных.Внешнее соединение:
merge(x = df1, y = df2, by = "CustomerId", all = TRUE)
Левый внешний:
merge(x = df1, y = df2, by = "CustomerId", all.x = TRUE)
Правый внешний:
merge(x = df1, y = df2, by = "CustomerId", all.y = TRUE)
Перекрестное соединение:
merge(x = df1, y = df2, by = NULL)
Как и в случае внутреннего соединения, вы, вероятно, захотите явно передать «CustomerId» в R в качестве соответствующей переменной.Я думаю, что почти всегда лучше явно указывать идентификаторы, по которым вы хотите объединить; безопаснее, если входные данные изменяются неожиданно и их легче читать позже.Вы можете объединить в несколько столбцов, давая
by
вектор, например,by = c("CustomerId", "OrderId")
.Если имена столбцов для объединения не совпадают, вы можете указать, например,
by.x = "CustomerId_in_df1", by.y = "CustomerId_in_df2"
гдеCustomerId_in_df1
имя столбца в первом кадре данных иCustomerId_in_df2
имя столбца во втором кадре данных. (Это также могут быть векторы, если вам нужно объединить несколько столбцов.)источник
data.table
пакет - это совершенно новый набор синтаксиса соединения, но он радикально быстрее, чем все, о чем мы здесь говорим.merge(x=df1,y=df2, by.x=c("x_col1","x_col2"), by.y=c("y_col1","y_col2"))
data.table
сейчас, та же функция, только быстрее.Я бы порекомендовал проверить пакет sqldf Габора Гротендика , который позволяет вам выражать эти операции в SQL.
Я считаю, что синтаксис SQL является более простым и более естественным, чем его эквивалент R (но это может просто отражать мою предвзятость RDBMS).
Посмотрите sqlf GitHub Gabor для получения дополнительной информации о соединениях.
источник
Существует метод data.table для внутреннего объединения, который очень экономит время и память (и необходим для некоторых больших data.frames):
merge
также работает на data.tables (так как это generic и вызываетmerge.data.table
)документирование data.table для stackoverflow:
как выполнить операцию слияния data.table
Перевод SQL-соединений по внешним ключам в R синтаксис data.table
Эффективные альтернативы слияния для более крупных data.frames R
Как выполнить базовое левое внешнее соединение с data.table в R?
Еще одним вариантом является
join
функция, найденная в пакете plyr.Варианты
type
:inner
,left
,right
,full
.From
?join
: В отличие отmerge
, [join
] сохраняет порядок x независимо от того, какой тип соединения используется.источник
plyr::join
. Микробенчмаркинг показывает, что он работает примерно в 3 раза быстрее, чемmerge
.data.table
это гораздо быстрее, чем оба. Существует также большая поддержка в SO, я не вижу, чтобы многие авторы пакетов отвечали на вопросы так часто, какdata.table
авторы или участники.data.table
синтаксис для объединения списка фреймов данных ?nomatch = 0L
в этом случае.Вы также можете делать объединения, используя удивительный пакет dplyr Хэдли Уикхэма .
Мутирующие объединения: добавление столбцов в df1 с использованием совпадений в df2
Фильтрация объединений: отфильтровывать строки в df1, не изменять столбцы
источник
CustomerId
в числовые? Я не вижу упоминаний в документации (для обоихplyr
иdplyr
) об этом типе ограничений. Будет ли ваш код работать неправильно, если столбец слияния будет иметьcharacter
тип (особенно интересуетplyr
)? Я что-то пропустил?Есть несколько хороших примеров этого в R Wiki . Я украду пару здесь:
Метод слияния
Поскольку ваши ключи называются одинаково, короткий способ выполнить внутреннее соединение - это merge ():
полное внутреннее объединение (все записи из обеих таблиц) может быть создано с ключевым словом «all»:
левое внешнее соединение df1 и df2:
правое внешнее соединение df1 и df2:
Вы можете перевернуть их, дать им пощечину и потереть их, чтобы получить два других внешних объединения, о которых вы спрашивали :)
Подстрочный метод
Левое внешнее объединение с df1 слева с использованием метода индекса:
Другая комбинация внешних объединений может быть создана путем добавления примера нижнего индекса внешнего соединения. (да, я знаю, что это эквивалентно высказыванию «Я оставлю это как упражнение для читателя ...»)
источник
Новое в 2014 году:
Особенно, если вы также заинтересованы в манипулировании данными в целом (включая сортировку, фильтрацию, поднаборы, суммирование и т. Д.), Вы обязательно должны взглянуть на это
dplyr
, которое поставляется с множеством функций, предназначенных для облегчения вашей работы именно с фреймами данных и некоторые другие типы баз данных. Он даже предлагает довольно сложный интерфейс SQL и даже функцию для преобразования (большей части) кода SQL непосредственно в R.Четыре связанные с присоединением функции в пакете dplyr:
inner_join(x, y, by = NULL, copy = FALSE, ...)
: вернуть все строки из x, где есть совпадающие значения в y, и все столбцы из x и yleft_join(x, y, by = NULL, copy = FALSE, ...)
: вернуть все строки из x и все столбцы из x и ysemi_join(x, y, by = NULL, copy = FALSE, ...)
: вернуть все строки из x, где есть соответствующие значения в y, оставив только столбцы из x.anti_join(x, y, by = NULL, copy = FALSE, ...)
: вернуть все строки из x, где нет соответствующих значений в y, оставив только столбцы из xЭто все здесь очень подробно.
Выбор столбцов может быть сделан с помощью
select(df,"column")
. Если для вас этого недостаточно, естьsql()
функция, в которую вы можете ввести SQL-код как есть, и он выполнит указанную вами операцию так же, как вы писали в R все время (для получения дополнительной информации, пожалуйста, обратитесь к на виньетка с базой данных / dplyr ). Например, при правильном примененииsql("SELECT * FROM hflights")
выберет все столбцы из таблицы dplyr «hflights» («tbl»).источник
Обновление методов data.table для объединения наборов данных. Ниже приведены примеры для каждого типа соединения. Есть два метода, один из которых
[.data.table
при передаче второго data.table в качестве первого аргумента подмножеству, другой способ - использоватьmerge
функцию, которая отправляет быстрый метод data.table.Ниже приведены тесты производительности базы R, sqldf, dplyr и data.table.
Бенчмарк тестирует наборы данных без ключей / без индекса. Тестирование выполняется для наборов данных 50M-1, в столбце объединения есть общие значения 50M-2, поэтому каждый сценарий (внутренний, левый, правый, полный) можно протестировать, и объединение все еще не является тривиальным. Это тип объединения, который хорошо подчеркивает алгоритмы объединения. Задержки являются из
sqldf:0.4.11
,dplyr:0.7.8
,data.table:1.12.0
.Имейте в виду, что существуют другие типы объединений, которые можно выполнить с помощью
data.table
:- обновления при объединении - если вы хотите искать значения из другой таблицы в основной таблице
- агрегировать при объединении - если вы хотите объединить по ключу, к которому вы присоединяетесь, у вас нет материализовать все результаты объединения
- перекрывающееся объединение - если вы хотите объединить по диапазонам
- скользящее объединение - если вы хотите, чтобы объединение могло соответствовать значениям из предыдущих или следующих строк путем их наматывания вперед или назад
- неэквивалентное объединение - если ваше условие соединения не равно
Код для воспроизведения:
источник
on =
?on
merge.data.table
по умолчаниюsort = TRUE
есть ключ, который добавляет ключ во время слияния и оставляет его там в результате. Это то, на что нужно обратить внимание, особенно если вы пытаетесь избежать установки клавиш.data.table
, что вы имеете в виду? Можете ли вы быть более конкретным, пожалуйста.В dplyr начиная с 0.4 реализованы все эти объединения, в том числе
outer_join
, но стоит отметить, что в первые несколько выпусков до 0.4 он раньше не предлагалouter_join
, и в результате было довольно много плохого хакерского обходного пользовательского кода, плавающего довольно долго впоследствии (вы все еще можете найти такой код в SO, ответы Kaggle, github того периода. Следовательно, этот ответ по-прежнему служит полезной цели.)Основные моменты релиза, связанные с присоединением :
версия 0.5 (6/2016)
v0.4.0 (1/2015)
v0.3 ( 10/2014 )
версия 0.2 ( 5/2014 )
v0.1.3 (4/2014)
Методы обхода комментариев Хадли в этом выпуске:
источник
dplyr
синтаксис, переход отlazyeval
кrlang
бэкэндам сломал кучу коды для меня, что заставило меня , чтобы узнать большеdata.table
, и теперь я в основном использоватьdata.table
.)plyr
/dplyr
/data.table
/ tidyverse зависит огромно , на котором год мы начали, и что (эмбриональный) состояние пакеты были тогда, в отличие от теперь ...Объединив два фрейма данных с ~ 1 миллионом строк в каждом, один с двумя столбцами, а другой с ~ 20, я неожиданно обнаружил,
merge(..., all.x = TRUE, all.y = TRUE)
что он быстрееdplyr::full_join()
. Это с dplyr v0.4Слияние занимает ~ 17 секунд, full_join - ~ 65 секунд.
Впрочем, немного еды, так как я обычно использую dplyr для манипуляций.
источник
В случае левого соединения с количеством элементов
0..*:0..1
или правого соединения с количеством элементов0..1:0..*
можно назначить на месте односторонние столбцы из0..1
рамы (таблицы) непосредственно на раму (0..*
таблицу) и, таким образом, избежать создания совершенно новая таблица данных. Для этого необходимо сопоставить ключевые столбцы от joinee в joiner и проиндексировать + упорядочить строки joiner соответственно для назначения.Если ключ представляет собой один столбец, то
match()
для сопоставления мы можем использовать один вызов . Это тот случай, который я расскажу в этом ответе.Вот пример, основанный на OP, за исключением того, что я добавил дополнительную строку
df2
с идентификатором 7, чтобы проверить случай несоответствующего ключа в столяре. Это фактическиdf1
оставленное соединениеdf2
:Выше я жестко закодировал предположение, что ключевой столбец является первым столбцом обеих входных таблиц. Я бы сказал, что в целом это не является необоснованным предположением, поскольку, если у вас есть data.frame с ключевым столбцом, было бы странно, если бы он не был установлен в качестве первого столбца data.frame из начало. И вы всегда можете изменить порядок столбцов, чтобы сделать это так. Преимущественным следствием этого предположения является то, что имя ключевого столбца не обязательно должно быть жестко закодировано, хотя я полагаю, что оно просто заменяет одно предположение другим. Краткость - это еще одно преимущество целочисленной индексации, а также скорость. В нижеприведенных тестах я изменю реализацию на использование индексации имен строк в соответствии с конкурирующими реализациями.
Я думаю, что это особенно подходящее решение, если у вас есть несколько таблиц, которые вы хотите объединить против одной большой таблицы. Повторное восстановление всей таблицы для каждого слияния было бы ненужным и неэффективным.
С другой стороны, если вам нужно, чтобы участник оставался неизменным в ходе этой операции по какой-либо причине, то это решение не может быть использовано, поскольку оно изменяет участника напрямую. Хотя в этом случае вы могли бы просто сделать копию и выполнить ее назначение на месте.
Как примечание, я кратко рассмотрел возможные решения для соответствия многоколоночных ключей. К сожалению, единственные подходящие решения, которые я нашел, были:
match(interaction(df1$a,df1$b),interaction(df2$a,df2$b))
, или та же идея сpaste()
.outer(df1$a,df2$a,`==`) & outer(df1$b,df2$b,`==`)
.merge()
и эквивалентные основанные на пакетах функции слияния, которые всегда выделяют новую таблицу для возврата объединенного результата и, таким образом, не подходят для решения на основе назначения на месте.Например, см. Сопоставление нескольких столбцов в разных фреймах данных и получение другого столбца в качестве результата , сопоставление двух столбцов с двумя другими столбцами , Сопоставление по нескольким столбцам и ответ на этот вопрос, в котором я изначально придумал решение на месте, Объединить два кадра данных с различным количеством строк в R .
Бенчмаркинг
Я решил сделать свой собственный сравнительный анализ, чтобы увидеть, как подход на месте назначения сравнивается с другими решениями, которые были предложены в этом вопросе.
Код тестирования:
Вот пример из примера, основанного на OP, который я продемонстрировал ранее:
Здесь я сравниваю случайные входные данные, пробуя разные шкалы и разные комбинации клавиш для двух таблиц ввода. Этот тест по-прежнему ограничен случаем целочисленного ключа из одного столбца. Кроме того, чтобы гарантировать, что решение на месте будет работать как для левого, так и для правого объединений одних и тех же таблиц, все данные случайного теста используют количество
0..1:0..1
элементов. Это реализуется путем выборки без замены ключевого столбца первого data.frame при создании ключевого столбца второго data.frame.Я написал некоторый код для создания графиков log-log приведенных выше результатов. Я создал отдельный график для каждого процента перекрытия. Это немного загромождено, но мне нравится, когда все типы решений и типы соединений представлены на одном графике.
Я использовал сплайн-интерполяцию, чтобы показать плавную кривую для каждой комбинации решения / типа соединения, нарисованную отдельными символами pch. Тип соединения фиксируется символом pch, используя точку для внутреннего, левого и правого угловых скобок для левого и правого и ромб для полного. Тип решения определяется цветом, как показано в легенде.
Вот второй крупномасштабный тест, который более тяжелый, с точки зрения количества и типов ключевых столбцов, а также количества элементов. Для этого теста я использую три ключевых столбца: один символ, одно целое и один логический, без ограничений по количеству элементов (то есть
0..*:0..*
). (В общем случае не рекомендуется определять ключевые столбцы с двойными или сложными значениями из-за сложностей сравнения с плавающей точкой, и в основном никто никогда не использует необработанный тип, тем более для ключевых столбцов, поэтому я не включил эти типы в ключ Кроме того, ради информации, я изначально пытался использовать четыре ключевых столбца, включая ключевой столбец POSIXct, но тип POSIXctsqldf.indexed
по какой-то причине не подходил для решения, возможно, из-за аномалий сравнения с плавающей запятой, поэтому я убрал это.)Полученные графики, используя тот же код построения, приведенный выше:
источник
merge
функцию, мы можем выбрать переменную левой или правой таблицы, точно так же, как мы все знакомы с оператором select в SQL (EX: выберите a. * ... или Select b. * Из .....)Мы должны добавить дополнительный код, который будет подмножеством из вновь объединенной таблицы.
SQL: -
select a.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
Р :-
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df1)]
Так же
SQL: -
select b.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
Р :-
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df2)]
источник
Для внутреннего объединения всех столбцов вы также можете использовать
fintersect
из data.table -package илиintersect
из dplyr -package в качестве альтернативыmerge
без указанияby
-columns. это даст строки, которые равны между двумя кадрами данных:Пример данных:
источник
Обновление присоединиться. Еще одно важное соединение в стиле SQL - это « соединение обновления », при котором столбцы в одной таблице обновляются (или создаются) с использованием другой таблицы.
Изменение примеров таблиц OP ...
Предположим, мы хотим добавить состояние клиента из
cust
таблицы покупокsales
, игнорируя столбец года. С помощью базы R мы можем определить совпадающие строки, а затем скопировать значения в:Как видно здесь,
match
выбирается первая подходящая строка из таблицы клиентов.Обновите объединение с несколькими столбцами. Приведенный выше подход хорошо работает, когда мы объединяем только один столбец и удовлетворены первым соответствием. Предположим, мы хотим, чтобы год измерения в таблице клиентов соответствовал году продажи.
Как отмечает ответ @ bgoldst, в
match
сinteraction
может быть одним из вариантов для этого случая. Проще говоря, можно использовать data.table:Скользящее обновление присоединяется. Кроме того, мы можем выбрать последнее состояние, в котором был найден клиент:
Три приведенных выше примера сосредоточены на создании / добавлении нового столбца. Смотрите соответствующий R FAQ для примера обновления / изменения существующего столбца.
источник