Moving rows between subarrays - r

I have a number of subarrays, say 2 (for simplicity), each with the same number of rows and columns. Each spot in the subarrays is occupied by a number in [1, 10].
What I would like to do is move rows randomly between subarrays according to some rate of movement m = [0, 1]. m = 0 corresponds to no movement, while m = 1 means that any rows across all subarrays can be moved.
I take inspiration from:
How to swap a number of the values between 2 rows in R
but my problem is a bit different than this. I do know that sample() would be needed here.
Is there an easy way to go about accomplishing this?
This doesn't do it, but I believe I'm on the right track anyway.
m <- 0.2
a <- array(dim = c(5, 5, 2)) # 5 rows, 5 columns, 2 subarrays
res <- rep(sample(nrow(a), size = ceiling(nrow(a)*m), replace = FALSE)) # sample 20% of rows from array a.
Any assistance is appreciated.

It is significantly easier if you can use a matrix (2-dim array).
set.seed(2)
m <- 0.2
d <- c(10, 4)
a <- array(sample(prod(d)), dim = d)
a
# [,1] [,2] [,3] [,4]
# [1,] 8 17 14 1
# [2,] 28 37 40 26
# [3,] 22 38 16 29
# [4,] 7 35 3 32
# [5,] 34 11 23 4
# [6,] 36 33 19 31
# [7,] 5 24 30 13
# [8,] 39 6 27 25
# [9,] 15 10 12 9
# [10,] 18 2 21 20
(I'm going to set the seed again to something that conveniently gives me something "interesting" to show.)
set.seed(2)
ind <- which(runif(d[1]) < m)
ind
# [1] 1 4 7
The first randomness, runif, is compared against m and generates the indices that may change. The second randomness, sample below, takes those indices and possibly reorders them. (In this case, it reorders "1,4,7" to "4,1,7", meaning the third of the rows-that-may-change will be left unchanged.)
a[ind,] <- a[sample(ind),]
a
# [,1] [,2] [,3] [,4]
# [1,] 7 35 3 32 # <-- row 4
# [2,] 28 37 40 26
# [3,] 22 38 16 29
# [4,] 8 17 14 1 # <-- row 1
# [5,] 34 11 23 4
# [6,] 36 33 19 31
# [7,] 5 24 30 13 # <-- row 7, unchanged
# [8,] 39 6 27 25
# [9,] 15 10 12 9
# [10,] 18 2 21 20
Note that this is probabilistic, which means a probability of 0.2 does not guarantee you 20% (or even any) of the rows will be swapped.
(Since I'm guessing you'd really like to preserve your 3-dim (or even n-dim) array, you might be able to use aperm to transfer between array <--> matrix.)
EDIT 1
As an alternative to a probabilitic use of runif, you can use:
ind <- head(sample(d[1]),size=d[1]*m)
to get closer to your goal of "20%". Since d[1]*m will often not be an integer, head silently truncates/floors the number, so you'll get the price-is-right winner: closest to but not over your desired percentage.
EDIT 2
A reversible method for transforming an n-dimensional array into a matrix and back again. Caveat: though the logic appears solid, my testing has only included a couple arrays.
array2matrix <- function(a) {
d <- dim(a)
ind <- seq_along(d)
a2 <- aperm(a, c(ind[2], ind[-2]))
dim(a2) <- c(d[2], prod(d[-2]))
a2 <- t(a2)
attr(a2, "origdim") <- d
a2
}
The reversal uses the "origdim" attribute if still present; this will work as long as your modifications to the matrix do not clear its attributes. (Simple row-swapping does not.)
matrix2array <- function(m, d = attr(m, "origdim")) {
ind <- seq_along(d)
m2 <- t(m)
dim(m2) <- c(d[2], d[-2])
aperm(m2, c(ind[2], ind[-2]))
}
(These two functions should probably do some more error-checks, such as is.null(d).)
A sample run:
set.seed(2)
dims <- 5:2
a <- array(sample(prod(dims)), dim=dims)
Quick show:
a[,,1,1:2,drop=FALSE]
# , , 1, 1
# [,1] [,2] [,3] [,4]
# [1,] 23 109 61 90
# [2,] 84 15 27 102
# [3,] 68 95 83 24
# [4,] 20 53 117 46
# [5,] 110 62 43 8
# , , 1, 2
# [,1] [,2] [,3] [,4]
# [1,] 118 25 14 93
# [2,] 65 21 16 77
# [3,] 87 82 3 38
# [4,] 92 12 78 17
# [5,] 49 4 75 80
The transformation:
m <- array2matrix(a)
dim(m)
# [1] 30 4
head(m)
# [,1] [,2] [,3] [,4]
# [1,] 23 109 61 90
# [2,] 84 15 27 102
# [3,] 68 95 83 24
# [4,] 20 53 117 46
# [5,] 110 62 43 8
# [6,] 67 47 1 54
Proof of reversability:
identical(matrix2array(m), a)
# [1] TRUE
EDIT 3, "WRAP UP of all code"
Creating fake data:
dims <- c(5,4,2)
(a <- array(seq(prod(dims)), dim=dims))
# , , 1
# [,1] [,2] [,3] [,4]
# [1,] 1 6 11 16
# [2,] 2 7 12 17
# [3,] 3 8 13 18
# [4,] 4 9 14 19
# [5,] 5 10 15 20
# , , 2
# [,1] [,2] [,3] [,4]
# [1,] 21 26 31 36
# [2,] 22 27 32 37
# [3,] 23 28 33 38
# [4,] 24 29 34 39
# [5,] 25 30 35 40
(m <- array2matrix(a))
# [,1] [,2] [,3] [,4]
# [1,] 1 6 11 16
# [2,] 2 7 12 17
# [3,] 3 8 13 18
# [4,] 4 9 14 19
# [5,] 5 10 15 20
# [6,] 21 26 31 36
# [7,] 22 27 32 37
# [8,] 23 28 33 38
# [9,] 24 29 34 39
# [10,] 25 30 35 40
# attr(,"origdim")
# [1] 5 4 2
The random-swapping of rows. I'm using 50% here.
pct <- 0.5
nr <- nrow(m)
set.seed(3)
(ind1 <- sample(nr, size = ceiling(nr * pct)))
# [1] 2 8 4 3 9
(ind2 <- sample(ind1))
# [1] 3 2 9 8 4
m[ind1,] <- m[ind2,]
m
# [,1] [,2] [,3] [,4]
# [1,] 1 6 11 16
# [2,] 3 8 13 18
# [3,] 23 28 33 38
# [4,] 24 29 34 39
# [5,] 5 10 15 20
# [6,] 21 26 31 36
# [7,] 22 27 32 37
# [8,] 2 7 12 17
# [9,] 4 9 14 19
# [10,] 25 30 35 40
# attr(,"origdim")
# [1] 5 4 2
(Note that I pre-made ind1 and ind2 here, mostly to see what was going on internally. You can replace m[ind2,] with m[sample(ind1),] for the same effect.)
BTW: if we had instead used a seed of 2, we would notice that 2 rows are not swapped:
set.seed(2)
(ind1 <- sample(nr, size = ceiling(nr * pct)))
# [1] 2 7 5 10 6
(ind2 <- sample(ind1))
# [1] 6 2 5 10 7
Because of this, I chose a seed of 3 for demonstration. However, this may give the appearance of things not working. Lacking more controlling code, sample does not ensure that positions change: it is certainly reasonable to expect that "randomly swap rows" could randomly choose to move row 2 to row 2. Take for example:
set.seed(267)
(ind1 <- sample(nr, size = ceiling(nr * pct)))
# [1] 3 6 5 7 2
(ind2 <- sample(ind1))
# [1] 3 6 5 7 2
The first randomly chooses five rows, and then reorders them randomly into an unchanged order. (I suggest that if you want to force that they are all movements, you should ask a new question asking about just forcing a sample vector to change.)
Anyway, we can regain the original dimensionality with the second function:
(a2 <- matrix2array(m))
# , , 1
# [,1] [,2] [,3] [,4]
# [1,] 1 6 11 16
# [2,] 3 8 13 18
# [3,] 23 28 33 38
# [4,] 24 29 34 39
# [5,] 5 10 15 20
# , , 2
# [,1] [,2] [,3] [,4]
# [1,] 21 26 31 36
# [2,] 22 27 32 37
# [3,] 2 7 12 17
# [4,] 4 9 14 19
# [5,] 25 30 35 40
In the first plane of the array, rows 1 and 5 are unchanged; in the second plane, rows 1, 2, and 5 are unchanged. Five rows the same, five rows moved around (but otherwise unchanged within each row).

Related

Vector to list of matrices

I´m trying convert a vector to multiple matrices and save them in a list.
#Create list to save matrix
BSEPRA=vector("list", 420)
#Vector size 6720
temporalRA
I need to build 4*4 size matrices with the first 16 elements then with the next ones (17:32) and so on up to 6705:6720 to have 420 and save them in the list. But this doesn't work:
for (i in 1:length(temporalRA)){
temp2<-matrix(temporalRA[seq(1,6720, 16), ],nrow = 4,ncol = 4, )
BSEPRA[[i]]=temp2
}
vec <- 1:32
split(vec, (seq_along(vec) - 1) %/% (4*4))
# $`0`
# [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# $`1`
# [1] 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
lapply(split(vec, (seq_along(vec) - 1) %/% 16), matrix, nrow = 4, ncol = 4)
# $`0`
# [,1] [,2] [,3] [,4]
# [1,] 1 5 9 13
# [2,] 2 6 10 14
# [3,] 3 7 11 15
# [4,] 4 8 12 16
# $`1`
# [,1] [,2] [,3] [,4]
# [1,] 17 21 25 29
# [2,] 18 22 26 30
# [3,] 19 23 27 31
# [4,] 20 24 28 32
If there's any concern that you will not have a perfect multiple of 16, I encourage you to take one extra step.
The problem:
vec <- 1:30
vecs <- split(vec, (seq_along(vec) - 1) %/% (4*4))
vecs
# $`0`
# [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# $`1`
# [1] 17 18 19 20 21 22 23 24 25 26 27 28 29 30
lapply(vecs, matrix, nrow = 4)
# Warning in FUN(X[[i]], ...) :
# data length [14] is not a sub-multiple or multiple of the number of rows [4]
# $`0`
# [,1] [,2] [,3] [,4]
# [1,] 1 5 9 13
# [2,] 2 6 10 14
# [3,] 3 7 11 15
# [4,] 4 8 12 16
# $`1`
# [,1] [,2] [,3] [,4]
# [1,] 17 21 25 29
# [2,] 18 22 26 30
# [3,] 19 23 27 17
# [4,] 20 24 28 18
(Notice how 17:18 are "recycled".)
The fix:
vec <- 1:30
vecs <- split(vec, (seq_along(vec) - 1) %/% (4*4))
vecs <- lapply(vecs, `length<-`, max(lengths(vecs)))
vecs
# $`0`
# [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# $`1`
# [1] 17 18 19 20 21 22 23 24 25 26 27 28 29 30 NA NA
lapply(vecs, matrix, nrow = 4)
# $`0`
# [,1] [,2] [,3] [,4]
# [1,] 1 5 9 13
# [2,] 2 6 10 14
# [3,] 3 7 11 15
# [4,] 4 8 12 16
# $`1`
# [,1] [,2] [,3] [,4]
# [1,] 17 21 25 29
# [2,] 18 22 26 30
# [3,] 19 23 27 NA
# [4,] 20 24 28 NA
Another approach that I think will be more efficient than splitting the vector and iterating over the list to make matrices is to make an array and split it:
vec <- 1:32
mdim <- 4
mdimsq <- mdim^2
asplit(array(vec, dim = c(mdim, mdim, length(vec) / mdimsq)), 3)
[[1]]
[,1] [,2] [,3] [,4]
[1,] 1 5 9 13
[2,] 2 6 10 14
[3,] 3 7 11 15
[4,] 4 8 12 16
[[2]]
[,1] [,2] [,3] [,4]
[1,] 17 21 25 29
[2,] 18 22 26 30
[3,] 19 23 27 31
[4,] 20 24 28 32
Likewise, if the vector is not a perfect multiple it can be padded with NA:
vec <- 1:30
vln <- ceiling(length(vec)/mdimsq) * mdimsq
if (length(vec) < vln) vec[vln] <- NA
asplit(array(vec, dim = c(mdim, mdim, length(vec) / mdimsq)), 3)
[[1]]
[,1] [,2] [,3] [,4]
[1,] 1 5 9 13
[2,] 2 6 10 14
[3,] 3 7 11 15
[4,] 4 8 12 16
[[2]]
[,1] [,2] [,3] [,4]
[1,] 17 21 25 29
[2,] 18 22 26 30
[3,] 19 23 27 NA
[4,] 20 24 28 NA

Exclude specific columns from a matrix

I have a list of numbers (example bellow):
[[178]]
NULL
[[179]]
[1] 179 66
[[180]]
[1] 180 67
[[181]]
[1] 181 123
[[182]]
[1] 182
This list contains columns (179, 66, 180, 67, 181, 123) I want to exclude from a matrix.
I tried commands bellow, but they didn't work:
MyMatrix[, !(unlist(MyList))]
MyMatrix[, -(unlist(MyList))]
MyMatrix[, !unlist(MyList)]
MyMatrix[, -unlist(MyList)]
My question: what is a right way to exclude specific columns from a matrix?
Here's my small replication of your problem.
listOfColumns<-list(NULL, c(2,3), 5, NULL)
listOfColumns #print for viewing
#output
#[[1]]
#NULL
#[[2]]
#[1] 2 3
#[[3]]
#[1] 5
#[[4]]
#NULL
MyMatrix<-matrix(1:50, nrow=10, ncol=5)
MyMatrix #print for viewing
#output
# [,1] [,2] [,3] [,4] [,5]
#[1,] 1 11 21 31 41
#[2,] 2 12 22 32 42
#[3,] 3 13 23 33 43
#[4,] 4 14 24 34 44
#[5,] 5 15 25 35 45
#[6,] 6 16 26 36 46
#[7,] 7 17 27 37 47
#[8,] 8 18 28 38 48
#[9,] 9 19 29 39 49
#[10,] 10 20 30 40 50
First, the way you're going to want to subset your matrix so that you omit the given column numbers is to do
MyMatrix[-columnNumbers]
In R, negative numbers used to subset correspond to entries that should be omitted.
The following call output's what you want
MyMatrix[,-unlist(listOfNumbers)]
#output
# [,1] [,2]
# [1,] 1 31
# [2,] 2 32
# [3,] 3 33
# [4,] 4 34
# [5,] 5 35
# [6,] 6 36
# [7,] 7 37
# [8,] 8 38
# [9,] 9 39
# [10,] 10 40
If you want to keep this result for later use, you'll need to store it (As David Robinson got at)
MySmallerMatrix<-MyMatrix[,-unlist(listOfNumbers)]

R reverse some rows in a matrix

I have a 60 column matrix, and I want to reverse the some of its rows.
I came across the following two ways to do this:
#rtr is an integer vectors with the indices of the rows I want to reverse
matrix[rtr,]<-matrix[rtr,(ncol(matrix):1]
and
matrix[rtr,]<-rev(mat[rtr,])
Are these two implementations expected to produce the same result, or
are there some differences between them?
Thanks in advance
This seems to be a pretty easy thing to test
mm <- matrix(1:(6*7), ncol=6)
m2 <- m1 <- mm
rtr<-c(1,6,7)
m1[rtr,]<-m1[rtr, ncol(m1):1]
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 36 29 22 15 8 1
# [2,] 2 9 16 23 30 37
# [3,] 3 10 17 24 31 38
# [4,] 4 11 18 25 32 39
# [5,] 5 12 19 26 33 40
# [6,] 41 34 27 20 13 6
# [7,] 42 35 28 21 14 7
m2[rtr,]<-rev(m2[rtr,])
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 42 35 28 21 14 7
# [2,] 2 9 16 23 30 37
# [3,] 3 10 17 24 31 38
# [4,] 4 11 18 25 32 39
# [5,] 5 12 19 26 33 40
# [6,] 41 34 27 20 13 6
# [7,] 36 29 22 15 8 1
We can see they produce different output. The latter changes the order of the rows as well rather than just reversing them "in place"

Sorting specified rows in a matrix by the first column in R

I have a character matrix mtr of n rows and 3 columns.
I have a numeric vector nmb with some numbers, for example, 4,5,6
I want to sort only the rows of mtr, the numbers of which are contained by nmb, by the first column of my matrix.
So in my case I want to leave my matrix untouched except for rows 4,5,6 which I would like to be sorted by the first column and, of course, written back into my matrix mtr.
How could I do that? Thanks.
You can do it in this way:
mtr[nmb,] <- mtr[order(mtr[nmb,1]),]
I think this will do it
mtr[nmb,] <- mtr[nmb,][order(mtr[nmb,1]),]
An example:
nmb <- 4:6
mtr <- matrix(30:1, ncol=3)
> mtr
[,1] [,2] [,3]
[1,] 30 20 10
[2,] 29 19 9
[3,] 28 18 8
[4,] 27 17 7
[5,] 26 16 6
[6,] 25 15 5
[7,] 24 14 4
[8,] 23 13 3
[9,] 22 12 2
[10,] 21 11 1
> mtr[nmb,] <- mtr[nmb,][order(mtr[nmb,1]),]
> mtr
[,1] [,2] [,3]
[1,] 30 20 10
[2,] 29 19 9
[3,] 28 18 8
[4,] 25 15 5 <-
[5,] 26 16 6 <- sorted
[6,] 27 17 7 <-
[7,] 24 14 4
[8,] 23 13 3
[9,] 22 12 2
[10,] 21 11 1

R: summing over an interval of rows

I'm a beginner R user and I need to write a function that sums the rows of a data frame over a fixed interval (every 4 rows).
I've tried the following code
camp<-function(X){
i<-1
n<-nrow(X)
xc<-matrix(nrow=36,ncol=m)
for (i in 1:n){
xc<-apply(X[i:(i+4),],2,sum)
rownames(xc[i])<-rownames(X[i])
i<-i+5
}
return(xc)
}
the result is "Error in X[i:(i + 4), ] : index out of range".
How can I solve? Any suggestion?
Thanks.
The zoo package has rollapply which is pretty handy for stuff like this...
# Make some data
set.seed(1)
m <- matrix( sample( 10 , 32 , repl = TRUE ) , 8 )
# [,1] [,2] [,3] [,4]
#[1,] 3 7 8 3
#[2,] 4 1 10 4
#[3,] 6 3 4 1
#[4,] 10 2 8 4
#[5,] 3 7 10 9
#[6,] 9 4 3 4
#[7,] 10 8 7 5
#[8,] 7 5 2 6
# Sum every 4 rows
require( zoo )
tmp <- rollapply( m , width = 4 , by = 4 , align = "left" , FUN = sum )
# [,1] [,2] [,3] [,4]
#[1,] 23 13 30 12
#[2,] 29 24 22 24
You can also use rowSums() on the result if you actually wanted to aggregate the columns into a single value for each of the 4 rows...
rowSums( tmp )
#[1] 78 99
Here is a way to do it :
## Sample data
m <- matrix(1:36, nrow=12)
## Create a "group" index
fac <- (seq_len(nrow(m))-1) %/% 4
## Apply sum
apply(m, 2, function(v) tapply(v, fac, sum))
Sample data :
[,1] [,2] [,3]
[1,] 1 13 25
[2,] 2 14 26
[3,] 3 15 27
[4,] 4 16 28
[5,] 5 17 29
[6,] 6 18 30
[7,] 7 19 31
[8,] 8 20 32
[9,] 9 21 33
[10,] 10 22 34
[11,] 11 23 35
[12,] 12 24 36
Result :
[,1] [,2] [,3]
0 10 58 106
1 26 74 122
2 42 90 138

Resources