Moving rows between subarrays

后端 未结 1 1244
轻奢々
轻奢々 2021-01-26 05:19

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

相关标签:
1条回答
  • 2021-01-26 06:03

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

    0 讨论(0)
提交回复
热议问题