Обновить соединение с несколькими строками

Вопрос

Когда вы выполняете обновление-соединение, где таблица i имеет несколько строк на ключ, как вы можете контролировать, какая строка возвращается?

пример

В этом примере соединение update-join возвращает последнюю строку из dt2

library(data.table) 

dt1 <- data.table(id = 1) 
dt2 <- data.table(id = 1, letter = letters) 

dt1[ 
    dt2 
    , on = "id" 
    , letter := i.letter 
    ] 

dt1
#    id letter
# 1:  1      z

Как я могу управлять им, чтобы вернуть 1-ю, 2-ю, n-ю строку, а не по умолчанию?


Рекомендации

Несколько ссылок, подобных этому пользователю @Frank

Ответ 1

Наиболее гибкой идеей, о которой я могу думать, является только присоединение к части dt2 которая содержит dt2 вам строки. Итак, для второй строки:

dt1[ 
    dt2[, .SD[2], by=id]
    , on = "id" 
    , letter := i.letter
    ]

dt1
#   id letter
#1:  1      b

С помощью наконечника шляпы на @Frank для упрощения подвыборки dt2.

Ответ 2

Как я могу управлять им, чтобы вернуть 1-ю, 2-ю, n-ю строку, а не по умолчанию?

Не изящные, но сорт работы:

n = 3L
dt1[, v := dt2[.SD, on=.(id), x.letter[n], by=.EACHI]$V1]

Пара проблем:

  1. Он не выбирает использование GForce, например, как показано здесь:

    > dt2[, letter[3], by=id, verbose=TRUE]
    Detected that j uses these columns: letter 
    Finding groups using forderv ... 0.020sec 
    Finding group sizes from the positions (can be avoided to save RAM) ... 0.000sec 
    lapply optimization is on, j unchanged as 'letter[3]'
    GForce optimized j to ''g['(letter, 3)'
    Making each group and running j (GForce TRUE) ... 0.000sec 
       id V1
    1:  1  c
    
  2. Если для некоторых объединенных групп значение n находится за пределами 1:.N, предупреждение не будет выдано:

    n = 40L
    dt1[, v := dt2[.SD, on=.(id), x.letter[n], by=.EACHI]$V1]
    

В качестве альтернативы, сделайте привычку проверять, что i в присоединении к обновлению x[i] "привязан" к столбцам соединения:

cols = "id"
stopifnot(nrow(dt2) == uniqueN(dt2, by=cols))

И затем сделайте другую таблицу i чтобы присоединиться, если это необходимо.

mDT = dt2[, .(letter = letter[3L]), by=id]
dt1[mDT, on=cols, v := i.letter]