Выполните полусоединение с data.table

Как выполнить полусоединение с data.table? Полусоединение подобно внутреннему соединению, за исключением того, что оно возвращает только столбцы X (а не те, что у Y), и не повторяет строки X, чтобы они соответствовали строкам Y. Например, следующий код выполняет внутренний присоединиться:

x <- data.table(x = 1:2, y = c("a", "b")) setkey(x, x) y <- data.table(x = c(1, 1), z = 10:11) x[y] # xyz # 1: 1 a 10 # 2: 1 a 11 

Полусоединение вернет только x[1]

Дополнительные возможности:

 w = unique(x[y,which=TRUE]) # the row numbers in x which have a match from y x[w] 

Если в x есть повторяющиеся значения ключа, то это необходимо:

 w = unique(x[y,which=TRUE,allow.cartesian=TRUE]) x[w] 

Или, наоборот:

 setkey(y,x) w = !is.na(y[x,which=TRUE,mult="first"]) x[w] 

Если nrow (x) << nrow (y), то подход y [x] должен быть быстрее.
Если nrow (x) >> nrow (y), то подход x [y] должен быть быстрее.

Но анти-анти-призывы присоединиться тоже 🙂

Одно из решений, о котором я могу думать, это:

 tmp <- x[!y] x[!tmp] 

В data.table вы можете иметь другую таблицу данных как выражение i (т. data.table.[ Первое выражение в data.table.[ Call]), и это будет выполнять соединение, например:

 x <- data.table(x = 1:10, y = letters[1:10]) setkey(x, x) y <- data.table(x = c(1,3,5,1), z = 1:4) > x[y] xyz 1: 1 a 1 2: 3 c 2 3: 5 e 3 4: 1 a 4 

! перед выражением i это расширение синтаксиса выше, которое выполняет «не-объединение», как описано на стр. 11 документации данных . Таким образом, первые присваивания оцениваются подмножеством x , у которого нет строк, где ключ (столбец x ) присутствует в y :

 > x[!y] xy 1: 2 b 2: 4 d 3: 6 f 4: 7 g 5: 8 h 6: 9 i 7: 10 j 

Это похоже на setdiff в этом отношении. И поэтому второй оператор возвращает все строки в x где ключ присутствует в y .

! функция была добавлена ​​в data.table 1.8.4 со следующей заметкой в NEWS :

 o A new "!" prefix on i signals 'not-join' (aka 'not-where'), #1384i. DT[-DT["a", which=TRUE, nomatch=0]] # old not-join idiom, still works DT[!"a"] # same result, now preferred. DT[!J(6),...] # !J == not-join DT[!2:3,...] # ! on all types of i DT[colA!=6L | colB!=23L,...] # multiple vector scanning approach (slow) DT[!J(6L,23L)] # same result, faster binary search '!' has been used rather than '-' : * to match the 'not-join'/'not-where' nomenclature * with '-', DT[-0] would return DT rather than DT[0] and not be backwards compatible. With '!', DT[!0] returns DT both before (since !0 is TRUE in base R) and after this new feature. * to leave DT[+J...] and DT[-J...] available for future use 

По какой-то причине следующее не работает x[!(x[!y])] - возможно, data.table слишком умна в анализе аргумента.

PS Как сказал Джош О'Брайен в другом ответе, одна строка будет x[!eval(x[!y])] .

Я путаюсь со всеми не-объединениями выше, это не то, что вы хотите просто:

 unique(x[y, names(x), with = F]) # xy #1: 1 a 

Если x может иметь повторяющиеся ключи, тогда вы можете использовать уникальное y :

 ## Creating an example data.table 'a' three-times-repeated first row x <- data.table(x = c(1,1,1,2), y = c("a", "a", "a", "b")) setkey(x, x) y <- data.table(x = c(1, 1), z = 10:11) setkey(y, x) x[eval(unique(y, by = key(y))), names(x), with = F] # data.table >= 1.9.8 requires by=key(y) # xy # 1: 1 a # 2: 1 a # 3: 1 a 

Обновление . Основываясь на всем обсуждении здесь, я бы сделал что-то вроде этого, которое должно быть быстрым и работать в самом общем случае:

 x[eval(unique(y[, key(x), with = FALSE]))] 

Вот еще одно, более прямое решение:

 unique(x[eval(y$x)]) 

Это более прямолинейно и работает быстрее – вот сравнение во время выполнения с моим предыдущим решением:

 # Generate some large data N <- 1000000 * 26 x <- data.table(x = 1:N, y = letters, z = rnorm(N)) setkey(x, x) y <- data.table(x = sample(N, N/10, replace = TRUE), z = sample(letters, N/10, replace = TRUE)) setkey(y, x) system.time(r1 <- x[!eval(x[!y])]) user system elapsed 7.772 1.217 11.998 system.time(r2 <- unique(x[eval(y$x)])) user system elapsed 0.540 0.142 0.723 

В более общем случае вы можете сделать что-то вроде

 x[eval(y[, key(x), with = FALSE])] 

Я попытался написать метод, который не использует никаких имен, которые просто путают в примере OP.

 sJ <- function(x,y){ ycols <- 1:min(ncol(y),length(key(x))) yjoin <- unique(y[,ycols,with=FALSE,drop=FALSE]) yjoin } x[eval(sJ(x,y))] 

Для более простого примера Виктора это дает желаемый результат:

  xy 1: 1 a 2: 3 c 3: 5 e 

Это на 30% медленнее, чем у Виктора.

РЕДАКТИРОВАТЬ: И подход Виктора, взяв на себя уникальную форму перед присоединением, довольно быстро:

 N <- 1e5*26 x <- data.table(x = 1:N, y = letters, z = rnorm(N)) setkey(x, x) y <- data.table(x = sample(N, N/10, replace = TRUE), z = sample(letters, N/10, replace = TRUE)) setkey(y, x) require(microbenchmark) microbenchmark( sJ=x[eval(sJ(x,y))], dolla=unique(x[eval(y$x)]), brack=x[eval(unique(y[['x']]))] ) Unit: milliseconds expr min lq median uq max neval # sJ 120.22700 125.04900 126.50704 132.35326 217.6566 100 # dolla 105.05373 108.33804 109.16249 118.17613 285.9814 100 # brack 53.95656 61.32669 61.88227 65.21571 235.8048 100 

Я предполагаю, что [[ vs $ не помогает скорости, но не проверял.

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

«Полусоединение подобно внутреннему соединению, за исключением того, что оно возвращает только столбцы X (а не те, что у Y), и не повторяет строки X, чтобы они соответствовали строкам Y»

 library(data.table) dt1 <- data.table(ProdId = 1:4, Product = c("Bread", "Cheese", "Pizza", "Butter")) dt2 <- data.table(ProdId = c(1, 1, 3, 4, 5), Company = c("A", "B", "C", "D", "E")) # semi-join unique(merge(dt1, dt2, on="ProdId")[, names(dt1), with=F]) ProdId Product 1: 1 Bread 2: 3 Pizza 3: 4 Butter 

Я просто применил синтаксис inner-join, за которым следуют фильтрация столбцов только из первой таблицы, с unique() для удаления строк первой таблицы, которые повторялись для соответствия строкам второй таблицы.

Изменить: вышеупомянутый подход будет соответствовать dplyr::semi_join() только если в первой таблице есть уникальные строки. Если нам нужно вывести все строки, включая дубликаты из первой таблицы, то мы можем использовать fsetdiff() показанный ниже.

Другое data.table решение для data.table данных:

 fsetdiff(dt1, dt1[!dt2, on="ProdId"]) ProdId Product 1: 1 Bread 2: 3 Pizza 3: 4 Butter 

Я только что удалил из первой таблицы анти-соединение первого и второго. Мне кажется проще. Если первая таблица имеет повторяющиеся строки, нам понадобятся:

 fsetdiff(dt1, dt1[!dt2, on="ProdId"], all=T) 

Результат fsetdiff() с ,all=T совпадает с выходом dplyr:

 dplyr::semi_join(dt1, dt2, by="ProdId") ProdId Product 1 1 Bread 2 3 Pizza 3 4 Butter 

Используя другой dataset, взятых из одного из предыдущих сообщений:

 x <- data.table(x = c(1,1,1,2), y = c("a", "a", "a", "b")) y <- data.table(x = c(1, 1), z = 10:11) 

С dplyr:

 dplyr::semi_join(x, y, by="x") xy 1 1 a 2 1 a 3 1 a 

С data.table:

 fsetdiff(x, x[!y, on="x"], all=T) xy 1: 1 a 2: 1 a 3: 1 a 

Без ,all=T удаляются повторяющиеся строки:

 fsetdiff(x, x[!y, on="x"]) xy 1: 1 a 

Пакет dplyr поддерживает следующие четыре типа соединения:

inner_join , left_join , semi_join , anti_join

Поэтому для полусоединения попробуйте следующий код

 library("dplyr") table1 <- data.table(x = 1:2, y = c("a", "b")) table2 <- data.table(x = c(1, 1), z = 10:11) semi_join(table1, table2) 

Ожидается выход:

 # Joining by: "x" # Source: local data table [1 x 2] # # xy # (int) (chr) # 1 1 a 
  • Извлечение текста UTF-8 из MySQL в R возвращает «????»
  • Создайте штабелированный планшет, где каждый стек масштабируется, чтобы суммировать до 100%
  • сгруппированные операции, которые приводят к длине, не равной 1 или длине группы в dplyr
  • Построение нескольких временных рядов в ggplot
  • Извлечь значения столбца матрицы по имени столбца матрицы
  • Кривая кривой полинома в R
  • преобразовать формат столбца data.frame из символа в фактор
  • Как сопоставить вектор значений другому вектору с моей собственной пользовательской картой в R
  • Извлечение уникальных чисел из строки в R
  • Как эффективно использовать Rprof в R?
  • Формула с динамическим числом переменных
  • Давайте будем гением компьютера.