This is not at all something new(*). As maintainer of the
Matrix package, I don't like this inconsistency of base R's diag().
We have had the following -- forever, almost surely inherited
from S and S+ :
diag(x) preserves the storage mode of x for 'complex' and
'double' precision, but converts integer and logicals to double :
> storage.mode(x <- 1i + 1:7); storage.mode(diag(x))
[1] "complex"
[1] "complex"
> storage.mode(x <- 0 + 1:7); storage.mode(diag(x))
[1] "double"
[1] "double"
> storage.mode(x <- 1:7); storage.mode(diag(x))
[1] "integer"
[1] "double"
> storage.mode(x <- 1:7 > 3); storage.mode(diag(x))
[1] "logical"
[1] "double"
and so it is actually a bit cumbersome (and a memory waste in
the case of large matrices) to create a diagonal integer or
logical matrix.
The help page does not mention the current behavior, though you
may say it alludes to the fact that logicals are treated as 0/1
implicitly (**)
If I change this behavior such that logical and integer x are
preserved,
make check-all
which includes all checks, including those of all recommended
packages (including Matrix!) successfully runs through; so at
least base + Recommended R never relies on the current
behavior, nor should any "well programmed" R code ...
Hence my proposal, somewhat tentative for now,
to change this diag(.) behavior.
Martin Maechler
*) and possibly something we "can not" change in R, because too
much code implicitely may be depending on it, but now I hope
we can still...
**) BTW, also including the somewhat amusing case of diag(c("A","B")).
RFC: diag(x, n) not preserving integer and logical x
5 messages · Martin Maechler, Duncan Murdoch, William Dunlap
1 day later
On 07/08/2014, 4:51 AM, Martin Maechler wrote:
This is not at all something new(*). As maintainer of the Matrix package, I don't like this inconsistency of base R's diag(). We have had the following -- forever, almost surely inherited from S and S+ : diag(x) preserves the storage mode of x for 'complex' and 'double' precision, but converts integer and logicals to double :
> storage.mode(x <- 1i + 1:7); storage.mode(diag(x))
[1] "complex" [1] "complex"
> storage.mode(x <- 0 + 1:7); storage.mode(diag(x))
[1] "double" [1] "double"
> storage.mode(x <- 1:7); storage.mode(diag(x))
[1] "integer" [1] "double"
> storage.mode(x <- 1:7 > 3); storage.mode(diag(x))
[1] "logical" [1] "double" and so it is actually a bit cumbersome (and a memory waste in the case of large matrices) to create a diagonal integer or logical matrix. The help page does not mention the current behavior, though you may say it alludes to the fact that logicals are treated as 0/1 implicitly (**)
I think the change to preserve integer makes sense, but preserving logical does not. A diagonal matrix has zeros off the diagonal, and they are not logical. Having diag() sometimes return a matrix with FALSE off the diagonal just looks wrong. Duncan Murdoch
If I change this behavior such that logical and integer x are
preserved,
make check-all
which includes all checks, including those of all recommended
packages (including Matrix!) successfully runs through; so at
least base + Recommended R never relies on the current
behavior, nor should any "well programmed" R code ...
Hence my proposal, somewhat tentative for now,
to change this diag(.) behavior.
Martin Maechler
*) and possibly something we "can not" change in R, because too
much code implicitely may be depending on it, but now I hope
we can still...
**) BTW, also including the somewhat amusing case of diag(c("A","B")).
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
2 days later
Duncan Murdoch <murdoch.duncan at gmail.com>
on Fri, 8 Aug 2014 10:51:10 -0400 writes:
Thank you, Duncan (read inline)
> On 07/08/2014, 4:51 AM, Martin Maechler wrote:
>> This is not at all something new(*). As maintainer of the
>> Matrix package, I don't like this inconsistency of base
>> R's diag(). We have had the following -- forever, almost
>> surely inherited from S and S+ :
>>
>> diag(x) preserves the storage mode of x for 'complex' and
>> 'double' precision, but converts integer and logicals to
>> double :
>>
>> > storage.mode(x <- 1i + 1:7); storage.mode(diag(x)) [1]
>> "complex" [1] "complex" > storage.mode(x <- 0 + 1:7);
>> storage.mode(diag(x)) [1] "double" [1] "double"
>>
>> > storage.mode(x <- 1:7); storage.mode(diag(x)) [1]
>> "integer" [1] "double" > storage.mode(x <- 1:7 > 3);
>> storage.mode(diag(x)) [1] "logical" [1] "double"
>>
>> and so it is actually a bit cumbersome (and a memory
>> waste in the case of large matrices) to create a diagonal
>> integer or logical matrix.
>>
>> The help page does not mention the current behavior,
>> though you may say it alludes to the fact that logicals
>> are treated as 0/1 implicitly (**)
>>
> I think the change to preserve integer makes sense, but
> preserving logical does not. A diagonal matrix has zeros
> off the diagonal, and they are not logical.
That's true if you come from the usual mathematical thinking
about matrices; and the same would extend to triangular
matrices, too.
In (S and) R however, matrices and array()s have always been
more general than in applied math (and have allowed any atomic (and even
more general) content).
> Having diag() sometimes return a matrix with FALSE off the diagonal just
> looks wrong.
[... if you have never seen them ..]
OTOH, sparse matrices (in the Matrix package) and also sparse
and dense triangular matrices (all with their classes) have
always (in the Matrix package) had their logical counter parts,
> Diagonal(5)
5 x 5 diagonal matrix of class "ddiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] 1 . . . .
[2,] . 1 . . .
[3,] . . 1 . .
[4,] . . . 1 .
[5,] . . . . 1
> Diagonal(5) > 0
5 x 5 diagonal matrix of class "ldiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] TRUE . . . .
[2,] . TRUE . . .
[3,] . . TRUE . .
[4,] . . . TRUE .
[5,] . . . . TRUE
>
and if you consider any sparseness for logical matrices, typical
application cases would have the TRUE to be rare rather than the
FALSE... and then of course, we have had the as.numeric(<logical>)
behavior of S and R, all this leading to
the usual FALSE <==> 0 mapping
>> If I change this behavior such that logical and integer x
>> are preserved,
>>
>> make check-all
>>
>> which includes all checks, including those of all
>> recommended packages (including Matrix!) successfully
>> runs through; so at least base + Recommended R never
>> relies on the current behavior, nor should any "well
>> programmed" R code ...
>>
>> Hence my proposal, somewhat tentative for now, to change
>> this diag(.) behavior.
>>
>> Martin Maechler
>>
>> *) and possibly something we "can not" change in R,
>> because too much code implicitely may be depending on it,
>> but now I hope we can still...
>>
>> **) BTW, also including the somewhat amusing case of
>> diag(c("A","B")).
>>
>> ______________________________________________
>> R-devel at r-project.org mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>
Would you allow a list argument to diag() as well? I see that Matrix::Diagonal does not accept it. Perhaps nothing in Matrix deals with matrices of lists, but they are handy and for diag() it may be simpler to allow lists than to check for and reject them. Bill Dunlap TIBCO Software wdunlap tibco.com On Mon, Aug 11, 2014 at 7:26 AM, Martin Maechler
<maechler at stat.math.ethz.ch> wrote:
Duncan Murdoch <murdoch.duncan at gmail.com>
on Fri, 8 Aug 2014 10:51:10 -0400 writes:
Thank you, Duncan (read inline)
> On 07/08/2014, 4:51 AM, Martin Maechler wrote:
>> This is not at all something new(*). As maintainer of the
>> Matrix package, I don't like this inconsistency of base
>> R's diag(). We have had the following -- forever, almost
>> surely inherited from S and S+ :
>>
>> diag(x) preserves the storage mode of x for 'complex' and
>> 'double' precision, but converts integer and logicals to
>> double :
>>
>> > storage.mode(x <- 1i + 1:7); storage.mode(diag(x)) [1]
>> "complex" [1] "complex" > storage.mode(x <- 0 + 1:7);
>> storage.mode(diag(x)) [1] "double" [1] "double"
>>
>> > storage.mode(x <- 1:7); storage.mode(diag(x)) [1]
>> "integer" [1] "double" > storage.mode(x <- 1:7 > 3);
>> storage.mode(diag(x)) [1] "logical" [1] "double"
>>
>> and so it is actually a bit cumbersome (and a memory
>> waste in the case of large matrices) to create a diagonal
>> integer or logical matrix.
>>
>> The help page does not mention the current behavior,
>> though you may say it alludes to the fact that logicals
>> are treated as 0/1 implicitly (**)
>>
> I think the change to preserve integer makes sense, but
> preserving logical does not. A diagonal matrix has zeros
> off the diagonal, and they are not logical.
That's true if you come from the usual mathematical thinking about matrices; and the same would extend to triangular matrices, too. In (S and) R however, matrices and array()s have always been more general than in applied math (and have allowed any atomic (and even more general) content).
> Having diag() sometimes return a matrix with FALSE off the diagonal just
> looks wrong.
[... if you have never seen them ..] OTOH, sparse matrices (in the Matrix package) and also sparse and dense triangular matrices (all with their classes) have always (in the Matrix package) had their logical counter parts,
> Diagonal(5)
5 x 5 diagonal matrix of class "ddiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] 1 . . . .
[2,] . 1 . . .
[3,] . . 1 . .
[4,] . . . 1 .
[5,] . . . . 1
> Diagonal(5) > 0
5 x 5 diagonal matrix of class "ldiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] TRUE . . . .
[2,] . TRUE . . .
[3,] . . TRUE . .
[4,] . . . TRUE .
[5,] . . . . TRUE
>
and if you consider any sparseness for logical matrices, typical application cases would have the TRUE to be rare rather than the FALSE... and then of course, we have had the as.numeric(<logical>) behavior of S and R, all this leading to the usual FALSE <==> 0 mapping
>> If I change this behavior such that logical and integer x
>> are preserved,
>>
>> make check-all
>>
>> which includes all checks, including those of all
>> recommended packages (including Matrix!) successfully
>> runs through; so at least base + Recommended R never
>> relies on the current behavior, nor should any "well
>> programmed" R code ...
>>
>> Hence my proposal, somewhat tentative for now, to change
>> this diag(.) behavior.
>>
>> Martin Maechler
>>
>> *) and possibly something we "can not" change in R,
>> because too much code implicitely may be depending on it,
>> but now I hope we can still...
>>
>> **) BTW, also including the somewhat amusing case of
>> diag(c("A","B")).
>>
>> ______________________________________________
>> R-devel at r-project.org mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
On 11/08/2014, 10:26 AM, Martin Maechler wrote:
Duncan Murdoch <murdoch.duncan at gmail.com>
on Fri, 8 Aug 2014 10:51:10 -0400 writes:
Thank you, Duncan (read inline)
> On 07/08/2014, 4:51 AM, Martin Maechler wrote:
>> This is not at all something new(*). As maintainer of the
>> Matrix package, I don't like this inconsistency of base
>> R's diag(). We have had the following -- forever, almost
>> surely inherited from S and S+ :
>>
>> diag(x) preserves the storage mode of x for 'complex' and
>> 'double' precision, but converts integer and logicals to
>> double :
>>
>> > storage.mode(x <- 1i + 1:7); storage.mode(diag(x)) [1]
>> "complex" [1] "complex" > storage.mode(x <- 0 + 1:7);
>> storage.mode(diag(x)) [1] "double" [1] "double"
>>
>> > storage.mode(x <- 1:7); storage.mode(diag(x)) [1]
>> "integer" [1] "double" > storage.mode(x <- 1:7 > 3);
>> storage.mode(diag(x)) [1] "logical" [1] "double"
>>
>> and so it is actually a bit cumbersome (and a memory
>> waste in the case of large matrices) to create a diagonal
>> integer or logical matrix.
>>
>> The help page does not mention the current behavior,
>> though you may say it alludes to the fact that logicals
>> are treated as 0/1 implicitly (**)
>>
> I think the change to preserve integer makes sense, but
> preserving logical does not. A diagonal matrix has zeros
> off the diagonal, and they are not logical.
That's true if you come from the usual mathematical thinking about matrices; and the same would extend to triangular matrices, too.
I'm not objecting to having a logical matrix, I'm objecting to saying that a matrix with FALSE off the diagonal is a diagonal matrix. A diagonal matrix is one with zeros off the diagonal.
In (S and) R however, matrices and array()s have always been more general than in applied math (and have allowed any atomic (and even more general) content).
> Having diag() sometimes return a matrix with FALSE off the diagonal just
> looks wrong.
[... if you have never seen them ..] OTOH, sparse matrices (in the Matrix package) and also sparse and dense triangular matrices (all with their classes) have always (in the Matrix package) had their logical counter parts,
> Diagonal(5)
5 x 5 diagonal matrix of class "ddiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] 1 . . . .
[2,] . 1 . . .
[3,] . . 1 . .
[4,] . . . 1 .
[5,] . . . . 1
> Diagonal(5) > 0
5 x 5 diagonal matrix of class "ldiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] TRUE . . . .
[2,] . TRUE . . .
[3,] . . TRUE . .
[4,] . . . TRUE .
[5,] . . . . TRUE
That is completely different, and I have no objection to it. Where I would have an objection is if you got that result from diag(rep(TRUE, 5)) or Diagonal(rep(TRUE, 5)) Duncan Murdoch
>
and if you consider any sparseness for logical matrices, typical application cases would have the TRUE to be rare rather than the FALSE... and then of course, we have had the as.numeric(<logical>) behavior of S and R, all this leading to the usual FALSE <==> 0 mapping
>> If I change this behavior such that logical and integer x
>> are preserved,
>>
>> make check-all
>>
>> which includes all checks, including those of all
>> recommended packages (including Matrix!) successfully
>> runs through; so at least base + Recommended R never
>> relies on the current behavior, nor should any "well
>> programmed" R code ...
>>
>> Hence my proposal, somewhat tentative for now, to change
>> this diag(.) behavior.
>>
>> Martin Maechler
>>
>> *) and possibly something we "can not" change in R,
>> because too much code implicitely may be depending on it,
>> but now I hope we can still...
>>
>> **) BTW, also including the somewhat amusing case of
>> diag(c("A","B")).
>>
>> ______________________________________________
>> R-devel at r-project.org mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>