creating a triangular matrix
Asked Answered
T

4

14

There must an elegant way to do this but I can't figure out so:

Columns are probabilities from 1 to 0 going right

Rows are probabilities from 0 to 1 going down

This kludgy code produces see the desired result (but I want to do it with a much larger matrix than this):

# Vector entries are rowname - colname, if >= 0
#
rb0 <-  c(NA,NA,NA,NA,NA,NA,NA,NA,NA,NA, 0)
rb1 <-  c(NA,NA,NA,NA,NA,NA,NA,NA,NA, 0,.1)
rb2 <-  c(NA,NA,NA,NA,NA,NA,NA,NA, 0,.1,.2)
rb3 <-  c(NA,NA,NA,NA,NA,NA,NA, 0,.1,.2,.3)
rb4 <-  c(NA,NA,NA,NA,NA,NA, 0,.1,.2,.3,.4)
rb5 <-  c(NA,NA,NA,NA,NA, 0,.1,.2,.3,.4,.5)
rb6 <-  c(NA,NA,NA,NA, 0,.1,.2,.3,.4,.5,.6)
rb7 <-  c(NA,NA,NA, 0,.1,.2,.3,.4,.5,.6,.7)
rb8 <-  c(NA,NA, 0,.1,.2,.3,.4,.5,.6,.7,.8)
rb9 <-  c(NA, 0,.1,.2,.3,.4,.5,.6,.7,.8,.9)
rb10 <- c( 0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1 )
indbias <- rbind(rb0,rb1,rb2,rb3,rb4,rb5,rb6,rb7,rb8,rb9,rb10)
colnames(indbias) <- seq(1,0,by=-.1)
rownames(indbias) <- seq(0,1,by=.1)
indbias

Thanks!

Twicetold answered 2/8, 2012 at 22:34 Comment(0)
S
19
 mat <- matrix(NA, 10,10)
 mat[row(mat)+col(mat) >=11] <- (row(mat)+col(mat) -11)[row(mat)+col(mat)>=11]/10
 mat
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]   NA   NA   NA   NA   NA   NA   NA   NA   NA   0.0
 [2,]   NA   NA   NA   NA   NA   NA   NA   NA  0.0   0.1
 [3,]   NA   NA   NA   NA   NA   NA   NA  0.0  0.1   0.2
 [4,]   NA   NA   NA   NA   NA   NA  0.0  0.1  0.2   0.3
 [5,]   NA   NA   NA   NA   NA  0.0  0.1  0.2  0.3   0.4
 [6,]   NA   NA   NA   NA  0.0  0.1  0.2  0.3  0.4   0.5
 [7,]   NA   NA   NA  0.0  0.1  0.2  0.3  0.4  0.5   0.6
 [8,]   NA   NA  0.0  0.1  0.2  0.3  0.4  0.5  0.6   0.7
 [9,]   NA  0.0  0.1  0.2  0.3  0.4  0.5  0.6  0.7   0.8
[10,]    0  0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8   0.9

I think this will be much faster than a plyr solution and I happen to think it is easier to comprehend. It basically sets up a test for the entries that are in the lower right hand "triangle" and then divides the results of that "test" matrix bu 10. You can look at the test matrix with this code:

row(mat)+col(mat) -11

Edit: I thought it possible that making the matrix once as sebastian-c illustrated and then doing a single test to do the NA setting might be faster ( with one third the number of calls to row and col) but it appears to be only one third as fast. It looks like the two seq calls take more time than the extra :

mat <- round(outer(seq(-0.5, 0.5, 0.1), seq(-0.5, 0.5, 0.1), `+`), 1)
is.na(mat) <- row(mat)+col(mat) <= 11
mat

I did find another solution based on the little known embed function:

mat <- embed(seq(-1,1, by=0.1), 11 )[,11:1]
is.na(mat) <- row(mat)+col(mat) <= 11

Although it is 50% faster than the new solution, it is still slower than the original.

Solanaceous answered 2/8, 2012 at 23:59 Comment(1)
(+1) Elegant and fast solution.Brachio
D
10

A slightly different solution, close in style to @DWin's:

Create a matrix with the appropriate lower triangle (I don't think the rounding is strictly necessary, but otherwise the floating point error makes it look awful):

mat <- round(outer(seq(-0.5, 0.5, 0.1), seq(-0.5, 0.5, 0.1), `+`), 1)
mat

      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
 [1,] -1.0 -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2  -0.1   0.0
 [2,] -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1   0.0   0.1
 [3,] -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1  0.0   0.1   0.2
 [4,] -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1  0.0  0.1   0.2   0.3
 [5,] -0.6 -0.5 -0.4 -0.3 -0.2 -0.1  0.0  0.1  0.2   0.3   0.4
 [6,] -0.5 -0.4 -0.3 -0.2 -0.1  0.0  0.1  0.2  0.3   0.4   0.5
 [7,] -0.4 -0.3 -0.2 -0.1  0.0  0.1  0.2  0.3  0.4   0.5   0.6
 [8,] -0.3 -0.2 -0.1  0.0  0.1  0.2  0.3  0.4  0.5   0.6   0.7
 [9,] -0.2 -0.1  0.0  0.1  0.2  0.3  0.4  0.5  0.6   0.7   0.8
[10,] -0.1  0.0  0.1  0.2  0.3  0.4  0.5  0.6  0.7   0.8   0.9
[11,]  0.0  0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8   0.9   1.0

Reverse the columns

mat <- mat[,rev(seq.int(ncol(mat)))]

Remove the upper triangle:

mat[upper.tri(mat)] <- NA

Re-reverse the columns:

mat <- mat[,rev(seq_len(ncol(mat)))]
mat

      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
 [1,]   NA   NA   NA   NA   NA   NA   NA   NA   NA    NA   0.0
 [2,]   NA   NA   NA   NA   NA   NA   NA   NA   NA   0.0   0.1
 [3,]   NA   NA   NA   NA   NA   NA   NA   NA  0.0   0.1   0.2
 [4,]   NA   NA   NA   NA   NA   NA   NA  0.0  0.1   0.2   0.3
 [5,]   NA   NA   NA   NA   NA   NA  0.0  0.1  0.2   0.3   0.4
 [6,]   NA   NA   NA   NA   NA  0.0  0.1  0.2  0.3   0.4   0.5
 [7,]   NA   NA   NA   NA  0.0  0.1  0.2  0.3  0.4   0.5   0.6
 [8,]   NA   NA   NA  0.0  0.1  0.2  0.3  0.4  0.5   0.6   0.7
 [9,]   NA   NA  0.0  0.1  0.2  0.3  0.4  0.5  0.6   0.7   0.8
[10,]   NA  0.0  0.1  0.2  0.3  0.4  0.5  0.6  0.7   0.8   0.9
[11,]    0  0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8   0.9   1.0

You can change the rownames from there.

EDIT: Given there are so many solutions, you may be interested to see how they benchmark. Using microbenchmark:

Unit: microseconds
   expr       min         lq     median         uq       max
1 AGS()   682.491   738.9370   838.0955   892.8815  4518.740
2  DW()    23.244    27.1680    31.3930    34.8650    70.937
3 MvG() 15469.664 15920.4820 17352.3215 17827.4380 18989.270
4  SC()   118.629   131.4575   144.1360   157.7190   631.779

@DWin's solution appears to be the fastest by quite a margin.

Donnydonnybrook answered 3/8, 2012 at 1:40 Comment(0)
H
5

One possible way, using my current favorite library:

library(plyr)
daply(expand.grid(x=seq(1,0,-.1), y=seq(0,1,.1)),
      .(y, x), with,
      if (x+y >= 1) x+y-1 else NA)

This gives the following result:

     x
y      0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9   1
  0   NA  NA  NA  NA  NA  NA  NA  NA  NA  NA 0.0
  0.1 NA  NA  NA  NA  NA  NA  NA  NA  NA 0.0 0.1
  0.2 NA  NA  NA  NA  NA  NA  NA  NA 0.0 0.1 0.2
  0.3 NA  NA  NA  NA  NA  NA  NA 0.0 0.1 0.2 0.3
  0.4 NA  NA  NA  NA  NA  NA 0.0 0.1 0.2 0.3 0.4
  0.5 NA  NA  NA  NA  NA 0.0 0.1 0.2 0.3 0.4 0.5
  0.6 NA  NA  NA  NA 0.0 0.1 0.2 0.3 0.4 0.5 0.6
  0.7 NA  NA  NA 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7
  0.8 NA  NA 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8
  0.9 NA 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9
  1    0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

The idea is that the expand.grid creates a data frame of all possible cell values. You could just as well use merge for this. Then you apply a function to each of these values to compute the cell content. And have daply turn this into a nice matrix for you, including names.

EDIT:
OK, you wanted the columns labeled in reverse order. ddply will sort them ascending. So try this:

daply(expand.grid(x=seq(0,1,.1), y=seq(0,1,.1)),
      .(y, x), with,
      if (y-x >= 0) y-x else NA)[,11:1]
     x
y      1 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1   0
  0   NA  NA  NA  NA  NA  NA  NA  NA  NA  NA 0.0
  0.1 NA  NA  NA  NA  NA  NA  NA  NA  NA 0.0 0.1
  0.2 NA  NA  NA  NA  NA  NA  NA  NA 0.0 0.1 0.2
  0.3 NA  NA  NA  NA  NA  NA  NA 0.0 0.1 0.2 0.3
  0.4 NA  NA  NA  NA  NA  NA 0.0 0.1 0.2 0.3 0.4
  0.5 NA  NA  NA  NA  NA 0.0 0.1 0.2 0.3 0.4 0.5
  0.6 NA  NA  NA  NA 0.0 0.1 0.2 0.3 0.4 0.5 0.6
  0.7 NA  NA  NA 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7
  0.8 NA  NA 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8
  0.9 NA 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9
  1    0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
Humfrid answered 2/8, 2012 at 23:38 Comment(0)
O
2
require(matlab)
x=matrix(seq(0,1,.1),1)
X=x[rep(1,c(11)),]
X[upper.tri(X)]=NA
X=t(X)
for(a in 1:11){
  X[1:a,a]=rev(X[1:a,a])
}
X=flipud(X)
colnames(X) <- seq(1,0,by=-.1)
rownames(X) <- seq(0,1,by=.1)
Ocampo answered 2/8, 2012 at 23:1 Comment(1)
I fixed your code formatting, but I also downvoted you because the result doesn't look anything like what the OP asked for. :-(Expiatory

© 2022 - 2024 — McMap. All rights reserved.