4

という名前の S4 クラスを定義しました。そのcellインスタンスを 3x3 マトリックスに割り当てたいと考えています (明確にするために 3x3 が選択されています)。次のコードは R バージョン 2.15.1 で機能し、単純なケースで R の動作を再現します。タイプのオブジェクトをマトリックスに割り当てることができることがわかりcellました。そのエントリは最初に で空のリストに初期化されmatrix(list(),3,3)、その後、新しいタイプのオブジェクトをエントリに割り当てcellます。問題は、なぜそれが機能するのかということです。

    setClass("cell", representation = representation(       
        A="numeric",  # a field
        B="numeric")) # another one

    # initialize the cell
    setMethod("initialize", "cell", function(.Object, a,b) {
        .Object@A <- a;
        .Object@B <- b;
    .Object})

    createGrid <- function(a,b) {
        grid <- matrix(list(),3,3)  # note initialization to list()
        for (i in 1:3 )
            for (j in 1:3)
                grid[[i,j]] <- new("cell",j,i);
        grid}

これはサンプル セッションです。

    > source("stackoverflow.R")
    > grid <- createGrid(1,2)
    > grid[[1,3]]
    An object of class "cell"
    Slot "A":
    [1] 3

    Slot "B":
    [1] 1

    > grid[[2,3]]
    An object of class "cell"
    Slot "A":
    [1] 3

    Slot "B":
    [1] 2

createGrid()空のリストの割り当てを変更して変更grid<- matrix(0,3,3)すると、エラーが発生します。

    > grid <- createGrid0(1,2)
    Error in grid[[i, j]] <- new("cell", j, i) : 
      more elements supplied than there are to replace

これは驚くべきことではありませんが、動作するコードにたどり着きました。を使用してセルの 3x3 マトリックスを定義しようとする次の試みはnew()失敗します。

    > grid <- matrix(new("cell",1,2),3,3)
    Error in as.vector(data) : 
      no method for coercing this S4 class to a vector

問題は、なぜ最初のものが機能するのかということです。

4

3 に答える 3

3

質問への回答ではありませんが...

通常、ベクトルの観点から考えるとよいので、'cell' の代わりに、行列全体を表す 'Cell' を使用することもできます。これが実装です。アイデアは、Cell が行列を拡張し、追加の値を含むベクトルを使用するというものです。

setClass("Cell", representation("matrix", A="numeric", B="numeric"))

妥当性関数を使用して、A と B が同じ長さになるように制約します。

setValidity("Cell", function(object) {
    msg <- NULL
    if (length(object@A) != length(object@B))
        msg <- c(msg, "'A' and 'B' must be the same length")
    if (is.null(msg)) TRUE else msg
})

インデックスを持つ Cell の行列を A と B に初期化するコンストラクタを作成します。

Cell <- function(A, B, ...)
    new("Cell", matrix(seq_along(A), ...), A=A, B=B)

引数は、matrix()の...非データ引数 (nrow、ncol、dimnames など) です。

一部の機能は無料で入手できますが (dim、nrow、ncol、length など)、サブセット設定や、おそらくその他の操作を実装する必要があります。単一ブラケット サブセットの場合、操作を基礎となるマトリックスに渡し、結果のインデックスを使用して A と B をサブセット化し、更新された値で新しい Cell インスタンスを作成します。

setMethod("[", "Cell", function(x, i, j, ..., drop=TRUE) {
    m <- callNextMethod()
    initialize(x, m, A=x@A[m], B=x@B[m])
})

最後に、show メソッドを実装します。

setMethod(show, "Cell", function(object) {
    cat("class:", class(object), "\n")
    cat("dim:", dim(object), "\n")
    m <- object@.Data
    m[] <- object@A
    cat("A:\n"); print(m)
    m[] <- object@B
    cat("B:\n"); print(m)
})

そして実際に:

> Cell(1:6, 6:1, 3, 2)[c(3, 1), 2:1]
class: Cell 
dim: 2 2 
A:
     [,1] [,2]
[1,]    6    3
[2,]    4    1
B:
     [,1] [,2]
[1,]    1    4
[2,]    3    6

この実装にはいくつかの利点があるようです。行列は単一の型を持つと想定されており、ここではそれが当てはまります ( の場合cell、行列の要素はリストであるため、任意のデータを含むことができます)。マトリックスから継承されたかなりの機能があります。おそらく、Cell 行列に操作を実装したいと考えており、基になるベクトル A、B は、たとえばグループ ジェネリック Arith を介して効率的に操作できます。

setMethod("Arith", c("Cell", "Cell"), function(e1, e2) {
    A <- callGeneric(e1@A, e2@A)
    B <- callGeneric(e1@B, e2@B)
    initialize(e1, e1@.Data, A=A, B=B)
})

その後

> c1 = Cell(1:6, 6:1, 3, 2)
> c2 = Cell(6:1, 1:6, 3, 2)
> c1 + c2
class: Cell 
dim: 3 2 
A:
     [,1] [,2]
[1,]    7    7
[2,]    7    7
[3,]    7    7
B:
     [,1] [,2]
[1,]    7    7
[2,]    7    7
[3,]    7    7
> c1 * c2
class: Cell 
dim: 3 2 
A:
     [,1] [,2]
[1,]    6   12
[2,]   10   10
[3,]   12    6
B:
     [,1] [,2]
[1,]    6   12
[2,]   10   10
[3,]   12    6
于 2012-08-08T14:23:05.763 に答える
1

を使用した別のソリューションouter

createGrid <- function(a,b) { 
    grid <- outer(1:3, 1:3, FUN = Vectorize(function(x, y) new("cell", x, y)));
    grid}


grid <- createGrid(1, 2)
grid[[2, 3]]

## An object of class "cell"
## Slot "A":
## [1] 2

## Slot "B":
## [1] 3
于 2012-08-08T09:06:32.910 に答える