How to edit elements of a 2xn matrix along the rows - r

I am having some issues working a forloop which allows me to take the following matrix:
> cd
[,1] [,2]
[1,] -142.5066 -132.9431
[2,] -161.6038 -166.9276
and renaming the elements along the columns. Specifically, I want do the following:
if cd[1,1] > cd[1,2] , then I want cd[1,2] == 'STOP'
else cd[1,2]==cd[1,2]
my code right now for a forloop for K rows is:
for(k in 1:2){
if(cd[1,k]>cd[2,k]){
cd[2,k]<-'STOP'
}else{
cd[2,k]<-cd[2,k]
}
print(cd)
}
The output is the following:
[,1] [,2]
[1,] "-142.50660967154" "-132.943085827163"
[2,] "STOP" "-166.92760911847"
[,1] [,2]
[1,] "-142.50660967154" "-132.943085827163"
[2,] "STOP" "-166.92760911847"
Essentially, after running the loop, I want the result to be:
> cd
[,1] [,2]
[1,] -142.5066 -132.9431
[2,] STOP STOP
Thank you again.

When you have a matrix in r, all the elements have to be the same type. At first, you have all numeric elements, so the first comparison works, but when it makes it "stop", then it makes them all character, and "-3" is less than "-4", characterwise. Your options are to use a dataframe instead of a matrix, or use as.numeric(cd[1,k])...
> cd <- matrix(c(-1,-2,-3,-4), nrow = 2)
> for(k in 1:2){
+ if(as.numeric(cd[1,k])>as.numeric(cd[2,k])){
+ cd[2,k]<-'STOP'
+ } else{cd[2,k]<-cd[2,k]}
+ print(cd)
+ }
[,1] [,2]
[1,] "-1" "-3"
[2,] "STOP" "-4"
[,1] [,2]
[1,] "-1" "-3"
[2,] "STOP" "STOP"

With a matrix there can be no conflicting data types in a column so the numeric values are coerced to character. If you want separate types, use a data.frame:
cd <- matrix(c(-142.5066, -132.9431, -161.6038, -166.9276), nrow = 2, byrow = TRUE)
cd[2, ] <- ifelse(cd[1,] > cd[2, ], "STOP", cd[2,])
cd
[,1] [,2]
[1,] "-142.5066" "-132.9431"
[2,] "STOP" "STOP"

Instead of using a loop, you could try the following:
cd[cd[,1] > cd[,2]] <- 'STOP'
[,1] [,2]
cd "-142.5066" "-132.9431"
"STOP" "STOP"

Related

Efficiently finding minimum cells values from a set of matrices in R

I have a list of matrices (size n*n), and I need to create a new matrix giving the minimum value observed for each cell, based on my list.
For instance, with the following matrices list:
> a = list(matrix(rexp(9), 3), matrix(rexp(9), 3), matrix(rexp(9), 3))
> a
[[1]]
[,1] [,2] [,3]
[1,] 0.5220069 0.39643016 0.04255687
[2,] 0.4464044 0.66029350 0.34116609
[3,] 2.2495949 0.01705576 0.08861866
[[2]]
[,1] [,2] [,3]
[1,] 0.3823704 0.271399 0.7388449
[2,] 0.1227819 1.160775 1.2131681
[3,] 0.1914548 1.004209 0.7628437
[[3]]
[,1] [,2] [,3]
[1,] 0.2125612 0.45379057 1.5987420
[2,] 0.3242311 0.02736743 0.4372894
[3,] 0.6634098 1.15401347 0.9008529
The output should be:
[,1] [,2] [,3]
[1,] 0.2125612 0.271399 0.04255687
[2,] 0.1227819 0.02736743 0.34116609
[3,] 0.1914548 0.01705576 0.08861866
I tried using apply loop with the following code (using melt and dcast from reshape2 library):
library(reshape2)
all = melt(a)
allComps = unique(all[,c(1:2)])
allComps$min=apply(allComps, 1, function(x){
g1 = x[1]
g2 = x[2]
b = unlist(lapply(a, function(y){
return(y[g1,g2])
}))
return(b[which(b==min(b))])
})
dcast(allComps, Var1~Var2)
It works but it is taking a very long time to run when applied on large matrices (6000*6000). I am looking for a faster way to do this.
Use Reduce with pmin :
Reduce(pmin, a)
# [,1] [,2] [,3]
#[1,] 0.02915345 0.03157736 0.3142273
#[2,] 0.57661027 0.05621098 0.1452668
#[3,] 0.48021473 0.18828404 0.4787604
data
set.seed(123)
a = list(matrix(rexp(9), 3), matrix(rexp(9), 3), matrix(rexp(9), 3))
Maybe it should be considered to store the matrices in an array instead of a list. This can be done with simplify2array. In an array the minimum over specific dimensions can be found using min in apply.
A <- simplify2array(a)
apply(A, 1:2, min)
We can use
apply(array(unlist(a), c(3, 3, 3)), 1:2, min)

Paste together two data tables in R [duplicate]

I want to paste cells of matrix together, But when I do paste(),It returns a vector. Is there a direct function for same in R?
mat <- matrix(1:4,2,2)
paste(mat,mat,sep=",")
I want the output as
[,1] [,2]
[1,] 1,1 2,2
[2,] 3,3 4,4
A matrix in R is just a vector with an attribute specifying the dimensions. When you paste them together you are simply losing the dimension attribute.
So,
matrix(paste(mat,mat,sep=","),2,2)
Or, e.g.
mat1 <- paste(mat,mat,sep=",")
> mat1
[1] "1,1" "2,2" "3,3" "4,4"
> dim(mat1) <- c(2,2)
> mat1
[,1] [,2]
[1,] "1,1" "3,3"
[2,] "2,2" "4,4"
Here's just one example of how you might write a simple function to do this:
paste_matrix <- function(...,sep = " ",collapse = NULL){
n <- max(sapply(list(...),nrow))
p <- max(sapply(list(...),ncol))
matrix(paste(...,sep = sep,collapse = collapse),n,p)
}
...but the specific function you want will depend on how you want it to handle more than two matrices, matrices of different dimensions or possibly inputs that are totally unacceptable (random objects, NULL, etc.).
This particular function recycles the vector and outputs a matrix with the dimension matching the largest of the various inputs.
Another approach to the Joran's one is to use [] instead of reconstructing a matrix. In that way you can also keep the colnames for example:
truc <- matrix(c(1:3, LETTERS[3:1]), ncol=2)
colnames(truc) <- c("A", "B")
truc[] <- paste(truc, truc, sep=",")
truc
# A B
# [1,] "1,1" "C,C"
# [2,] "2,2" "B,B"
# [3,] "3,3" "A,A"
Or use sprintf withdim<-
`dim<-`(sprintf('%d,%d', mat, mat), dim(mat))
# [,1] [,2]
#[1,] "1,1" "3,3"
#[2,] "2,2" "4,4"
The ascii library has a function paste.matrix for element-wise paste across matrices. The output is the transpose to the desired outcome, but that's easy to address with t().
library(ascii)
mat <- matrix(1:4,2,2)
t(paste.matrix(mat,mat,sep=","))
[,1] [,2]
[1,] "1,1" "2,2"
[2,] "3,3" "4,4"

Extract elements from matrix diagonal saved in multiple lists in R

I´m trying to get different elements from multiple diagonal saved as lists. My data looks something like this:
res <- list()
res[[1]] <- matrix(c(0.04770856,0.02854005,0.02854005,0.03260190), nrow=2, ncol=2)
res[[2]] <- matrix(c(0.05436957,0.04887182,0.04887182, 0.10484454), nrow=2, ncol=2)
> res
[[1]]
[,1] [,2]
[1,] 0.04770856 0.02854005
[2,] 0.02854005 0.03260190
[[2]]
[,1] [,2]
[1,] 0.05436957 0.04887182
[2,] 0.04887182 0.10484454
> diag(res[[1]])
[1] 0.04770856 0.03260190
> diag(res[[2]])
[1] 0.05436957 0.10484454
I would like to save the first and second elements of each diagonal of a given list into a vector similar to this:
d.1st.el <- c(0.04770856, 0.05436957)
d.2nd.el <- c(0.03260190, 0.10484454)
My issue is to write the function that runs for all given lists and get the diagonals. For some reason, when I use unlist() to extract the values of each matrix for a given level, it doesn't get me the number but the full matrix.
Does anyone have a simple solution?
sapply(res, diag)
[,1] [,2]
[1,] 0.04770856 0.05436957
[2,] 0.03260190 0.10484454
# or
lapply(res, diag)
[[1]]
[1] 0.04770856 0.03260190
[[2]]
[1] 0.05436957 0.10484454
If you want the vectors for some reason in your global environment:
alld <- lapply(res, diag)
names(alld) <- sprintf("d.%d.el", 1:length(alld))
list2env(alld, globalenv())
In two steps you can do:
# Step 1 - Get the diagonals
all_diags <- sapply(res, function(x) diag(t(x)))
print(all_diags)
[,1] [,2]
[1,] 0.04770856 0.05436957
[2,] 0.03260190 0.10484454
# Step 2 - Append to vectors
d.1st.el <- all_diags[1,]
d.2nd.el <- all_diags[2,]

R paste matrix cells together

I want to paste cells of matrix together, But when I do paste(),It returns a vector. Is there a direct function for same in R?
mat <- matrix(1:4,2,2)
paste(mat,mat,sep=",")
I want the output as
[,1] [,2]
[1,] 1,1 2,2
[2,] 3,3 4,4
A matrix in R is just a vector with an attribute specifying the dimensions. When you paste them together you are simply losing the dimension attribute.
So,
matrix(paste(mat,mat,sep=","),2,2)
Or, e.g.
mat1 <- paste(mat,mat,sep=",")
> mat1
[1] "1,1" "2,2" "3,3" "4,4"
> dim(mat1) <- c(2,2)
> mat1
[,1] [,2]
[1,] "1,1" "3,3"
[2,] "2,2" "4,4"
Here's just one example of how you might write a simple function to do this:
paste_matrix <- function(...,sep = " ",collapse = NULL){
n <- max(sapply(list(...),nrow))
p <- max(sapply(list(...),ncol))
matrix(paste(...,sep = sep,collapse = collapse),n,p)
}
...but the specific function you want will depend on how you want it to handle more than two matrices, matrices of different dimensions or possibly inputs that are totally unacceptable (random objects, NULL, etc.).
This particular function recycles the vector and outputs a matrix with the dimension matching the largest of the various inputs.
Another approach to the Joran's one is to use [] instead of reconstructing a matrix. In that way you can also keep the colnames for example:
truc <- matrix(c(1:3, LETTERS[3:1]), ncol=2)
colnames(truc) <- c("A", "B")
truc[] <- paste(truc, truc, sep=",")
truc
# A B
# [1,] "1,1" "C,C"
# [2,] "2,2" "B,B"
# [3,] "3,3" "A,A"
Or use sprintf withdim<-
`dim<-`(sprintf('%d,%d', mat, mat), dim(mat))
# [,1] [,2]
#[1,] "1,1" "3,3"
#[2,] "2,2" "4,4"
The ascii library has a function paste.matrix for element-wise paste across matrices. The output is the transpose to the desired outcome, but that's easy to address with t().
library(ascii)
mat <- matrix(1:4,2,2)
t(paste.matrix(mat,mat,sep=","))
[,1] [,2]
[1,] "1,1" "2,2"
[2,] "3,3" "4,4"

Why do.call rbind outputs only one row?

I have a similar situation like this:
set.seed(2014)
df<-data.frame(
group=rbinom(100,1,0.6),
y1=rbinom(100,1,0.3),
y2=rbinom(100,1,0.8))
for (y in c("y1","y1")){
test<-summary(table(df[,"group"],df[,y]))
output<-do.call(rbind,list(cbind(test$statistic,test$p.value)))
}
output
[,1] [,2]
[1,] 1.066 0.3019
I'm wondering why it's not an output as I expected:
output
[,1] [,2]
[1,] 1.066 0.3019
[2,] 0.00011 1
In each iteration of the loop (you've used y1 twice) output is overwritten by a new value. Presumably you were aiming for soemthing like:
set.seed(2014)
df<-data.frame(
group=rbinom(100,1,0.6),
y1=rbinom(100,1,0.3),
y2=rbinom(100,1,0.8))
output <- NULL
for (y in c("y1","y2")){
test<-summary(table(df[,"group"],df[,y]))
output<-rbind(output,cbind(test$statistic,test$p.value))
}
output
2 issues: you are looping over y1 twice, and you are not appending your new result to the older one. I think you want to loop using lapply and rbind that list:
do.call(rbind,lapply(c("y1","y2"),
function (y) summary(table(df[,"group"],df[,y]))))[,c("statistic","p.value")]
statistic p.value
[1,] 1.065739 0.30191
[2,] 0.000106695 0.9917585
You basically do this twice:
y <- "y1"
test<-summary(table(df[,"group"],df[,y]))
myList <- list(cbind(test$statistic,test$p.value))
#[[1]]
# [,1] [,2]
#[1,] 1.065739 0.30191
See how there is only one element in the list? This element is passed to rbind:
do.call(rbind, myList)
# [,1] [,2]
#[1,] 1.065739 0.30191
rbind(myList[[1]])
# [,1] [,2]
#[1,] 1.065739 0.30191

Resources