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 d
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.
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.
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
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)