Find the minimum row value and return value and the corresponding column name

Consider the following matrix m :

  ca bsa rd zaa ada 3 4 3 2 aca 1 4 5 2 ara 3 4 3 2 ava 3 4 5 2 

I try to find the minimum value for each row and return data.frame as:

  qsd 1 ada zaa 2 2 aca ca 1 3 ara zaa 2 4 ava zaa 2 

Now I am doing:

 res <- t(sapply(seq(nrow(m)), function(i) { j <- which.min(m[i,]) c(q = rownames(m)[i], s = colnames(m)[j], d = m[i,j])})) res <- data.frame(res) res$d <- as.numeric(res$d) 

I am looking for a better way to create this.

It is not possible to build res with c() (forcing all components to the same type), then convert it to data.frame and finally change d to numeric to get the following structure:

 'data.frame': 4 obs. of 3 variables: $ q: Factor w/ 4 levels "aca","ada","ara",..: 2 1 3 4 $ s: Factor w/ 2 levels "ca","zaa": 2 1 2 2 $ d: num 2 1 2 2 

I will also need to handle a case where there may be several lows


Data

 m <- structure(c(3, 1, 3, 3, 4, 4, 4, 4, 3, 5, 3, 5, 2, 2, 2, 2), .Dim = c(4L, 4L), .Dimnames = list(c("ada", "aca", "ara", "ava"), c("ca", "bsa", "rd", "zaa"))) 
+4
source share
4 answers

You can find the column index of the minimum value for each row using max.col in the negative version of the matrix.

 col_id <- max.col(-m) data.frame(q = rownames(m), s = colnames(m)[col_id], d = m[cbind(1:length(col_id), col_id)]) # qsd # 1 ada zaa 2 # 2 aca ca 1 # 3 ara zaa 2 # 4 ava zaa 2 
+5
source

I can do:

 cn<-colnames(m) data.frame(t(apply(m,1,function(x){y<-which.min(x); c(cn[y],x[y])}))) X1 X2 ada zaa 2 aca ca 1 ara zaa 2 ava zaa 2 

Key points to save time: only calculate rownames once (outside the loop) and only calculate min once at each iteration.

Here's a control option parameter:

 set.seed(10151) NN<-1000 m<-matrix(sample(NN,NN^2,T),ncol=NN, dimnames=list(replicate(NN,paste0(sample(letters,3,T),collapse="")), replicate(NN,paste0(sample(letters,3,T),collapse="")))) library(microbenchmark) Unit: milliseconds expr min lq mean median uq max neval steven 26.35880 28.46085 30.84153 29.51562 30.82366 80.18648 50 henrik 16.83619 17.47235 19.14324 18.73855 20.01433 26.63396 50 mikec 25.25390 27.06398 28.69647 28.25848 29.46897 37.15669 50 rawr 110.00786 118.80357 150.76281 128.73180 176.67976 292.00051 50 richard 23.44658 25.49981 27.86844 26.72024 27.62607 78.14996 50 akrun 26.65337 28.12803 35.52941 29.33611 33.54546 83.34182 50 andres 53.05468 59.54172 71.10404 61.85948 71.27818 149.76439 50 

* Note: henrik has a typo, which I corrected incorrectly in the previous test. For akrun I use optimized {res<-setDT(melt(m)); res[res[, .I[which.min(value)] ,.(Var1)]$V1]} {res<-setDT(melt(m)); res[res[, .I[which.min(value)] ,.(Var1)]$V1]}

Elimination of difficulty, setting NN<-2000 (the two slowest deletions to save time):

 Unit: milliseconds expr min lq mean median uq max neval steven 112.80108 114.06360 115.73233 115.29611 116.97757 122.3215 50 henrik 67.16095 70.17341 93.84658 98.30461 99.96561 162.4522 50 mikec 107.81738 110.24776 117.01182 111.64840 114.39962 166.1335 50 richard 101.08277 104.76309 115.75823 105.96692 107.78915 206.8925 50 akrun 101.65822 131.51744 159.14601 165.14284 183.04740 236.5955 50 

And if you were not sure, NN<-5000 :

 Unit: milliseconds expr min lq mean median uq max neval henrik 413.3938 422.7162 450.3574 432.1532 465.9551 707.6048 50 mikec 705.4221 725.0111 764.4510 742.2715 801.8704 901.3484 50 richard 695.7005 716.7905 754.1729 732.5105 778.5526 902.7917 50 
+3
source

Maybe something like

 w <- apply(m, 1, which.min) data.frame( q = rownames(m), s = colnames(m)[w], d = m[cbind(seq_along(w), w)] ) # qsd # 1 ada zaa 2 # 2 aca ca 1 # 3 ara zaa 2 # 4 ava zaa 2 
+2
source

Here is another. Maybe someone smarter can come up with one liner, but this is the best I can do, mom

 m <- structure(c(3, 1, 3, 3, 4, 4, 4, 4, 3, 5, 3, 5, 2, 2, 2, 2),.Dim = c(4L, 4L),.Dimnames = list(c("ada", "aca", "ara", "ava"), c("ca", "bsa", "rd", "zaa"))) (m2 <- data.frame(as.table(m * (apply(m, 1, min) == m)))) m2[m2$Freq > 0, ] # Var1 Var2 Freq # 2 aca ca 1 # 13 ada zaa 2 # 15 ara zaa 2 # 16 ava zaa 2 
+1
source

All Articles