Steven Rytina
2009-Aug-09 10:04 UTC
[R] binary operators that implement row and column sweeps of matrices by vectors
Submitted for perusal, comment, improvements, and/or critique. The presentation is in 3 sections: motivation, code, and comment. Motivation: As a new-comer to R from matrix oriented Gauss and Mata, I miss the tools for using a vector (and operator) to ‘sweep’ across a matrix. Here is how these work. If M is I rows by J columns, then one entry corresponding to each row of M is provided any 1 by I (row) vector, R. This can be swept ‘down’ the columns with a result that is I rows, J columns with entries {M[i,j] op R[i]) } where op can be any atomic binary operator including comparisons, such as !=, or arithmetic operators, such as ^. A J by 1 column vector can be applied analogously to ‘sweep’ through the rows. It is natural to implement these as R op M and M op C where R, M, and C conform as for matrix multiplication. Following Gauss, an arguably analogous outer product is returned when 2 vectors are equal length are submitted. Scalars are allowed. And so are matrices with identical numbers of rows and numberrs of columns. Code: dop<-function(ml,mr,op) {# inspired by Gauss dot op, more plainly order sensitive if (!is.matrix(ml)) print("Warning: in dop(), Left arg will be coerced to matrix") if (!is.matrix(mr)) print("Warning: in dop(), Right arg will be coerced to matrix") if (is.vector(ml) & ! is.matrix(ml))ml<-matrix(ml,nrow=1,ncol=length(ml)) #risky business, force conversion of Left-hand vector m.L<-as.matrix(ml);m.R<-as.matrix(mr); #conserve old Left and Right objects for debug I=nrow(m.L);J=ncol(m.L);K=nrow(m.R);L=ncol(m.R) if ( I == K & J==L) # standard element-wise conformity for binary op {return(mapply(op,m.L,m.R)); } else # the elses are just for show since the if’s form a partition. Unless my testing was mistaken, of course if (!( I==1 | J==1 |K==1|L==1)) # reject if no vector and not same size {print(" DOT op requires same shapes or vector argument") return(NA); } else if ( (I==1 & J==1) | (K==1 & L==1)) # at least one arg is scalar, mapply works {return(mapply(op,m.L,m.R));} else if (I==1 & L==1 & J==K) #this and next mimic Gauss convention(s) {return((matrix(outer(m.R,m.L,op),nrow=J,ncol=J )));} else # r1.*r2' # n.b outer returns matrix but matrix() left in for clarity if (J==1 & K==1 & I==L ) {return(matrix(outer(m.L,m.R,op),nrow=I,ncol=I ));}else # r1'.*r2 if (I==1 & J==K) # m.L is row vec, hence sweep columns {return( (matrix(mapply(op,t(m.L),m.R),nrow=nrow(m.R),ncol=ncol(m.R))));}else if (J==K & L==1) # m.R is col vec, hence sweep rows { return(t(matrix(qq1<-mapply(op,t(m.L),m.R),nrow=ncol(m.L),ncol=nrow(m.L))));}else print(c(" conformity failure with I,J,K,L= ",paste(I,J,K,L)));return(NA) } #a further step is to implement the various binary operators using the %text% convention. # some construction tools #set.of.ops<-c(getGroupMembers(Compare),getGroupMembers(Arith)) #names.for.ops<-as.array(c( "eq","gt","lt","ne","ge","le","plus","minus","x","exp","mod","dmod","div")) `%eq%`<-function(x,y) dop(x,y,op= "==") `%gt%`<-function(x,y) dop(x,y,op=">") `%lt%`<-function(x,y) dop(x,y,op="<") `%ne%`<-function(x,y) dop(x,y,op="!=") `%ge%`<-function(x,y) dop(x,y,op="<=") `%le%`<-function(x,y) dop(x,y,op=">=") `%plus%`<-function(x,y) dop(x,y,op="+") `%minus%`<-function(x,y) dop(x,y,op="-") `%x%`<-function(x,y) dop(x,y,op="*") `%exp%`<-function(x,y) dop(x,y,op="^") `%mod%`<-function(x,y) dop(x,y,op="%%") `%dmod%`<-function(x,y) dop(x,y,op="%/%") `%div%`<-function(x,y) dop(x,y,op="/") Comments: I have found these constructs very helpful in the past. One illustration is using logical comparison for a vector of standards applied to some set of variables. There are many different ways to try to achieve these results in R. I would love to hear if I have re-invented the wheel or over-looked some approach that is more general, more straightforward, or what have you. Could anyone suggest more elegant code that exploits the obvious parallelism(s) of that last mass of functions? The core construction is relatively strict in imposing explicit conformability requirements. This implicitly rejects the usual R approach of freely allowing multiple copies of shorter entities. While this will, in principle, return the ‘correct’ result whenever entities of the right size and shape are presented in the right order, it also seems to raise risk of returning unintended nonsense without adequate warning when presented with entities not in the right order or of inapt sizes. And I have, on related grounds, imposed restrictions to matrix objects. I would be interested in sentiments on the pluses and minuses of such decisions. While one could confine this to a (new?) class, it seems general enough to leave it unconfined. Would wiser folk disagree? My present approach is to add this code to my list of files that are source() –d at startup. That bulk is growing. Any thoughts on how to manage this? Perhaps for some this would seem to be a wasteful proliferation of operators. Is there perhaps some convenient mechanism so that this collection could be switched on and then off as needed? (I bet my question misses some obvious point which is the point of asking it.) Those illustrate my questions but I presume others may see other avenues worth pursuing. Thanks in advance for any comments and/or reactions. Steven Rytina Department of Sociology McGill University [[alternative HTML version deleted]]
Peter Dalgaard
2009-Aug-09 10:35 UTC
[R] binary operators that implement row and column sweeps of matrices by vectors
Steven Rytina wrote:> > Submitted for perusal, comment, improvements, and/or critique. The presentation is in 3 sections: motivation, code, and comment. > > > > Motivation: > > > > As a new-comer to R from matrix oriented Gauss and Mata, I miss thetools for using a vector (and operator) to ?sweep? across a matrix. And you have checked that this is not possible using sweep()?? -- O__ ---- Peter Dalgaard ?ster Farimagsgade 5, Entr.B c/ /'_ --- Dept. of Biostatistics PO Box 2099, 1014 Cph. K (*) \(*) -- University of Copenhagen Denmark Ph: (+45) 35327918 ~~~~~~~~~~ - (p.dalgaard at biostat.ku.dk) FAX: (+45) 35327907