Multiply matrix by each sublist - r

I am multplying a matrix tm by a vector tb to produce a "response" vector. I need to apply this to a list of n tb vectors, which would produce a list containing n response vectors. I am struggling to get this to iterate over the list, for a single case it is this:
set.seed(19)
n <- 10
k <- 4
tb <- list(split(rnorm(n*k, 0, 1),seq(1:n)))
tm <- matrix(c(1.0, 0.1, 0.2, 0.3, 0.1, 1.0, 0.2, 0.1, 0.2, 0.2, 1.0, 0.5, 0.3, 0.1, 0.5, 1.0), ncol = k)
tm %*% as.vector(unlist(tb[[1]][1]))
Which produces the first response vector when doing this calculation in isolation:
> tm %*% as.vector(unlist(tb[[1]][1]))
[,1]
[1,] -0.4014836
[2,] 0.8348435
[3,] 2.0416294
[4,] 1.9114801
However, I've tried to get all 10 response vectors using lapply/sapply but this gives me an unexpected output:
> sapply(tm, function(x) x %*% as.vector(unlist(tb)))
[,1] [,2] [,3] [,4] [,5]
[1,] -1.189453745 -0.1189453745 -0.2378907491 -0.3568361236 -0.1189453745
[2,] 0.518629988 0.0518629988 0.1037259975 0.1555889963 0.0518629988
[3,] 1.423423.. ... ... ...
Just showing a snippet of the output here, it's 16 columns and 40 rows, in other words - one column per element of the matrix, and n x k rows. It's seemingly taking the first cell of the matrix, and doing the calculation, then the second cell, and the third cell and so on - as you can see this matches the output from sapply when I take a single element of tm:
> tm[1] %*% as.vector(unlist(tb[[1]][1]))
[,1] [,2] [,3] [,4]
[1,] -1.189454 0.51863 1.423423 1.504741
My question is, how do I get this multiplication to take the whole matrix when using lapply/sapply as it does when I do it in isolation?

I think you just need to remove the list() function from your tb definition:
set.seed(19)
n <- 10
k <- 4
tb <- split(rnorm(n*k, 0, 1),seq(1:n))
tm <- matrix(c(1.0, 0.1, 0.2, 0.3, 0.1, 1.0, 0.2, 0.1, 0.2, 0.2, 1.0, 0.5, 0.3, 0.1, 0.5, 1.0), ncol = k)
you can then produce your first response vector simpler:
tm %*% tb[[1]]
[,1]
[1,] -0.4014836
[2,] 0.8348435
[3,] 2.0416294
[4,] 1.9114801
and all the response vectors with sapply:
sapply(tb, function(x) x %*%tm )
1 2 3 4 5 6 7 8 9 10
[1,] -0.4014836 0.1513720 -0.1113092 -0.28636882 1.1300914 -0.7037464 1.5886556 -0.8908194 -0.6891749 -0.4927336
[2,] 0.8348435 0.6747836 0.6135654 -0.01236765 0.6523212 -0.3599526 -0.2293118 1.5190890 0.1165567 -0.7644372
[3,] 2.0416294 -0.9832891 0.3399474 1.04671293 -0.1986427 -0.4779628 1.3585457 1.0673985 -1.7597788 -0.4059126
[4,] 1.9114801 -0.7064887 0.5356257 0.57154412 0.8048432 -1.6563305 2.9935210 -1.3916476 -1.3746462 -0.9662248

Related

R Loop random normal distribution

I'm trying to achieve the following: I want to generate 7 values from a normal distribution. Then I want to take these values, and using them as a mean generate 3 more (for each initial value) values from a normal distribution to replace them. I'd like to write this in a loop.
Let's use sd = 1.5 and sd = 0.7, and start with a mean of 0.
set.seed(1234)
mu.mat<-rnorm(7,mean=0,sd=1.5)
Gives me 7 nice values.
Then I want to create a number num [1:21] that generates 3 norm. distr. values using mean = first value of the just created list with sd = 0.7, three more using the second value and so on.
Of the form:
rnorm(3,mean=mu.mat[1],sd=0.7)
Just for all entries in a loop.
What I've tried:
mu.mat2<-NULL
for(i in 1:7) {
mu.mat2[i]<-rnorm(3,mean=mu.mat[i],sd=0.7)
}
Results in error: no. of items to replace is not a multiple of replacement length.
Any help on how to put this into a loop is very appreciated. Thanks in advance!
You don't need a loop. You can do:
rnorm(21, mean = rep(mu.mat, each = 3), sd = 0.7)
#> [1] -0.4811184 -1.2327778 -1.8603816 -3.3073277 -2.5190560 -3.2298056
#> [7] -2.3695570 -2.0228732 -1.1692489 2.0342910 1.0186855 1.0838678
#> [13] 0.5486730 -0.2439510 -0.1831147 2.2026024 0.1925301 -0.2153864
#> [19] 2.8944894 1.9213206 1.3804706
But the problem with your code is that you are trying to write three values (rnorm(3,mean=mu.mat[i],sd=0.7)) into a single atomic index mu.mat2[i]. It's not clear whether you were expecting a matrix as a result, but if so your loop would be:
mu.mat2 <- matrix(ncol = 3, nrow = 7)
for(i in 1:7) {
mu.mat2[i,] <- rnorm(3, mean = mu.mat[i], sd = 0.7)
}
If you were wanting the result as a 7 x 3 matrix, you can do:
matrix(rnorm(21, mean = rep(mu.mat, each = 3), sd = 0.7), ncol = 3, byrow = TRUE)
#> [,1] [,2] [,3]
#> [1,] -0.96624036 -1.4808460 -2.6824842
#> [2,] -2.88942108 -1.7299094 -3.0446737
#> [3,] -2.82034688 -0.9570087 -2.1822797
#> [4,] 0.58997289 1.0384926 1.8111506
#> [5,] -0.07705959 -0.1024418 0.7249310
#> [6,] 0.48851487 1.4729882 0.6496858
#> [7,] 1.47961292 1.5653253 2.0629409
Try replicate like below
> replicate(3,rnorm(length(mu.mat),mu.mat,0.7))
[,1] [,2] [,3]
[1,] -2.19324092 -1.13895278 -2.1540788
[2,] 0.02102746 0.33894402 0.1077604
[3,] 1.00363528 1.26895511 1.9483744
[4,] -3.85258144 -4.15638335 -4.0041507
[5,] -0.05518348 0.05766686 -0.3700564
[6,] 0.21570611 2.45016846 1.1614128
[7,] -0.81698877 -0.76824819 -1.5786689

Iterating over a matrix and a list of times to plug into nls function in R

I have spent a fair amount of time searching for an answer to my novice question and am still confused. I am trying to plot initial magnetization of an FID versus time. My initial magnetizations are in a matrix and my time values corresponding to each column of the matrix is a list. How do I run the nls for a exponential decay over each column of data with the corresponding value in the list of times? I am trying to have the nls function input the first time value from the list and run use the initial magnetization values columnwise and return the rates in a matrix of the same dimensions as m0_matrix.
> m0_matrix
[,1] [,2] [,3] [,4]
[1,] 19439311560 15064186946 11602185622 9009147617
[2,] 9437620734 7135488585 5348160563 4156154903
[3,] 11931439242 9584153017 7765094983 6470870180
[4,] 9367920785 7612552829 5927424214 4331819248
[5,] 12077347835 8892705185 6866664357 5530601653
[6,] 20191716524 15729555553 11920147205 8964406945
[7,] 20177137879 15744074858 12364404080 9971845743
[8,] 15990100401 12464163359 9724743390 8294038306
[9,] 19409862926 16085027074 13110425604 10330007806
[10,] 15367044986 11994945813 9565243969 7535061239
r2_from_decay_matrix = matrix(data = NA, nrow = nrow(m0_matrix), ncol =
ncol(m0_matrix))
t <- c(0.1, 0.2, 0.3, 0.4)
for (i in seq(1,nrow(m0_matrix))) {
m0 <- m0_matrix[,i]
t <- t[i]
r <- 1
mCPMG_function <- function(m0, t)
results <- paste(a = m0, b = t)
mCPMG_formula <- mCPMG ~ m0*exp(-r*t)
fit_start <- c(m0= 19439311560, r=1)
fit_data <- list(m0=m0, t=t)
r2 <- nls(mCPMG_formula, fit_data, fit_start)
r2_from_decay_matrix <- r2$m$getPars()["r"][i]
}
Thank you for helping!

Conversion between covariance matrix and correlation matrix

I have a correlation matrix
cor.mat <- structure(c(1, -0.25, 0.11, 0.25, 0.18, -0.25, 1, -0.14, -0.22,
-0.15, 0.11, -0.14, 1, 0.21, 0.19, 0.25, -0.22, 0.21, 1, 0.53,
0.18, -0.15, 0.19, 0.53, 1), .Dim = c(5L, 5L))
I also have a matrix of standard errors
sd <- structure(c(0.33, 0.62, 1, 0.54, 0.47), .Dim = c(1L, 5L))
dim(cor.mat)
#[1] 5 5
dim(sd)
#[1] 1 5
is.matrix(cor.mat)
#[1] TRUE
is.matrix(sd)
#[1] TRUE
cov.mat <-cor2cov(cor.mat, sd)
# Error in sds * R : non-conformable arrays
So, the matrices have compatible dimensions, why doesn't cor2cov function work for me?
OK, I don't know where your cor2cov comes from. But actually, it is really straightforward to obtain covariance matrix from correlation matrix and standard errors:
cov.mat <- sweep(sweep(cor.mat, 1L, sd, "*"), 2L, sd, "*")
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.108900 -0.051150 0.0363 0.044550 0.027918
#[2,] -0.051150 0.384400 -0.0868 -0.073656 -0.043710
#[3,] 0.036300 -0.086800 1.0000 0.113400 0.089300
#[4,] 0.044550 -0.073656 0.1134 0.291600 0.134514
#[5,] 0.027918 -0.043710 0.0893 0.134514 0.220900
Yes, it is just a symmetric row & column rescaling.
We can verify this by transforming this covariance matrix back to correlation matrix using cov2cor, which is exactly your correlation matrix:
all.equal(cov2cor(cov.mat), cor.mat)
# [1] TRUE
My guess on your cor2cov
If you read How to rescale a matrix by row / column, you will see there are lots of different ways for rescaling. The sweep used above is just one option.
R base function cov2cor(V) is using:
Is <- sqrt(1/diag(V)) ## inverse of square root diagonal (inverse of sd)
Is * V * rep(Is, each = p)
I think your cor2cov(R, sds) is written in the same style:
sds * R * rep(sds, each = p) ## `sd` must be a vector
If so, sd must be a vector, otherwise "*" will complain (note, the error message you got is indeed reported from "*").
Your argument "the matrices have compatible dimensions" is a bogus one. Purely in terms of linear algebra, you need sd to be a diagonal matrix, so that you can do:
sd %*% cor.mat %*% sd
But row / column rescaling is never done by matrix computations as this is too expensive.
By definition of correlation and covariance matrix, you can simply do this:
cov.mat <- cor.mat * matrix(outer(sd, sd), nrow=5, byrow=TRUE)
cov.mat
[,1] [,2] [,3] [,4] [,5]
[1,] 0.108900 -0.051150 0.0363 0.044550 0.027918
[2,] -0.051150 0.384400 -0.0868 -0.073656 -0.043710
[3,] 0.036300 -0.086800 1.0000 0.113400 0.089300
[4,] 0.044550 -0.073656 0.1134 0.291600 0.134514
[5,] 0.027918 -0.043710 0.0893 0.134514 0.220900
I think I might have found an answer on another post: Non-conformable arrays error in code
When I treat sd matrix as vertor, it works (I hope, it's correct?)
sd = as.vector(sd)
cov.mat <- cor2cov(cor.mat, sd)
Thank you and please let me know if this operation makes the results not equivalent to what I was initially asking about.

r easy nxn covariance matrix creation with given variances and covariances

For a simulation study I need to create nxn covariance matrices. for example I can input 2x2 covariance matrices like
[,1] [,2]
[1,] 1.0 1.5
[2,] 1.5 2.0
into a r function/object:
var <- c(1,2) ## variances
covar <- c(1.5,1.5) ## covariance(s)
mat <- matrix(c(var[1],covar[1],covar[2],var[2]),ncol=length(var))
then I only have to change var & covar values to form the matrices. but unfortunately I'm not just dealing with 2x2s but 2x2:30x30 or even higher! so is it possible to write only one function for any matrix of nxn dimension in r?
You can do:
m <- diag(variance)
m[lower.tri(m)] = m[upper.tri(m)] <- head(covar, length(covar)/2)
For example:
variance = c(0.25, 0.75, 0.6)
covar = c(0.1, 0.3, 0.2, 0.1, 0.3, 0.2)
#>m
# [,1] [,2] [,3]
#[1,] 0.25 0.10 0.3
#[2,] 0.10 0.75 0.2
#[3,] 0.30 0.20 0.6

Easily input a correlation matrix in R

I have a R script I'm running now that is currently using 3 correlated variables. I'd like to add a 4th, and am wondering if there's a simple way to input matrix data, particularly for correlation matrices---some Matlab-like technique to enter a correlation matrix, 3x3 or 4x4, in R without the linear to matrix reshape I've been using.
In Matlab, you can use the semicolon as an end-row delimiter, so it's easy to keep track of where the cross correlations are.
In R, where I first create
corr <- c(1, 0.1, 0.5,
0.1, 1, 0.9,
0.5, 0.9, 1)
cormat <- matrix(corr, ncol=3)
Versus
cormat = [1 0.1 0.5;
0.1 1 0.9;
0.5 0.9 1]
It just feels clunkier, which makes me suspect there's a smarter way I haven't looked up yet. Thoughts?
Welcome to the site! :) you should be able to do it in one step:
MyMatrix = matrix(
c(1, 0.1, 0.5,
0.1, 1, 0.9,
0.5, 0.9, 1),
nrow=3,
ncol=3)
Here is another way:
CorrMat <- matrix(scan(),3,3,byrow=TRUE)
1 0.1 0.5
0.1 1 0.9
0.5 0.9 1
Trailing white line is important.
If you want to input a symmetric matrix, you can use the xpnd() function in the MCMCpack library.
xpnd() takes a vector which corresponds to the upper-triangle of the matrix (thus you only have to enter each value once). For instance, if you want to input:
$\left(\begin{array}{c c c}
1 & 0.1 & 0.5 \\
0.1 & 1 & 0.9 \\
0.5 & 0.9 & 1
\end{array}\right)$
You would use
library(MCMCpack)
xpnd(c(1, 0.1, 0.5, 1, 0.9, 1), 3)
where 3 refers to the number of rows in the matrix.
Help page for xpnd.
rbind(c(1, 0.1, 0.5),
c(0.1, 1, 0.9),
c(0.5, 0.9, 1))
For the existing solutions. That may only work for 3*3 matrix. I tried this one.
a<-diag(3)
m<-diag(3)
m[lower.tri(m,diag=F)]<-c(0.1, 0.5, 0.9)
m<-m+t(m)-a
As you are working with correlation matrices, you are probably not interested in entering the diagonal, and both the upper and lower parts. You can manipulate/extract those three parts separately using diag(), upper.tri() and lower.tri().
> M <- diag(3) # create 3x3 matrix, diagonal defaults to 1's
> M[lower.tri(M, diag=F)] <- c(0.1, 0.5, 0.9) # read in lower part
> M # lower matrix containing all information
[,1] [,2] [,3]
[1,] 1.0 0.0 0
[2,] 0.1 1.0 0
[3,] 0.5 0.9 1
If you want the full matrix:
> M[upper.tri(M, diag=F)] <- M[lower.tri(M)] # fill upper part
> M # full matrix
[,1] [,2] [,3]
[1,] 1.0 0.1 0.5
[2,] 0.1 1.0 0.9
[3,] 0.5 0.9 1.0

Resources