Reputation: 139
How can I remove the diagonal elements (diagL) from my matrix L using R? I tried using the following:
subset(L, select=-diag(L)) or
subset(L, select=-c(diag(L)))
but I get 0 numbers...
Upvotes: 10
Views: 21304
Reputation: 1204
Still using basic R, it is possible to use a combination of upper.tri()
and lower.tri
to find what you are looking for in one line. To have it handier, I created a one-line function. The code goes as follows.
a <- matrix(rnorm(100), nrow = 4, ncol = 4)
select_all_but_diag <- function(x) matrix(x[lower.tri(x, diag = F) | upper.tri(x, diag = F)], nrow = nrow(x) - 1, ncol = ncol(x))
select_all_but_diag(a)
This is the matrix a
before (in my case):
[,1] [,2] [,3] [,4]
[1,] 0.3 2.5 -0.5 2.8
[2,] 0.7 1.1 -1.4 -0.7
[3,] 0.9 0.8 1.6 0.5
[4,] -0.8 -0.3 -0.9 1.6
And this is the select_all_but_diag(a)
output matrix:
[,1] [,2] [,3] [,4]
[1,] 0.7 2.5 -0.5 2.8
[2,] 0.9 0.8 -1.4 -0.7
[3,] -0.8 -0.3 -0.9 0.5
EDIT for row-major
Instead, if you want the collapse to be row-major, you can use this extended version of the function which allows you to collapse the matrix reducing the number of columns instead of the number of rows.
select_all_but_diag <- function(x, collapse_by = "row") {
if(collapse_by == "row") matrix(x[lower.tri(x, diag = F) | upper.tri(x, diag = F)], nrow = nrow(x) - 1, ncol = ncol(x))
else if(collapse_by == "col") t(matrix(t(x)[lower.tri(x, diag = F) | upper.tri(x, diag = F)], nrow = nrow(x) - 1, ncol = ncol(x)))
else stop("collapse_by accepts only 'row' or 'col'.")
}
a
select_all_but_diag(a, collapse_by = "col")
This is the output of the latter:
[,1] [,2] [,3]
[1,] 2.5 -0.5 2.8
[2,] 0.7 -1.4 -0.7
[3,] 0.9 0.8 0.5
[4,] -0.8 -0.3 -0.9
Upvotes: 0
Reputation:
The R programming language? I like C better, it is easier to spell.
One way is to create a matrix with the numbers the way I like them to look:
a<-t(matrix(1:16,nrow=4,ncol=4))
which looks like:
[,1] [,2] [,3] [,4]
[1,] 1 2 3 4
[2,] 5 6 7 8
[3,] 9 10 11 12
[4,] 13 14 15 16
Delete the values on the diagonal:
diag(a)=NA
which results in:
[,1] [,2] [,3] [,4]
[1,] NA 2 3 4
[2,] 5 NA 7 8
[3,] 9 10 NA 12
[4,] 13 14 15 NA
To actually REMOVE the values, rather than just making them go away, we need to recast:
a<-t(matrix(t(a)[which(!is.na(a))],nrow=3,ncol=4))
Which results in:
[,1] [,2] [,3]
[1,] 2 3 4
[2,] 5 7 8
[3,] 9 10 12
[4,] 13 14 15
which is the same thing as we got in C, above.
This is a little circuitous but it results in what I see as a correct answer. I would be interested in seeing an improved solution by somebody that knows R better than I do.
A bit of an explanation on the assignment:
a<-t(matrix(t(a)[which(!is.na(a))],nrow=3,ncol=4))
!is.na(a)
gives us a list of TRUE, FALSE values for which elements were nulled out.which(!is.na(a))
gives us a list of subscripts for each of the true elements.t(a)
transposes the matrix since we need to pull based upon the subscripts in #2.t(a)[which(!is.na(a))]
gives us a list of numbers that is missing the diagonal NA values.matrix(t(a)[which(!is.na(a))],nrow=3,ncol=4)
converts the list from #4 into a matrix, which is the transpose of what we want.a<-t(matrix(1:16,nrow=4,ncol=4))
(the whole thing) transposes #5 into the form we want and assigns it to the a
variable.This works with cases such as a<-t(matrix(11:26,nrow=4,ncol=4))
.
Upvotes: 19
Reputation: 9017
Here is some artificial data for illustration:
x <- matrix(1:16, 4, 4)
n <- nrow(x)
x
[,1] [,2] [,3] [,4]
[1,] 1 5 9 13
[2,] 2 6 10 14
[3,] 3 7 11 15
[4,] 4 8 12 16
After vectorizing the matrix x
, the diagonal elements correspond to the indices 1, n+2, 2*n+3, ...
, that is, to the sequence seq(1, n^2, n+1)
. You can remove these indices by
x[-seq(1,n^2,n+1)]
[1] 2 3 4 5 7 8 9 10 12 13 14 15
After "removing the diagonal" of the matrix, you can either shift the lower triangular matrix upward to get a matrix with n-1
rows and n
columns by
matrix(x[-seq(1,n^2,n+1)], n-1, n)
[,1] [,2] [,3] [,4]
[1,] 2 5 9 13
[2,] 3 7 10 14
[3,] 4 8 12 15
or, and this is probably what you want, you can shift the lower triangular matrix to the right to get a matrix with n
rows and n-1
columns by transposing x
before removing the diagonal indices and transposing it back afterwards
t(matrix(t(x)[-seq(1,n^2,n+1)], n-1, n))
[,1] [,2] [,3]
[1,] 5 9 13
[2,] 2 10 14
[3,] 3 7 15
[4,] 4 8 12
Upvotes: 9
Reputation:
Keep in mind that the diagonal is going to have the same X and Y index. A quick program to zero out the diagonal in C follows:
#include <stdio.h>
static void printMat(char mat[4][4], char *comment)
{
printf("%s:\n", comment);
for(int jj=0; jj<4; jj++) {
for(int ii=0; ii<4; ii++) {
printf("%2d ",mat[jj][ii]);
}
printf("\n");
}
}
main()
{
static char matrix[4][4]= {
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9,10,11,12},
{13,14,15,16}
};
printMat(matrix,"Before");
for(int ii=0; ii<4; ii++) {
matrix[ii][ii]=0;
}
printMat(matrix,"After");
}
This results in:
Before:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
After:
0 2 3 4
5 0 7 8
9 10 0 12
13 14 15 0
To REMOVE rather that just clear the diagonal is more complicated.
This should do the trick: (Keep in mind that a memcpy of zero bytes can address elements that are not there.)
#include <stdio.h>
#include <strings.h>
static void printMat(char *mat, int xDim, int yDim,char *comment)
{
printf("%s:\n", comment);
for(int jj=0; jj<yDim; jj++) {
for(int ii=0; ii<xDim; ii++) {
printf("%2d ",(mat[(jj)*xDim+ii]) );
}
printf("\n");
}
}
main()
{
static char matrix[4][4]= {
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9,10,11,12},
{13,14,15,16}
};
static char new[4][3];
printMat((char*)matrix,4,4,"Before");
for(int ii=0; ii<4; ii++) {
memcpy(&new[ii][0], &matrix[ii][0],ii);
memcpy(&new[ii][ii],&matrix[ii][ii+1], 4-ii);
}
printMat((char*)new,3,4,"After");
}
Results in:
Before:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
After:
2 3 4
5 7 8
9 10 12
13 14 15
Of course, if you want something in another language, it helps to ask.
Upvotes: 2