r s3




為什麼,對於整數向量x,為(x,“numeric”)觸發加載強制的附加S4方法? (2)

雖然我的問題與最近的問題有關,但我懷疑它的答案與R S4對象系統的詳細工作方式有關。

我期待的是:

TLDR; - 所有指示都是as(4L, "numeric")應該調度到一個函數,其主體使用as.numeric(4L)將其轉換為"numeric"向量。)

每當使用as(object, Class)將對象轉換為所需的Class ,就會觸發對coerce()的幕後調用。 反過來, coerce()有一堆基於函數調用簽名調度的方法 - 這裡是第一個和第二個參數的類。 要查看所有可用的S4 coerce()方法的列表,可以運行showMethods("coerce")

這樣做表明只有一種方法可以轉換為"numeric"類。 這是簽名from="ANY", to="numeric"

showMethods("coerce")
# Function: coerce (package methods)
# from="ANY", to="array"
#      ... snip ... 
# from="ANY", to="numeric"
#      ... snip ...

該方法使用as.numeric()來執行其轉換:

getMethod("coerce", c("ANY", "numeric"))
# Method Definition:
# 
# function (from, to, strict = TRUE) 
# {
#     value <- as.numeric(from)
#     if (strict) 
#         attributes(value) <- NULL
#     value
# }
# <environment: namespace:methods>
# 
# Signatures:
#         from  to       
# target  "ANY" "numeric"
# defined "ANY" "numeric"

鑑於它的簽名,以及它是轉換為類"numeric"的唯一coerce()方法這一事實,我希望上面顯示的函數是通過調用as(4L, "numeric")調度的函數。 as(4L, "numeric") 。 只有通過運行以下兩項檢查才能加強這種期望。

## (1) There isn't (apparently!) any specific method for "integer"-->"numeric"
##     conversion
getMethod("coerce", c("integer", "numeric"))
# Error in getMethod("coerce", c("integer", "numeric")) : 
#   no method found for function 'coerce' and signature integer, numeric

## (2) This says that the "ANY"-->"numeric" method will be used for "integer"-->"numeric"
##     conversion    
selectMethod("coerce",  signature=c("integer", "numeric"))
# Method Definition:
# 
# function (from, to, strict = TRUE) 
# {
#     value <- as.numeric(from)
#     if (strict) 
#         attributes(value) <- NULL
#     value
# }
# <environment: namespace:methods>
# 
# Signatures:
#         from      to       
# target  "integer" "numeric"
# defined "ANY"     "numeric"

實際發生了什麼:

TLDR;實際上,調用as(4L, "numeric")加載和調度到一個什麼都不做的方法。)

儘管上面提到的所有跡象, as(4L, "numeric") 沒有調度到帶有簽名c("ANY", "numeric")調用的coerce()方法。

以下是一些顯示方式:

## (1) as.numeric() would do the job, but as(..., "numeric") does not
class(as(4L, "numeric"))
#[1] "integer"
class(as.numeric(4L))
# [1] "numeric"

## (2) Tracing shows that the "generic" method isn't called
trace("coerce", signature=c("ANY", "numeric"))

as(c(FALSE, TRUE), "numeric")        ## <-- It's called for "logical" vectors
# Tracing asMethod(object) on entry   
# [1] 0 1

as(c("1", "2"), "numeric")           ## <-- and for "character" vectors
# Tracing asMethod(object) on entry   
# [1] 1 2    

as(c(1L, 2L), "numeric")             ## <-- but not for "integer" vectors 
# [1] 1 2

untrace("coerce")

那麼,使用什麼方法? 好吧,顯然調用as(4L, "numeric")將新的S4方法添加到coerce()的方法列表中,並且它是使用的。
(在我們嘗試第一次"integer""character"轉換之前,將以下調用的結果與它們產生的結果進行比較。)

## At least one conversion needs to be attempted before the  
## "integer"-->"numeric" method appears.
as(4L, "numeric")  

## (1) Now the methods table shows a new "integer"-->"numeric" specific method   
showMethods("coerce")    
# Function: coerce (package methods)
# from="ANY", to="array"
#      ... snip ... 
# from="ANY", to="numeric"
#      ... snip ...
# from="integer", to="numeric"        ## <-- Here's the new method
#      ... snip ...

## (2) selectMethod now tells a different story
selectMethod("coerce",  signature=c("integer", "numeric"))
# Method Definition:
# 
# function (from, to = "numeric", strict = TRUE) 
# if (strict) {
#     class(from) <- "numeric"
#     from
# } else from
# <environment: namespace:methods>
# 
# Signatures:
#         from      to       
# target  "integer" "numeric"
# defined "integer" "numeric"

我的問題:

  1. 為什麼as(4L, "numeric")沒有調度到signature=c("ANY", "numeric")的可用coerce()方法?

  2. 如何/為什麼它會在S4方法表中添加新方法?

  3. 從哪裡(在R的源代碼或其他地方), signature=c("integer", "numeric")coerce()方法的定義signature=c("integer", "numeric")來了嗎?


我不確定我是否可以詳盡地回答你的問題,但我會試試。

as()函數的幫助說明:

函數'as'將'object'變成'Class'類的對象。 在這樣做時,它使用“強制方法”,使用S4類和方法,但以一種特殊的方式。

[...]

假設'object'不是所需的類,'as'首先在函數'coerce'的方法表中查找簽名'c(from = class(object),to = Class)'的方法,方法選擇將以相同的方式進行初始查找。

[...]

如果沒有找到方法,'as'尋找一個方法。 首先,如果“Class”或“class(object)”是另一個的超類,則類定義將包含構造強制方法所需的信息。 在通常的情況下,子類包含超類(即,具有其所有的槽),該方法通過提取或替換繼承的槽來構造。

如果您查看as()函數的代碼(看到它,鍵入as (沒有括號!)到R控制台),這正是您可以看到的 - 見下文。 首先它查找asMethod ,如果它找不到任何它試圖構造一個,最後它執行它:

if (strict) 
    asMethod(object)
else asMethod(object, strict = FALSE)

當你複製粘貼as()函數的代碼並定義你自己的函數時 - 讓我們稱之為myas() - 你可以在上面提到的if (strict)上面插入一個print(asMethod)來獲取用於強制的函數。 在這種情況下,輸出是:

> myas(4L, 'numeric')
function (from, to = "numeric", strict = TRUE) 
if (strict) {
    class(from) <- "numeric"
    from
} else from
<environment: namespace:methods>
attr(,"target")
An object of class “signature”
     from        to 
"integer" "numeric" 
attr(,"defined")
An object of class “signature”
     from        to 
"integer" "numeric" 
attr(,"generic")
[1] "coerce"
attr(,"generic")attr(,"package")
[1] "methods"
attr(,"class")
[1] "MethodDefinition"
attr(,"class")attr(,"package")
[1] "methods"
attr(,"source")
[1] "function (from, to = \"numeric\", strict = TRUE) "
[2] "if (strict) {"                                    
[3] "    class(from) <- \"numeric\""                   
[4] "    from"                                         
[5] "} else from"                                      
[1] 4

因此,正如您所看到的(查看attr(,"source") ), as(4L, 'numeric')只是將類numeric賦給輸入對象並返回它。 因此,以下兩個片段是等效的(對於這種情況!):

> # Snippet 1
> x = 4L
> x = as(x, 'numeric')

> # Snippet 2
> x = 4L
> class(x) <- 'numeric'

有趣的是,兩者都“沒有”。 更有趣的是,另一種方式是:

> x = 4
> class(x)
[1] "numeric"
> class(x) <- 'integer'
> class(x)
[1] "integer"

我不太確定這一點(因為class方法似乎是在C實現的) - 但我的猜測是,在分配類numeric ,它首先檢查它是否已經是numeric 。 這可能是integernumeric (雖然不是double )的情況 - 請參閱下面的“歷史異常”引用:

> x = 4L
> class(x)
[1] "integer"
> is.numeric(x)
[1] TRUE

關於as.numeric :這是一個通用的方法並調用as.double() ,這就是它“工作”的原因(來自as.numeric的R幫助):

R是一個歷史異常,它的浮點向量有兩個名稱,'double'和'numeric'(之前有'real')。

'double'是該類型的名稱。 'numeric'是模式的名稱,也是隱式類的名稱。

關於問題(1) - (3):神奇發生在函數頂部的那四行中:

where <- .classEnv(thisClass, mustFind = FALSE)
coerceFun <- getGeneric("coerce", where = where)
coerceMethods <- .getMethodsTable(coerceFun, environment(coerceFun), inherited = TRUE)
asMethod <- .quickCoerceSelect(thisClass, Class, coerceFun, coerceMethods, where)

我沒時間深入那裡,對不起。

希望有所幫助。


查看as()的源代碼,它有兩個部分。 (為清楚起見,縮短了源代碼)。 首先,它會查找coerce()現有方法,如上所述。

function (object, Class, strict = TRUE, ext = possibleExtends(thisClass, 
    Class)) 
{
    thisClass <- .class1(object)
    where <- .classEnv(thisClass, mustFind = FALSE)
    coerceFun <- getGeneric("coerce", where = where)
    coerceMethods <- .getMethodsTable(coerceFun, environment(coerceFun), 
        inherited = TRUE)
    asMethod <- .quickCoerceSelect(thisClass, Class, coerceFun, 
        coerceMethods, where)

    # No matching signatures from the coerce table!!!
    if (is.null(asMethod)) {
        sig <- c(from = thisClass, to = Class)
        asMethod <- selectMethod("coerce", sig, optional = TRUE, 
            useInherited = FALSE, fdef = coerceFun, mlist = getMethodsForDispatch(coerceFun))

如果它沒有找到任何方法,就像在這種情況下那樣,它會嘗試創建一個新方法,如下所示:

        if (is.null(asMethod)) {
            canCache <- TRUE
            inherited <- FALSE

            # The integer vector is numeric!!!
            if (is(object, Class)) {
                ClassDef <- getClassDef(Class, where)
                if (identical(ext, FALSE)) {}
                else if (identical(ext, TRUE)) {}
                else {
                  test <- ext@test

                  # Create S4 coercion method here
                  asMethod <- .makeAsMethod(ext@coerce, ext@simple, 
                    Class, ClassDef, where)
                  canCache <- (!is(test, "function")) || identical(body(test), 
                    TRUE)
                }
            }
            if (is.null(asMethod)) {}
            else if (canCache) 
                asMethod <- .asCoerceMethod(asMethod, thisClass, 
                  ClassDef, FALSE, where)
            if (is.null(asMethod)) {}
            else if (canCache) {
                cacheMethod("coerce", sig, asMethod, fdef = coerceFun, 
                  inherited = inherited)
            }
        }
    }

    # Use newly created method on object here
    if (strict) 
        asMethod(object)
    else asMethod(object, strict = FALSE)

順便說一句,如果你只處理基本的原子類型,我會堅持基本函數並避免使用methods包; 使用methods的唯一原因是處理S4對象。




r   s4  

s4