I am trying to fill some rows of a (500,2) matrix with the row vector (1,0) using this code, last line is to verify the result:
data<-matrix(ncol=2,nrow=500)
data[41:150,]<-matrix(c(1,0),nrow=1,ncol=2,byrow=TRUE)
data[41:45,]
But the result is
> data[41:45,]
[,1] [,2]
[1,] 1 1
[2,] 0 0
[3,] 1 1
[4,] 0 0
[5,] 1 1
instead of
> data[41:45,]
[,1] [,2]
[1,] 1 0
[2,] 1 0
[3,] 1 0
[4,] 1 0
[5,] 1 0
(1) What am I doing wrong?
(2) Why aren't the row indices in the result 41, 42, 43, 44 and 45?
You're trying to fill a part of the matrix, so the block you're trying to drop in there should be of the right size:
data[41:150,]<-matrix(c(1,0),nrow=110,ncol=2,byrow=TRUE)
# nrow = 110, instead of 1 !!!!
Otherwise your piece-to-be-added will be reverted to vector and added columnwise. Try, for example, this:
data[41:150,] <- matrix(c(1,2,3,4,5), nrow=5, ncol=2, byrow=TRUE)
data[41:45,]
[,1] [,2]
[1,] 1 1
[2,] 3 3
[3,] 5 5
[4,] 2 2
[5,] 4 4
Can one complain? Yes, and now. No, because R behaves as documented (matrices are vectors with dimension attributes, and recycling works on vectors). Yes, because although recycling can be convenient, it may create false expectations.
Why aren't row indices 41,42,43,... ? I don't know, that's just the way matrices and vectors behave.
> (1:10)[5:6]
[1] 5 6
(Notice there's [1] in the output, not [5].)
Data frames behave differently, so you would see the original line numbers for slices:
as.data.frame(data)[45:50,]
It will be cleaner to just do this column-wise:
data[41:150, 1L] = 1
data[41:150, 2L] = 0
You could also accomplish this in one line with matrix indexing like so:
data[cbind(rep(41:150, each = 2L), 1:2)] = 1:0
You could use rep.
data[41:150,] <- rep(1:0, each=150-41+1)
#> data[41:45,]
# [,1] [,2]
#[1,] 1 0
#[2,] 1 0
#[3,] 1 0
#[4,] 1 0
#[5,] 1 0
I think MichaelChirico approach is the cleanest/savest to use.
Related
I have a matrix and I would like to perform a calculation on each number in the matrix so that I get another matrix with the same dimensions only with the results of the calculation. This should be easy except that part of the equation is dependent on which column I am accessing because I will need to have an internal reference to the number at row [3,] within that column.
The equation I would like to apply is:
output matrix value = input_matrix value at a given position + (1- (matrix value at [3,] and in the same column as the input matrix value))
For example, For (1,1) in the matrix the calculation would be 1+(1-3)
For position (1,2) in the matrix, the calculation would be 5+(1-7)
input_matrix<- matrix(1:12, nrow = 4, ncol = 3)
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
The output matrix should end up looking like this:
[,1] [,2] [,3]
[1,] -1 -1 -1
[2,] 0 0 0
[3,] 1 1 1
[4,] 2 2 2
I have tried doing something like this:
output_matrix<-apply(input_matrix,c(1,2), function(x) x+(1-(input_matrix[3,])))
but that gives me three matrices with the wrong dimensions as the output.
I am thinking that perhaps I can perhaps just modify the function in the above calculation to get this to work, or alternatively write something that iterates over each column of the matrix but I am not sure exactly how to do this in a way that gives me the output matrix that I want.
Any help would be greatly appreciated.
I think this should work for you:
apply(input_matrix, margin = 2, function(x) x + (1 - x[3]))
[,1] [,2] [,3]
[1,] -1 -1 -1
[2,] 0 0 0
[3,] 1 1 1
[4,] 2 2 2
We could also do this in a vectorized way
input_matrix + (1 - input_matrix[3,][col(input_matrix)])
# [,1] [,2] [,3]
#[1,] -1 -1 -1
#[2,] 0 0 0
#[3,] 1 1 1
#[4,] 2 2 2
Suppose I have a symmetric matrix:
> mat <- matrix(c(1,0,1,0,0,0,1,0,1,1,0,0,0,0,0,0), ncol=4, nrow=4)
> mat
[,1] [,2] [,3] [,4]
[1,] 1 0 1 0
[2,] 0 0 1 0
[3,] 1 1 0 0
[4,] 0 0 0 0
which I would like to analyse:
> which(mat==1, arr.ind=T)
row col
[1,] 1 1
[2,] 3 1
[3,] 3 2
[4,] 1 3
[5,] 2 3
now the question is: how am I not considering duplicated cells? As the resulting index matrix shows, I have the rows 2 and 4 pointing respectively to (3,1) and (1,3), which is the same cell.
How do I avoid such a situation? I only need a reference for each cell, even though the matrix is symmetric. Is there an easy way to deal with such situations?
EDIT:
I was thinking about using upper.tri or lower.tri but in this case what I get is an vector version of the matrix and I am not able to get back to the (row, col) notation.
> which(mat[upper.tri(mat)]==1, arr.ind=T)
[1] 2 3
EDIT II
expected output would be something like an unique over the couple of (row, col) and (col, row):
row col
[1,] 1 1
[2,] 3 1
[3,] 3 2
Since you have symmetrical matrix you could do
which(mat == 1 & upper.tri(mat, diag = TRUE), arr.ind = TRUE)
# row col
#[1,] 1 1
#[2,] 1 3
#[3,] 2 3
OR
which(mat == 1 & lower.tri(mat, diag = TRUE), arr.ind = TRUE)
I am trying to come up with every possible combination of a set number of 1's and 0's in a list, without any overlap. I want the list to contain a series of matrices.
I have come up with the following code, which accomplishes that goal, for example if you want 2 values of 1 and 2 of 0 in a 2x2 matrix:
z<-0
for(i in 1:(4-1)){
for(j in (i+1):(4)){
x<-rep(0,4)
x[c(i,j)]<-1
x<-matrix(x,nrow=2,byrow=TRUE)
z<-z+1
k[[z]]<-x
}}
This is fine, but I would like to be able to create lists involving a larger number of 0's and 1's.
The only way I know how to do this is to nest more and more for loops
For example, in order to print every non-repetitive combination of 3 1's in 9 total spaces in a 3x3 matrix:
for(i in 1:(9-2)){
for(j in (i+1):(9-1)){
for(k in (j+1):9){
x<-rep(0,9)
x[c(i,j,k)]<-1
x<-matrix(x,nrow=3,byrow=TRUE)
print(x)
}}}
I feel like there must be a more elegant and quicker solution(especially when dealing with larger numbers of digits). Even if a simple solution could just give me vectors, it'd be easy enough to make them a list of matrices. I would like variable amounts of 1's and 0's enumerated in a list so that I can use them for further manipulation.
Thank you for your help!
Here's a function to do it. The first argument is the size of the sides of the matrix, the second is the number of ones wanted:
makematrix <- function(n, k){
z <- as.data.frame(t(expand.grid(rep(list(c(0,1)), n * n))))
z <- z[ ,colSums(z) == k]
lapply(z, function(x){matrix(x, nrow = n)})
}
First we make all the combinations of 0 and 1 in a data frame using expand.grid, subset by the ones with the correct number of 1s, then we rearrange them into a list of matrices using lapply.
makematrix(2, 2)
$V4
[,1] [,2]
[1,] 1 0
[2,] 1 0
$V6
[,1] [,2]
[1,] 1 1
[2,] 0 0
$V7
[,1] [,2]
[1,] 0 1
[2,] 1 0
$V10
[,1] [,2]
[1,] 1 0
[2,] 0 1
$V11
[,1] [,2]
[1,] 0 0
[2,] 1 1
$V13
[,1] [,2]
[1,] 0 1
[2,] 0 1
You can also directly use library combinat, which permn function giving a direct list:
library(combinat)
unique(permn(c(1,1,0,0),function(x) matrix(x,nrow=sqrt(length(x)))))
#[[1]]
# [,1] [,2]
#[1,] 1 0
#[2,] 1 0
#[[2]]
# [,1] [,2]
#[1,] 1 1
#[2,] 0 0
#[[3]]
# [,1] [,2]
#[1,] 0 1
#[2,] 1 0
#[[4]]
# [,1] [,2]
#[1,] 0 0
#[2,] 1 1
#[[5]]
# [,1] [,2]
#[1,] 1 0
#[2,] 0 1
#[[6]]
# [,1] [,2]
#[1,] 0 1
#[2,] 0 1
I'm aware this might be a VERY easy question, but i'm new at R and have been trying to find this for hours now, with no luck...
I want to apply a transformation on each element of the first column of the following matrix only if the value of the second column is 1...
> a<-matrix(c(30,40,50,60,1,0,1,0),nrow=4,ncol=2)
> a
[,1] [,2]
[1,] 30 1
[2,] 40 0
[3,] 50 1
[4,] 60 0
>
So this would yield (assuming my function would multiply by -1 the value in column 1):
> a.transformed
[,1] [,2]
[1,] -30 1
[2,] 40 0
[3,] -50 1
[4,] 60 0
I think this would imply the apply() function but I can't seem to get the if() to work! Help!!
You can use the [ operator to do logical comparison, and then the [<- operator to subset and replace relevant values...
# rows we want to change
id <- a[,2] == 1
a[id , 1 ] <- a[ id , 1 ] * -1
a
# [,1] [,2]
#[1,] -30 1
#[2,] 40 0
#[3,] -50 1
#[4,] 60 0
I suggest reading an introductory text as this is some rather fundamental operations in R.
Using ifelse
a[,1]<-ifelse(a[,2]==1,a[,1]*-1,a[,1])
> a
[,1] [,2]
[1,] -30 1
[2,] 40 0
[3,] -50 1
[4,] 60 0
This question already has answers here:
How to replace non-diagonal elements in a matrix?
(6 answers)
Closed 9 years ago.
Okay, I asked this question earlier but I got bashed (deservedly) for not specifying anything and showing no sign of previous attempt. So let me try again..
I'm using R, and I have a 463✕463 matrix. What I would like to do is to replace all elements other than the diagonal ones (X11, X22, X33,...,Xjj) with zero.
E.g. I want:
[1 4 5
2 3 5
3 9 8]
to be:
[1 0 0
0 3 0
0 0 8]
When I use the diag() function, it simply gives me a column vector of the diagonal values. I imagine I can use the replace() function somehow combined with a "if not diagonal" logic...but I am lost.
And yes, as some here have guessed, I am probably much younger than many people here and am completely new at this...so please put me in the right direction. Really appreciate all your help!
In R, the diag method has two functions.
It returns the diagonal of a matrix. I.e.
m <- matrix(1:9, ncol=3)
m
# [,1] [,2] [,3]
# [1,] 1 4 7
# [2,] 2 5 8
# [3,] 3 6 9
diag(m)
# [1] 1 5 9
It can construct a diagonal matrix.
diag(1:3)
# [,1] [,2] [,3]
# [1,] 1 0 0
# [2,] 0 2 0
# [3,] 0 0 3
So in your case, extract the diagonal from your existing matrix and supply it to diag:
diag(diag(m))
# [,1] [,2] [,3]
# [1,] 1 0 0
# [2,] 0 5 0
# [3,] 0 0 9
using outer
You can use the following to compute a logical matrix which describes the non-diagonal entries of a n×n matrix:
outer(1:n, 1:n, function(i,j) i!=j)
Applied to your example:
> m <- matrix(c(1,2,3,4,3,9,5,5,8),ncol=3)
> m
[,1] [,2] [,3]
[1,] 1 4 5
[2,] 2 3 5
[3,] 3 9 8
> m[outer(1:3, 1:3, function(i,j) i!=j)] <- 0
> m
[,1] [,2] [,3]
[1,] 1 0 0
[2,] 0 3 0
[3,] 0 0 8
using triangles
A possible alternative would be combining the two triangles on either side of the diagonal. In this case, you use the matrix m itself as input to determine the size.
upper.tri(m) | lower.tri(m)
Applied to your use case:
> m[upper.tri(m) | lower.tri(m)] <- 0
It seems you already got this answer in response to your original post…
m[ col(m)==row(m) ] <- 0
> m <- matrix(1:9, 3)
> m[ col(m)==row(m) ]
[1] 1 5 9
> m[ col(m)!=row(m) ] <- 0
> m
[,1] [,2] [,3]
[1,] 1 0 0
[2,] 0 5 0
[3,] 0 0 9