Как избежать апкастинга в системе класса S4 R

Я пытаюсь создать неявно центрированные / масштабированные матрицы в R с использованием S4 (с целью сделать это для больших разреженных матриц).

Я могу создать неявно масштабируемую матрицу, которая правильно выполняет левое и правое умножение с вектором:

      N = 500
P = 100
X = matrix(runif(N * P), N)

setClass("scaled_matrix", contains="matrix", slots=c(scale="numeric"))
setMethod("%*%", signature(x="scaled_matrix", y="numeric"),
          function(x, y) x@.Data %*% (y / x@scale))
setMethod("%*%", signature(x="numeric", y="scaled_matrix"),
          function(x, y) (x %*% y@.Data) / y@scale)

get_scaled = function(A) {
  rmsd = sqrt(apply(A*A, 2, sum)/(nrow(A)-1))
  new("scaled_matrix", A, scale = rmsd)
}

X_scaled = get_scaled(X)
left_test = runif(N)
max(abs(left_test %*% X_scaled - left_test %*% scale(X, center = F))) # small, yay!
right_test = runif(P)
max(abs(X_scaled %*% right_test - scale(X, center = F) %*% right_test )) # small, yay!

И неявно центрированная матрица:

      setClass("centered_matrix", 
         contains="matrix", 
         slots=c(center="numeric"))
setMethod("%*%", signature(x="centered_matrix", y="numeric"),
          function(x, y) (x@.Data %*% y - as.numeric(x@center %*% y)))
setMethod("%*%", signature(x="numeric", y="centered_matrix"),
          function(x, y) (x %*% y@.Data - sum(x) * y@center ))
get_centered = function(A) {
  new("centered_matrix", A, center = apply(A, 2, mean))
}

X_centered = get_centered(X)
max(abs(left_test %*% X_centered - left_test %*% scale(X, scale = F))) # small, yay!
max(abs(X_centered %*% right_test - scale(X, scale = F) %*% right_test )) # small, yay!

Но что, если я захочу их объединить? Я думал, что следующее сработает

      X_centered_scaled = get_scaled(X_centered)

max(abs(left_test %*% X_centered_scaled - left_test %*% scale(X))) # not small, oh no! 
max(abs(X_centered_scaled %*% right_test - scale(X) %*% right_test )) # not small, oh no! 

Насколько я могу судить, проблема в том, что

      class(X_centered_scaled@.Data) # should be centered_matrix but is matrix

т.е. когда X_centered_scaled создается X_centered поднимается до matrix вместо того, чтобы оставаться centered_matrix. Есть ли способ избежать этого? Конечно, я мог бы сделать сингл matrix_centered_scaled class, но мне нравится элегантность соединения этих двух вместе, и это дает возможность просто использовать один или другой.

1 ответ

Хорошо, я понял это. Уловка заключается в использовании явного data слот и пусть он будет особого класса ANY.

      

N = 500
P = 100
X = matrix(runif(N * P), N)

setClass("scaled_matrix", 
         slots=c(data = "ANY", scale="numeric"))
setMethod("%*%", signature(x="scaled_matrix", y="numeric"),
          function(x, y) (x@data %*% (y / x@scale)))
setMethod("%*%", signature(x="numeric", y="scaled_matrix"),
          function(x, y) ((x %*% y@data) / y@scale))

get_scaled = function(A, scale = sqrt(apply(A*A, 2, sum)/(nrow(A)-1))) {
  new("scaled_matrix", data = A, scale = scale)
}

X_scaled = get_scaled(X)
left_test = runif(N)
max(abs(left_test %*% X_scaled - left_test %*% scale(X, center = F))) # small, yay!
right_test = runif(P)
max(abs(X_scaled %*% right_test - scale(X, center = F) %*% right_test )) # small, yay!

setClass("centered_matrix",  
         slots=c(data = "ANY", center="numeric"))
setMethod("%*%", signature(x="centered_matrix", y="numeric"),
          function(x, y) ( x@data %*% y - as.numeric(x@center %*% y)))
setMethod("%*%", signature(x="numeric", y="centered_matrix"),
          function(x, y) (x %*% y@data - sum(x) * y@center ))
get_centered = function(A) {
  new("centered_matrix", data = A, center = apply(A, 2, mean))
}

X_centered = get_centered(X)
max(abs(left_test %*% X_centered - left_test %*% scale(X, scale = F))) # small, yay!
max(abs(X_centered %*% right_test - scale(X, scale = F) %*% right_test )) # small, yay!

X_centered_scaled = get_scaled(X_centered, scale = apply(X, 2, sd)) # doesnt' work either

max(abs(left_test %*% X_centered_scaled - left_test %*% scale(X))) # small, yay! 
max(abs(X_centered_scaled %*% right_test - scale(X) %*% right_test )) # small, yay! 
Другие вопросы по тегам