Reputation: 226522
Converting a sparse Matrix
object (from the Matrix
package) to a plain old base-R dense matrix
object appears to lose the row/column names.
m <- matrix(0, 3,3, dimnames = list(LETTERS[1:3], LETTERS[1:3]))
dimnames(m)
## [[1]]
## [1] "A" "B" "C"
##
## [[2]]
## [1] "A" "B" "C"
Converting to a Matrix
is fine:
dimnames(M <- Matrix::Matrix(m))
## [[1]]
## [1] "A" "B" "C"
## [[2]]
## [1] "A" "B" "C"
But converting back appears to lose the row/column names:
dimnames(as.matrix(M))
## NULL
dimnames(as(M, "matrix"))
## NULL
I know I can work around it by storing the dimnames and then attaching them to the new object (see below), but I don't feel like I should have to ... am I missing either a transparent/better way of making the conversion, or a logical reason why dimnames should not be preserved when making this conversion??
## workaround/hack
dn <- dimnames(m)
m2 <- as.matrix(M)
dimnames(m2) <- dn
To clarify, I want to handle the case where M
already exists has been defined but m
hasn't (i.e. the old m[] <-
trick to replace the contents of m
while leaving its attributes unchanged doesn't seem to work ...)
Upvotes: 4
Views: 743
Reputation: 11326
[Update 1: bug report] [Update 2: patch]
The loss of [dD]imnames
here appears to be a bug in Matrix
, at least in the implementation of the specific subclass of "Matrix"
in question, namely "ddiMatrix"
(see ?`ddiMatrix-class`
).
library("Matrix")
M <- Matrix(0, 3L, 3L, dimnames = list(LETTERS[1:3], LETTERS[1:3]))
M
3 x 3 diagonal matrix of class "ddiMatrix"
A B C
A 0 . .
B . 0 .
C . . 0
There is a method for coercing from "ddiMatrix"
to "matrix"
, called when you do as(M, "matrix")
, but it does not preserve [dD]imnames
, as you observed.
selectMethod("coerce", signature(from = "ddiMatrix", to = "matrix"))
Method Definition:
function (from, to = "matrix", strict = TRUE)
base::diag(if (from@diag == "U") as1(from@x) else from@x, nrow = from@Dim[1])
<bytecode: 0x11e7dc068>
<environment: namespace:Matrix>
Signatures:
from to
target "ddiMatrix" "matrix"
defined "ddiMatrix" "matrix"
You can work around the bug with an intermediate coercion from "ddiMatrix"
to "dgCMatrix"
, which is a more general and more carefully implemented class of sparse numeric matrices (see ?`dgCMatrix-class`
). The coercions from "ddiMatrix"
to "dgCMatrix"
to "matrix"
do preserve [dD]imnames
:
MM <- as(M, "dgCMatrix")
MM
3 x 3 sparse Matrix of class "dgCMatrix"
A B C
A . . .
B . . .
C . . .
m <- as(MM, "matrix")
m
A B C
A 0 0 0
B 0 0 0
C 0 0 0
or simply m <- as(as(M, "dgCMatrix"), "matrix")
.
To be clear, your approach is both faster and more transparent. I am presenting this one mainly to expose the fact that the bug affects "ddiMatrix"
but not all classes of sparse numeric matrices implemented in Matrix
. If your use case doesn't actually require "ddiMatrix"
, then you don't necessarily need to worry about preserving [dD]imnames
yourself.
FWIW, when struggling with Matrix
coercions, I have found it helpful to:
showClass("Matrix")
to remind myself of the inheritance structure of the "Matrix"
subclasses.?`<classname>-class`
for human-readable descriptions of the classes reported in (1) and their slots.selectMethod("coerce", signature(from=, to=))
to find the method actually called by as
(though, as coercions are often implemented in C, the selectMethod
result can be unhelpful apart from telling me what to search in Matrix/src
).Upvotes: 2
Reputation: 26690
It looks like the difference between "m" and "M" is the object type:
m <- matrix(0, 3,3, dimnames = list(LETTERS[1:3], LETTERS[1:3]))
M <- Matrix::Matrix(m)
typeof(m)
#> [1] "double"
typeof(M)
#> [1] "S4"
I don't know if there's an 'easy' way, but another alternative might be:
m <- matrix(0, 3,3, dimnames = list(LETTERS[1:3], LETTERS[1:3]))
dimnames(m)
#> [[1]]
#> [1] "A" "B" "C"
#>
#> [[2]]
#> [1] "A" "B" "C"
M <- Matrix::Matrix(m)
m <- as.matrix(M)
dimnames(m) <- M@Dimnames
m
#> A B C
#> A 0 0 0
#> B 0 0 0
#> C 0 0 0
Or maybe:
m <- matrix(M, nrow = M@Dim, dimnames = M@Dimnames)
m
#> A B C
#> A 0 0 0
#> B 0 0 0
#> C 0 0 0
Upvotes: 1