Matthew Keller
2015-Sep-17 20:36 UTC
[R] fast way to create composite matrix based on mixed indices?
HI all, Sorry for the title here but I find this difficult to describe succinctly. Here's the problem. I want to create a new matrix where each row is a composite of an old matrix, but where the row & column indexes of the old matrix change for different parts of the new matrix. For example, the second row of new matrix (which has , e.g., 10 columns) might be columns 1 to 3 of row 2 of old matrix, columns 4 to 8 of row 1 of old matrix, and columns 9 to 10 of row 3 of old matrix. Here's an example in code: #The old matrix (old.mat <- matrix(1:30,nrow=3,byrow=TRUE)) #matrix of indices to create the new matrix from the old one. #The 1st column gives the row number of the new matrix #the 2nd gives the row of the old matrix that we're going to copy into the new matrix #the 3rd gives the starting column of the old matrix for the row in col 2 #the 4th gives the end column of the old matrix for the row in col 2 index <- matrix(c(1,1,1,4, 1,3,5,10, 2,2,1,3, 2,1,4,8, 2,3,9,10), nrow=5,byrow=TRUE, dimnames=list(NULL,c('new.mat.row','old.mat.row','old.mat.col.start','old.mat.col.end'))) I will be given old.mat and index and want to create new.mat from them. I want to create a new.matrix of two rows that looks like this: new.mat <- matrix(c(1:4,25:30,11:13,4:8,29:30),byrow=TRUE,nrow=2) So here, the first row of new.mat is columns 1 to 4 of row 1 of the old.mat and columns 5 to 10 of row 3 of old.mat. new.mat and old.mat will always have the same number of columns but the number of rows could differ. I could accomplish this in a loop, but the real problem is quite large (new.mat might have 1e8 elements), and so a for loop would be prohibitively slow. I may resort to unix tools and use a shell script, but wanted to first see if this is doable in R in a fast way. Thanks in advance! Matt -- Matthew C Keller Asst. Professor of Psychology University of Colorado at Boulder www.matthewckeller.com [[alternative HTML version deleted]]
Dénes Tóth
2015-Sep-17 21:22 UTC
[R] fast way to create composite matrix based on mixed indices?
Hi Matt, you could use matrix indexing. Here is a possible solution, which could be optimized further (probably). # The old matrix (old.mat <- matrix(1:30,nrow=3,byrow=TRUE)) # matrix of indices index <- matrix(c(1,1,1,4, 1,3,5,10, 2,2,1,3, 2,1,4,8, 2,3,9,10), nrow=5,byrow=TRUE, dimnames=list(NULL, c('new.mat.row','old.mat.row', 'old.mat.col.start','old.mat.col.end'))) # expected result new.mat <- matrix(c(1:4,25:30,11:13,4:8,29:30), byrow=TRUE, nrow=2) # # column indices ind <- mapply(seq, index[, 3], index[,4], SIMPLIFY = FALSE, USE.NAMES = FALSE) ind_len <- vapply(ind, length, integer(1)) ind <- unlist(ind) # # old indices old.ind <- cbind(rep(index[,2], ind_len), ind) # # new indices new.ind <- cbind(rep(index[,1], ind_len), ind) # # create the new matrix result <- matrix(NA_integer_, max(index[,1]), max(index[,4])) # # fill the new matrix result[new.ind] <- old.mat[old.ind] # # check the results identical(result, new.mat) HTH, Denes On 09/17/2015 10:36 PM, Matthew Keller wrote:> HI all, > > Sorry for the title here but I find this difficult to describe succinctly. > Here's the problem. > > I want to create a new matrix where each row is a composite of an old > matrix, but where the row & column indexes of the old matrix change for > different parts of the new matrix. For example, the second row of new > matrix (which has , e.g., 10 columns) might be columns 1 to 3 of row 2 of > old matrix, columns 4 to 8 of row 1 of old matrix, and columns 9 to 10 of > row 3 of old matrix. > > Here's an example in code: > > #The old matrix > (old.mat <- matrix(1:30,nrow=3,byrow=TRUE)) > > #matrix of indices to create the new matrix from the old one. > #The 1st column gives the row number of the new matrix > #the 2nd gives the row of the old matrix that we're going to copy into the > new matrix > #the 3rd gives the starting column of the old matrix for the row in col 2 > #the 4th gives the end column of the old matrix for the row in col 2 > index <- matrix(c(1,1,1,4, > 1,3,5,10, > 2,2,1,3, > 2,1,4,8, > 2,3,9,10), > nrow=5,byrow=TRUE, > > dimnames=list(NULL,c('new.mat.row','old.mat.row','old.mat.col.start','old.mat.col.end'))) > > I will be given old.mat and index and want to create new.mat from them. > > I want to create a new.matrix of two rows that looks like this: > new.mat <- matrix(c(1:4,25:30,11:13,4:8,29:30),byrow=TRUE,nrow=2) > > So here, the first row of new.mat is columns 1 to 4 of row 1 of the old.mat > and columns 5 to 10 of row 3 of old.mat. > > new.mat and old.mat will always have the same number of columns but the > number of rows could differ. > > I could accomplish this in a loop, but the real problem is quite large > (new.mat might have 1e8 elements), and so a for loop would be prohibitively > slow. > > I may resort to unix tools and use a shell script, but wanted to first see > if this is doable in R in a fast way. > > Thanks in advance! > > Matt > >
Matthew Keller
2015-Sep-18 15:11 UTC
[R] fast way to create composite matrix based on mixed indices?
Brilliant Denes. Thank you for your help. This worked and is obviously much faster than a loop... On Thu, Sep 17, 2015 at 3:22 PM, D?nes T?th <toth.denes at ttk.mta.hu> wrote:> Hi Matt, > > you could use matrix indexing. Here is a possible solution, which could be > optimized further (probably). > > # The old matrix > (old.mat <- matrix(1:30,nrow=3,byrow=TRUE)) > # matrix of indices > index <- matrix(c(1,1,1,4, > 1,3,5,10, > 2,2,1,3, > 2,1,4,8, > 2,3,9,10), > nrow=5,byrow=TRUE, > dimnames=list(NULL, > c('new.mat.row','old.mat.row', > 'old.mat.col.start','old.mat.col.end'))) > # expected result > new.mat <- matrix(c(1:4,25:30,11:13,4:8,29:30), > byrow=TRUE, nrow=2) > # > # column indices > ind <- mapply(seq, index[, 3], index[,4], > SIMPLIFY = FALSE, USE.NAMES = FALSE) > ind_len <- vapply(ind, length, integer(1)) > ind <- unlist(ind) > > # > # old indices > old.ind <- cbind(rep(index[,2], ind_len), ind) > # > # new indices > new.ind <- cbind(rep(index[,1], ind_len), ind) > # > # create the new matrix > result <- matrix(NA_integer_, max(index[,1]), max(index[,4])) > # > # fill the new matrix > result[new.ind] <- old.mat[old.ind] > # > # check the results > identical(result, new.mat) > > > HTH, > Denes > > > > > > On 09/17/2015 10:36 PM, Matthew Keller wrote: > >> HI all, >> >> Sorry for the title here but I find this difficult to describe succinctly. >> Here's the problem. >> >> I want to create a new matrix where each row is a composite of an old >> matrix, but where the row & column indexes of the old matrix change for >> different parts of the new matrix. For example, the second row of new >> matrix (which has , e.g., 10 columns) might be columns 1 to 3 of row 2 of >> old matrix, columns 4 to 8 of row 1 of old matrix, and columns 9 to 10 of >> row 3 of old matrix. >> >> Here's an example in code: >> >> #The old matrix >> (old.mat <- matrix(1:30,nrow=3,byrow=TRUE)) >> >> #matrix of indices to create the new matrix from the old one. >> #The 1st column gives the row number of the new matrix >> #the 2nd gives the row of the old matrix that we're going to copy into the >> new matrix >> #the 3rd gives the starting column of the old matrix for the row in col 2 >> #the 4th gives the end column of the old matrix for the row in col 2 >> index <- matrix(c(1,1,1,4, >> 1,3,5,10, >> 2,2,1,3, >> 2,1,4,8, >> 2,3,9,10), >> nrow=5,byrow=TRUE, >> >> >> dimnames=list(NULL,c('new.mat.row','old.mat.row','old.mat.col.start','old.mat.col.end'))) >> >> I will be given old.mat and index and want to create new.mat from them. >> >> I want to create a new.matrix of two rows that looks like this: >> new.mat <- matrix(c(1:4,25:30,11:13,4:8,29:30),byrow=TRUE,nrow=2) >> >> So here, the first row of new.mat is columns 1 to 4 of row 1 of the >> old.mat >> and columns 5 to 10 of row 3 of old.mat. >> >> new.mat and old.mat will always have the same number of columns but the >> number of rows could differ. >> >> I could accomplish this in a loop, but the real problem is quite large >> (new.mat might have 1e8 elements), and so a for loop would be >> prohibitively >> slow. >> >> I may resort to unix tools and use a shell script, but wanted to first see >> if this is doable in R in a fast way. >> >> Thanks in advance! >> >> Matt >> >> >>-- Matthew C Keller Asst. Professor of Psychology University of Colorado at Boulder www.matthewckeller.com [[alternative HTML version deleted]]