replace a vector to an upper Triangle matrix with different length - r

I want to have a triangle matrix by a vector when length of vector is less than replacement length.
for example:
v<- c(1,2,3,4,5,6)
and
mat<- matrix(0,5,5).
If I use
mat[upper.tri(mat, diag=FALSE)]<- v
,the result is:
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 2 4 1
[2,] 0 0 3 5 2
[3,] 0 0 0 6 3
[4,] 0 0 0 0 4
[5,] 0 0 0 0 0
But i don't want to replace more than length of vector in matrix. And i want to have:
[1,] 0 1 2 4 0
[2,] 0 0 3 5 0
[3,] 0 0 0 6 0
[4,] 0 0 0 0 0
[5,] 0 0 0 0 0

You could adjust the length of v to that of the upper triangle. This yields some NA values that you can replace with zeroes.
u.tri <- upper.tri(mat, diag=FALSE)
mat[u.tri] <- `length<-`(v, length(u.tri))
mat[is.na(mat)] <- 0
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 1 2 4 0
# [2,] 0 0 3 5 0
# [3,] 0 0 0 6 0
# [4,] 0 0 0 0 0
# [5,] 0 0 0 0 0

Related

use of the [<- operator to modify a line of data

I have some data
data <- diag(5)
I want to use the [<- operator to change a line.
The result should be:
data[1,] <- 2
> data
[,1] [,2] [,3] [,4] [,5]
[1,] 2 2 2 2 2
[2,] 0 1 0 0 0
[3,] 0 0 1 0 0
[4,] 0 0 0 1 0
[5,] 0 0 0 0 1
I know I can do e.g.
`[<-`(data, i=1, j=3, 2)
which gives
[,1] [,2] [,3] [,4] [,5]
[1,] 1 0 8 0 0
[2,] 0 1 0 0 0
[3,] 0 0 1 0 0
[4,] 0 0 0 1 0
[5,] 0 0 0 0 1
but how can I operate on line (or column, same issue)?
I tried j=NULL, j=integer(0), it doesn't work. I could do j=1:5 and get what I want but I am wondering how to mimic data[1,] <- 2 and not data[1,1:5] <- 2.
> `[<-`(data, 1, , 2) # blank 2nd argument
[,1] [,2] [,3] [,4] [,5]
[1,] 2 2 2 2 2
[2,] 0 1 0 0 0
[3,] 0 0 1 0 0
[4,] 0 0 0 1 0
[5,] 0 0 0 0 1
You can use ncol to ensure that all columns are set:
`[<-`(data, i = 1, j = 1:ncol(data), 2)

R which function displays wrong row numbers

I am trying to save the row number for values equal to one, for every column seperatly in a matrix (matx). The matrix should contain 0's for every other object. It somehow worked to give me numbers which are just a little bit smaller (1 value smaller in the beginning, two and three later on), but not the right values. The original matrix has just values of 0 and 1.
My try:
matx<-replicate(n=100,rbinom(n= 250, size=1, prob = 0.01))
maty<-apply(!matx, 2, function(x) ifelse(x==0,
which(x %in% 1),
x==0))
also tried:
maty<-apply(!matx, 2, function(x) ifelse(x>0, as.integer(rownames(matx)), 0))
The second attempt just leaves me with NA's and 0's instead of the row number.
Assuming that #akrun's interpretation is correct (it's also how I read the question) you can also use the row function:
matx * row(matx)
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 1 0 0 0
# [2,] 0 0 2 0 0
# [3,] 3 3 3 0 0
# [4,] 4 4 0 0 0
# [5,] 5 0 0 5 0
# [6,] 6 6 6 0 0
# [7,] 0 0 0 7 0
# [8,] 8 0 8 8 0
# [9,] 9 9 9 9 0
# [10,] 0 0 0 10 0
If we need to replace the '1s' (in the binary matrix) with the corresponding row numbers leaving the '0s' as such, we can can which with arr.ind=TRUE to get the row/column index of non-zero numbers, use that index to replace the 1s with the row index column from 'ind'. Here, I created a copy of 'matx' (ie. 'maty') in case the original matrix is needed.
maty <- matx
ind <- which(matx!=0, arr.ind=TRUE)
maty[ind] <- ind[,1]
maty
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0 1 0 0 0
#[2,] 0 0 2 0 0
#[3,] 3 3 3 0 0
#[4,] 4 4 0 0 0
#[5,] 5 0 0 5 0
#[6,] 6 6 6 0 0
#[7,] 0 0 0 7 0
#[8,] 8 0 8 8 0
#[9,] 9 9 9 9 0
#[10,] 0 0 0 10 0
and original matrix
matx
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0 1 0 0 0
#[2,] 0 0 1 0 0
#[3,] 1 1 1 0 0
#[4,] 1 1 0 0 0
#[5,] 1 0 0 1 0
#[6,] 1 1 1 0 0
#[7,] 0 0 0 1 0
#[8,] 1 0 1 1 0
#[9,] 1 1 1 1 0
#[10,] 0 0 0 1 0
NOTE: This could be also used for non-numeric elements
Or a base R modification of apply solution in #eipi's post would be
apply(matx, 2,function(x) ifelse(x!=0, seq_along(x), 0) )
data
set.seed(24)
matx <- matrix(sample(0:1, 10*5, replace=TRUE), nrow=10)
If your original matx is purely 0s and 1s then this show work:
maty <- matx * row(matx)
an example:
> matx # stealing from akrun
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 0 0 0
[2,] 0 0 1 0 0
[3,] 1 1 1 0 0
[4,] 1 1 0 0 0
[5,] 1 0 0 1 0
[6,] 1 1 1 0 0
[7,] 0 0 0 1 0
[8,] 1 0 1 1 0
[9,] 1 1 1 1 0
[10,] 0 0 0 1 0
> matx * row(matx)
[,1] [,2] [,3] [,4] [,5]
[1,] 0 1 0 0 0
[2,] 0 0 2 0 0
[3,] 3 3 3 0 0
[4,] 4 4 0 0 0
[5,] 5 0 0 5 0
[6,] 6 6 6 0 0
[7,] 0 0 0 7 0
[8,] 8 0 8 8 0
[9,] 9 9 9 9 0
[10,] 0 0 0 10 0
Here's a way to do it using apply:
library(zoo) # For index function
apply(matx, 2, function(x) ifelse(x==1, index(x), 0))

Generate large matrix filled with 0's or 1's in R

In R language, I am trying to generate a large matrix filled with 0's and 1's.
I have generated a large matrix but its filled with values between 0 and 1.
Here is how I did that:
NCols=500
NRows=700
mr<-matrix(runif(NCols*NRows), ncol=NCols)
I think you are asking how to generate a matrix with just zero and 1
Here is how I would do it
onezero <- function(nrow,ncol)
matrix(sample(c(0,1), replace=T, size=nrow*ncol), nrow=nrow)
With nrow and ncol the rows and columns of the matrix
R> onezero(5,5)
[,1] [,2] [,3] [,4] [,5]
[1,] 1 1 0 1 0
[2,] 1 1 1 1 0
[3,] 1 1 0 0 0
[4,] 1 0 0 1 0
[5,] 0 0 0 0 0
You can use rbinomtoo. And can change the probability of success on each trial. In this case, it's .5.
nrow<-700
ncol<-500
mat01 <- matrix(rbinom(nrow*ncol,1,.5),nrow,ncol)
> number.of.columns = 5
> number.of.rows = 10
> matrix.size = number.of.columns*number.of.rows
> ones.and.zeros.samples = sample(0:1, matrix.size, replace=TRUE)
> A = matrix(ones.and.zeros.samples, number.of.rows)
> A
[,1] [,2] [,3] [,4] [,5]
[1,] 0 0 0 1 1
[2,] 1 0 0 0 1
[3,] 0 1 1 0 0
[4,] 0 0 1 1 1
[5,] 1 0 1 1 0
[6,] 0 1 0 1 1
[7,] 0 0 1 1 0
[8,] 0 1 0 0 0
[9,] 0 0 0 0 0
[10,] 0 0 0 1 1

Convert a string into a similarity matrix

I have number of strings in an idiosyncratic format, representing sets. In R, I'd like to convert them into a similarity matrix.
For example, a string showing that 1+2 comprise a set, 3 is alone in a set, and 4,5, and 6 comprise a set is:
"1+2,3,4+5+6"
For the example above, I'd like to be able to produce
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 0 0 0 0
[2,] 1 1 0 0 0 0
[3,] 0 0 1 0 0 0
[4,] 0 0 0 1 1 1
[5,] 0 0 0 1 1 1
[6,] 0 0 0 1 1 1
It seems like this should be a painfully simple task. How would I go about it?
Here's an approach:
out <- lapply(unlist(strsplit("1+2,3,4+5+6", ",")), function(x) {
as.numeric(unlist(strsplit(x, "\\+")))
})
x <- table(unlist(out), rep(seq_along(out), sapply(out, length)))
matrix(x %*% t(x), nrow(x))
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 1 1 0 0 0 0
## [2,] 1 1 0 0 0 0
## [3,] 0 0 1 0 0 0
## [4,] 0 0 0 1 1 1
## [5,] 0 0 0 1 1 1
## [6,] 0 0 0 1 1 1
Pseudocode:
Split at , to get an array of strings, each describing a set.
For each element of the array:
Split at + to get an array of set members
Mark every possible pairing of members of this set on the matrix
You can create a matrix in R with:
m = mat.or.vec(6, 6)
By default, the matrix should initialize with all entries 0. You can assign new values with:
m[2,3] = 1
Here's another approach:
# write a simple function
similarity <- function(string){
sets <- gsub("\\+", ":", strsplit(string, ",")[[1]])
n <- as.numeric(tail(strsplit(gsub("[[:punct:]]", "", string), "")[[1]], 1))
mat <- mat.or.vec(n, n)
ind <- suppressWarnings(lapply(sets, function(x) eval(parse(text=x))))
for(i in 1:length(ind)){
mat[ind[[i]], ind[[i]]] <- 1
}
return(mat)
}
# Use that function
> similarity("1+2,3,4+5+6")
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 0 0 0 0
[2,] 1 1 0 0 0 0
[3,] 0 0 1 0 0 0
[4,] 0 0 0 1 1 1
[5,] 0 0 0 1 1 1
[6,] 0 0 0 1 1 1
# Using other string
> similarity("1+2,3,5+6+7, 8")
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,] 1 1 0 0 0 0 0 0
[2,] 1 1 0 0 0 0 0 0
[3,] 0 0 1 0 0 0 0 0
[4,] 0 0 0 0 0 0 0 0
[5,] 0 0 0 0 1 1 1 0
[6,] 0 0 0 0 1 1 1 0
[7,] 0 0 0 0 1 1 1 0
[8,] 0 0 0 0 0 0 0 1

How can I create a matrix as shown below in R

I am trying to create a matrix from a given vector in R, but I don't know how to achieve it in simple ways. I am giving an example below. The matrix was made using the "cbind" function.
Given x as
[1,] 1
[2,] 3
[3,] 4
how can I create the matrix below with simple method?
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 0 0 0 0 0
[2,] 3 1 0 0 0 0
[3,] 4 3 1 0 0 0
[4,] 0 4 3 1 0 0
[5,] 0 0 4 3 1 0
[6,] 0 0 0 4 3 1
[7,] 0 0 0 0 4 3
[8,] 0 0 0 0 0 4
Thank you for your help!
Using append and sapply
sapply(0:5, append, x = rep(0,5), values = c(1,3,4))
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 1 0 0 0 0 0
# [2,] 3 1 0 0 0 0
# [3,] 4 3 1 0 0 0
# [4,] 0 4 3 1 0 0
# [5,] 0 0 4 3 1 0
# [6,] 0 0 0 4 3 1
# [7,] 0 0 0 0 4 3
# [8,] 0 0 0 0 0 4
matrix(c(1,3,4,rep(0,6)),ncol=6,nrow=8)
You'll get a warning, but the correct matrix. If you don't like the warning just use suppressWarnings:
suppressWarnings(matrix(c(1,3,4,rep(0,6)),ncol=6,nrow=8))
Of course, be careful with that function if you are trying to abstract this to more general cases.
This method is very intuitive:
x <- c(1,3,4)
n <- 6
m <- matrix(0,ncol=n,nrow=n+length(x)-1)
diag(m) <- 1
diag(m[-1,]) <- 3
diag(m[-c(1, 2),]) <- 4
Assigning along diagonals can be automated easily, for example with a 'for' loop
for(i in seq_along(x)) diag(m[1:n + i - 1,]) <- x[i]
Both approaches yield:
R> m
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 0 0 0 0 0
[2,] 3 1 0 0 0 0
[3,] 4 3 1 0 0 0
[4,] 0 4 3 1 0 0
[5,] 0 0 4 3 1 0
[6,] 0 0 0 4 3 1
[7,] 0 0 0 0 4 3
[8,] 0 0 0 0 0 4
This works. (edit: no votes, maybe too telegraphic?) The idea is to create an all-zero matrix with the appropriate dimensions, and then use row/column arithmetic (using the row() and col() functions) to fill in the desired values in the elements where row-column is between 0 and 2 (i.e. the diagonal and the first two lower off-diagonals). This does rely on the column-major structure of matrices in R ...
x <- c(1,3,4)
n <- 6
m <- matrix(0,ncol=n,nrow=n+length(x)-1)
betw <- function(x,a,b) x>=a & x<= b
m[betw(row(m)-col(m),0,2)] <- x

Resources