R данные.имена столбцов таблицы, не работающие в функции

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

DT <- data.table(my_name=c("A","B","C","D","E","F"),my_id=c(2,2,3,3,4,4))
> DT
   my_name my_id
1:       A     2
2:       B     2
3:       C     3
4:       D     3
5:       E     4
6:       F     4

Я пытаюсь создать все пары " my_name "с разными значениями" my_id", которые для DT будут:

Var1 Var2    
A    C
A    D
A    E
A    F
B    C
B    D
B    E
B    F
C    E
C    F
D    E
D    F

у меня есть функция для возврата всех пар " my_name "для данной пары значений" my_id", которая работает так, как ожидалось.

get_pairs <- function(id1,id2,tdt) {
    return(expand.grid(tdt[my_id==id1,my_name],tdt[my_id==id2,my_name]))
}
> get_pairs(2,3,DT)
Var1 Var2
1    A    C
2    B    C
3    A    D
4    B    D

теперь я хочу выполнить эту функцию для всех пар идентификаторов, которые я пытаюсь сделать, найдя все пары идентификаторов, а затем используя mapply с функцией get_pairs.

> combn(unique(DT$my_id),2)
     [,1] [,2] [,3]
[1,]    2    2    3
[2,]    3    4    4
tid1 <- combn(unique(DT$my_id),2)[1,]
tid2 <- combn(unique(DT$my_id),2)[2,]
mapply(get_pairs, tid1, tid2, DT)
Error in expand.grid(tdt[my_id == id1, my_name], tdt[my_id == id2, my_name]) : 
  object 'my_id' not found

опять же, если я попытаюсь сделать то же самое без mapply, он работает.

get_pairs3(tid1[1],tid2[1],DT)
Var1 Var2
1    A    C
2    B    C
3    A    D
4    B    D

почему эта функция терпит неудачу только при использовании внутри mapply? Я думаю, что это как-то связано с областью данных.имена таблиц, но я не уверен.

альтернативно, есть ли другой / более эффективный способ выполнить эту задачу? У меня большие данные.таблица с третьим идентификатором "sample", и мне нужно получить все эти пары для каждого образца (например, работая на DT[sample==" sample_id",]). Я новичок в данных.пакет таблицы, и я не могу использовать его самым эффективным способом.

3 ответов


почему эта функция терпит неудачу только при использовании внутри mapply? Я думаю это как-то связано с объемом данных.имена таблиц, но я не уверенный.

почему функция не имеет ничего общего с областью действия в этом случае. mapply векторизирует функцию, принимает каждый элемент каждого параметра и переходит к функции. Итак, в вашем случае data.table элементы ее столбцов, так mapply передает столбец my_name вместо полное data.table.

если вы хотите пройти полное data.table to mapply, вы должны использовать . Тогда ваша функция будет работать:

res <- mapply(get_pairs, tid1, tid2, MoreArgs = list(tdt=DT), SIMPLIFY = FALSE)
do.call("rbind", res)
  Var1 Var2
1     A    C
2     B    C
3     A    D
4     B    D
5     A    E
6     B    E
7     A    F
8     B    F
9     C    E
10    D    E
11    C    F
12    D    F

перечислить все возможные пары

u_name    <- unique(DT$my_name)
all_pairs <- CJ(u_name,u_name)[V1 < V2]

перечислить наблюдаемые пары

obs_pairs <- unique(
  DT[,{un <- unique(my_name); CJ(un,un)[V1 < V2]}, by=my_id][, !"my_id", with=FALSE]
)

взять разницу

all_pairs[!J(obs_pairs)]

CJ как expand.grid за исключением того, что он создает данных.таблицу со всеми столбцами в качестве ключа. Данные.таблица X должен быть настроен для соединения X[J(Y)] или нет-присоединитесь X[!J(Y)] (как последняя строка) для работы. The J является необязательным, но делает более очевидным, что мы делаем присоединяться.


упрощений. @CathG указал, что есть более чистый способ построения obs_pairs если у вас всегда есть два отсортированных " имени "для каждого" id " (как в примере данных): используйте as.list(un) на месте CJ(un,un)[V1 < V2].


функции debugonce() чрезвычайно полезно в этих сценариях.

debugonce(mapply)
mapply(get_pairs, tid1, tid2, DT)

# Hit enter twice
# from within BROWSER
debugonce(FUN)
# Hit enter twice
# you'll be inside your function, and then type DT
DT
# [1] "A" "B" "C" "D" "E" "F"
Q # (to quit debugging mode)

что неправильно. В основном, mapply() принимает первый элемент каждого входного аргумента и передает его вашей функции. В этом случае вы предоставили данные.таблица, который также список. Итак, вместо того, чтобы передавать все данные.таблица, она передает каждый элемент списка (столбцов).

таким образом, вы можете обойти это путем делать:

mapply(get_pairs, tid1, tid2, list(DT))

но mapply() упрощает результат по умолчанию, и поэтому вы получите matrix обратно. Вам придется использовать SIMPLIFY = FALSE.

mapply(get_pairs, tid1, tid2, list(DT), SIMPLIFY = FALSE)

или просто использовать Map:

Map(get_pairs, tid1, tid2, list(DT))

использовать rbindlist() для привязки результатов.

HTH