Подтвердить что ты не робот

Выполните полусоединение с 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]
#   x y  z
# 1: 1 a 10
# 2: 1 a 11

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

4b9b3361

Ответ 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) < (y), то подход y [x] должен быть быстрее.
Если nrow (x) → nrow (y), то подход x [y] должен быть быстрее.

Но призывы против анти-объединения тоже: -)

Ответ 2

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

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

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

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]
   x y z
1: 1 a 1
2: 3 c 2
3: 5 e 3
4: 1 a 4

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

> x[!y]
    x y
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' (a.k.a. '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 слишком умно разбирать аргумент.

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

Ответ 3

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

unique(x[y, names(x), with = F])
#   x y
#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)
#    x y
# 1: 1 a
# 2: 1 a
# 3: 1 a

Ответ 4

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

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])]

Ответ 5

Я попытался написать метод, который не использует никаких имен, которые просто путают в примере 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))]

Для упрощенного примера Victor это дает желаемый результат:

   x y
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 $ не помогает скорости, но не проверял.

Ответ 6

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

"Полусоединение подобно внутреннему соединению, за исключением того, что оно возвращает только столбцы 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:

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

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

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")
  x y
1 1 a
2 1 a
3 1 a

С data.table:

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

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

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

Ответ 7

Пакет 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]
# 
#       x     y
#   (int) (chr)
# 1     1     a

Ответ 8

Попробуйте следующее:

 w <- y[,unique(x)]
 x[x %in% w]

Выход будет:

   x y
1: 1 a