2015-06-25 6 views
7

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

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? Я думаю, что это имеет какое-то отношение к объему имен data.table, но я не уверен.

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

+0

К сожалению, я не уверен, о том, почему mapply не работает и поэтому не упоминал об этом в своем ответе. – Frank

+0

для 'mapply', он работает, если вы поместите' DT' непосредственно в функцию, а не как параметр (хотя он не решает «почему это не работает» часть ...) – Cath

+0

Имеет ли каждый 'id' всегда ровно два «имени»? – Frank

ответ

3

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

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

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

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 
+0

О, хорошо, это имеет смысл. Это потому, что data.table также является списком()? – Sam

+0

@Sam Yeap, 'data.table',' data.frame ',' tbl_df's - это списки с некоторыми дополнительными свойствами. –

4

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

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 кроме того, что он создает data.table со всеми его столбцы являются его ключом. Таблица данных X должна быть введена в действие для присоединения X[J(Y)] или к не-присоединяться X[!J(Y)] (как и к последней строке). J не является обязательным, но делает его более очевидным, что мы делаем соединение.


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

+0

К сожалению, я не упоминал, что в «my_name» могут быть дубликаты, но ваше решение работает, если нет дубликатов. Это гораздо более элегантно, чем мой подход. Очевидно, мне нужно научиться больше использовать соединения. – Sam

+0

@ Sam Я сейчас редактировал этот случай (если я правильно понял). – Frank

3

Функция 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() берет первый элемент каждого входного аргумента и передает его вашей функции. В этом случае вы предоставили data.table, который также является list. Таким образом, вместо передачи всей таблицы данных, он передает каждый элемент списка (столбцы).

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

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() связать результаты.

НТН