2017-08-30 83 views
0

有這塊,我想在一個函數評估函數的參數傳遞給data.table

indata <- data.frame(id = c(1L, 2L, 3L, 4L, 12L, 13L, 14L, 15L), 
        fid = c(NA, 9L, 1L, 1L, 7L, 5L, 5L, 5L), 
        mid = c(0L, NA, 2L, 2L, 6L, 6L, 6L, 8L)) 
library(data.table) 
DT <- as.data.table(indata) 

DT[, msib:=.(list(id)), by = mid][            
    ,msibs := mapply(setdiff, msib, id)][ 
    ,fsib := .(list(id)), by = fid][ 
    ,fsibs := mapply(setdiff, fsib, id)][ 
    ,siblist := mapply(union, msibs, fsibs)][ 
    ,c("msib","msibs", "fsib", "fsibs") := NULL] 

到目前爲止好包裝代碼。按需要工作。現在它應該被封裝在一個函數中,我可以通過替代變量名稱(如果可能,不用引用),這是我的第一次嘗試。

f <- function(DT, id, fid, mid) { 

    DT[, msib:=.(list(id)), by = mid][            
     ,msibs := mapply(setdiff, msib, id)][ 
     ,fsib := .(list(id)), by = fid][ 
     ,fsibs := mapply(setdiff, fsib, id)][ 
     ,siblist := mapply(union, msibs, fsibs)][ 
     ,c("msib","msibs", "fsib", "fsibs") := NULL] 
} 

知道這是行不通的,但讓我們看看它的錯誤拋出as.vector

indata2 <- indata 
names(indata2) <- c("A", "B", "C") # Give new names 
DT2 <- as.data.table(indata2) 
f(DT2, A, B, C) 

錯誤(X, 「列表」): 不能強迫型'封'到類型'列表'的向量

這很有道理。現在要確保承諾正確評估我試過這個

f <- function(DT, id, fid, mid) { 
    mid <- deparse(substitute(mid)) 
    id <- deparse(substitute(id)) 
    fid <- deparse(substitute(fid)) 

    DT[, msib:=.(list(id)), by = mid][            
     ,msibs := mapply(setdiff, msib, id)][ 
     ,fsib := .(list(id)), by = fid][ 
     ,fsibs := mapply(setdiff, fsib, id)][ 
     ,siblist := mapply(union, msibs, fsibs)][ 
     ,c("msib","msibs", "fsib", "fsibs") := NULL] 
} 

這不會引發錯誤,但也不起作用。輸出看起來像這樣

f(DT2, A, B, C) 
    A B C siblist 
1: 1 NA 0   
2: 2 9 NA   
3: 3 1 2   
4: 4 1 2   
5: 12 7 6   
6: 13 5 6   
7: 14 5 6   
8: 15 5 8 

siblist欄是空的,它不應該是,不,當我手動運行它。我也試過這個版本(其轉換爲字符串),看看是否能工作:

f <- function(DT, id, fid, mid){ 
    mid <- as.character(substitute(mid)) 
    id <- as.character(substitute(id)) 
    fid <- as.character(substitute(fid)) 
    DT[, msib:=.(list(id)), by = mid][ # Siblings through the mother 
     ,msibs := mapply(setdiff, msib, id)][ 
     ,fsib := .(list(id)), by = fid][ 
     ,fsibs := mapply(setdiff, fsib, id)][ 
     ,siblist := mapply(union, msibs, fsibs)][ 
     ,c("msib","msibs", "fsib", "fsibs") := NULL] # Removed unused 
} 

但是,這並不工作,要麼 - 相同的輸出上面。我認爲這可能是因爲data.tablej部分中的承諾在錯誤的環境中評估,但我不確定。我如何解決我的功能?

+0

注意事項「迄今爲止很好」,列表列非常低效。你可能想看看igraph軟件包或其他專門用於這類數據的東西(包括個人之間的鏈接和其他東西)。假設你不介意低效率,一個快速的解決辦法是使'DT = setnames(copy(DT),c(mid,id,fid),c(「mid」,「id」,「fid」))在你的deparse/substitute/etc之後,對嗎? – Frank

+0

這會工作,但我需要處理20+ mio邊緣的數據集,所以我不想創建額外的數據副本。我很想聽聽有關生成最終列表的其他選項。我早就涉及這個問題的不同軟件包之間的時序問題做了一些嘗試,並發現'data.table'出來的速度很快,但我很想獲得其他方法的想法。 – ekstroem

回答

1

如果你希望一個對象有一定的結構或佔有一定的數據,然後定義一個類可以真正的幫助。而S3,這很簡單。

as.relationship <- function(DT, id, fid, mid) { 
    out <- DT[, c(id, fid, mid), with = FALSE] 
    setnames(out, c("id", "fid", "mid")) 
    setattr(out, "class", c("relationship", class(out))) 
    out 
} 

然後,您可以編寫一個函數來處理該類,並知道所有內容的安全性。

f <- function(DT, id, fid, mid) { 
    relatives <- as.relationship(DT, id, fid, mid) 
    relatives[ 
    relatives, 
    on = "fid", 
    allow.cartesian = TRUE 
    ][ 
    relatives, 
    on = "mid", 
    allow.cartesian = TRUE 
    ][ 
    , 
    { 
     siblings <- union(i.id, i.id.1) 
     except_self <- setdiff(siblings, .BY[["id"]]) 
     list(siblist = list(except_self)) 
    }, 
    by = "id" 
    ] 
} 

此函數將列名作爲字符串。所以你會這樣稱呼它:

f(DT, "id", "fid", "mid") 
# id siblist 
# 1: 1   
# 2: 2   
# 3: 3  4 
# 4: 4  3 
# 5: 12 13,14 
# 6: 13 14,15,12 
# 7: 14 13,15,12 
# 8: 15 13,14 

setnames(DT, c("A", "B", "C")) 
f(DT, "A", "B", "C") 
# id siblist 
# 1: 1   
# 2: 2   
# 3: 3  4 
# 4: 4  3 
# 5: 12 13,14 
# 6: 13 14,15,12 
# 7: 14 13,15,12 
# 8: 15 13,14 

如果你擔心性能,不要這樣做。如果您從另一個data.table的整列創建data.table,則它們足夠智能,不會實際複製數據。他們分享它。所以製作另一個對象並沒有真正的性能損失。

+0

對不起,延遲迴復。如果我運行'f(DT,id,fid,mid)',那麼我會得到以下錯誤eval(jsub,parent.frame(),parent.frame()): object'fid'not found' – ekstroem

+0

I應該提到該函數將列名稱作爲字符串。我已經更新了答案。 –

+0

謝謝!完美的作品(儘管我仍然爲跳躍所需的箍數感到驚訝) – ekstroem

0

這是越來越醜,但它似乎工作。隨着大量的get() S:

f <- function(DT, id, fid, mid) { 
    mid <- deparse(substitute(mid)) 
    id <- deparse(substitute(id)) 
    fid <- deparse(substitute(fid)) 

    DT[, msib:=.(list(get(id))), by = get(mid)][            
    ,msibs := mapply(setdiff, msib, get(id))][ 
     ,fsib := .(list(get(id))), by = get(fid)][ 
     ,fsibs := mapply(setdiff, fsib, get(id))][ 
      ,siblist := mapply(union, msibs, fsibs)][ 
      ,c("msib","msibs", "fsib", "fsibs") := NULL] 
} 

DT2 <- as.data.table(indata2) 
f(DT2, A, B, C) 

all.equal(DT, DT2) 
# [1] "Different column names" 
+0

這是一個非常多的'get':o)它的工作原理與你一樣,但如果我有一個'data.table',列名爲'id','fid'和'mid'(就像參數)然後我得到錯誤*錯誤在get(mid):無效的第一個參數*。猜測這是第一行給小提琴? – ekstroem