2014-05-09 89 views
9

如果我想爲函數的參數錯誤或警告執行deparse,如果參數轉換爲data.table功能:在使用data.table作爲參數的函數中使用deparse(substitute())

e <- data.frame(x = 1:10) 
### something strange is happening 
foo <- function(u) { 
    u <- data.table(u) 
    warning(deparse(substitute(u)), " is not a data.table") 
    u 
} 
foo(e) 

## foo(e) 
##  x 
## 1: 1 
## 2: 2 
## 3: 3 
## 4: 4 
## 5: 5 
## 6: 6 
## 7: 7 
## 8: 8 
## 9: 9 
## 10: 10 
## Warning message: 
## In foo(e) : 
## structure(list(x = 1:10), .Names = "x", row.names = c(NA, -10L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x10026568>) is not a data.table 

如果我deparse之前data.table一切工作正常:

### ok 
foo1 <- function(u) { 
    nu <- deparse(substitute(u)) 
    u <- data.table(u) 
    warning(nu, " is not a data.table") 
    u 
} 
## foo1(e) 
##  x 
## 1: 1 
## 2: 2 
## 3: 3 
## 4: 4 
## 5: 5 
## 6: 6 
## 7: 7 
## 8: 8 
## 9: 9 
## 10: 10 
## Warning message: 
## In foo1(e) : e is not a data.table 

有通過的方式沒有區別,如果e已經一個data.table或無噸。 我發現它的目的,當我分析一些代碼,其中deparse是非常耗時,因爲e是相當大的。

這裏發生了什麼?如何處理data.framedata.table輸入的這些功能?

nachti

回答

10

這是因爲substitute的行爲不同,當你正在處理一個正常的變量,而不是一個承諾對象。承諾對象是一個形式化的參數,並有一個特殊的插槽,其中包含生成它的表達式。換句話說,promise對象是函數中的一個變量,它是該函數參數列表的一部分。當您在函數的promise對象上使用substitute時,它將在調用指定給該形式參數的函數中檢索表達式。從?substitute:如果不是在一個ENV勢必象徵,它是不變的:

換人通過檢查如下解析樹的每個組件發生。如果它是承諾對象,即對函數的正式參數或使用delayedAssign(),明確創建的承諾的表達式槽替換該符號。如果它是一個普通變量,則其值被替換爲,除非env是.GlobalEnv,在這種情況下該符號保持不變。

在你的情況,你實際上有一個新的使用覆蓋原承諾變量:

u <- data.table(u) 

此時u成爲一個包含數據表中的普通變量。當substituteu之後,substitute只是返回數據表,其中deparse會返回到生成它的R語言,這就是爲什麼它很慢。

這也解釋了爲什麼你的第二個例子工作。你substitute而變量仍然是一個承諾(即覆蓋u之前)。這也是第二個問題的答案。在你覆蓋你的承諾之前,或者不要覆蓋你的承諾。

有關詳細信息,請參閱R語言定義(承諾),我這裏摘錄的section 2.1.8

承諾對象是討論R的懶惰評估機制的一部分。它們包含三個插槽:值,表達式和環境。當一個函數被調用時,參數被匹配,然後每個形式參數都被綁定到一個promise。爲該形式參數提供的表達式以及函數被調用的環境指針存儲在promise中。

+0

@nachti,這不是回答你的問題嗎? – BrodieG

+0

@ [BrodieG](http://stackoverflow.com/users/2725969/brodieg):謝謝你的回答。如上所述:如何處理'data.frame'和'data.table'輸入的這些函數?我應該複製它(需要很多空間)嗎?或者先把所有東西先抹掉,然後覆蓋它? – nachti

+1

@ nachti,後者,先貶低。另外,如果你想避免拷貝,你應該考慮使用'setDT'而不是'data.table'。前者通過引用創建數據表。 – BrodieG

0

您也可以使用sprintf以及is.data.table

> e <- data.frame(x = 1:10) 
> foo <- function(u){ 
     nu <- deparse(substitute(u)) 
     if(!is.data.table(u)){ 
      warning(sprintf('%s is not a data table', nu)) 
      u 
     } else { 
      u 
     } 
    } 
> foo(e) 
    x 
1 1 
2 2 
3 3 
4 4 
5 5 
6 6 
7 7 
8 8 
9 9 
10 10 
Warning message: 
In foo(e) : e is not a data table 
+0

感謝您的回答! @Richard:好主意,但它仍然不是data.table,我不能使用':='作爲例子。它應該在一個函數中使用,該函數接受'data.frame'或'data.table'作爲輸入,在'data.table'語法中執行某些操作,並在輸入時將其轉換爲'data.frame'是一個。順便說一下'u'很大(〜250 MB) – nachti