Случайный выбор значений из существующей матрицы после добавления вектора (в R) - PullRequest
5 голосов
/ 28 июля 2011

Заранее большое спасибо за вашу помощь!

Я пытаюсь изменить существующую матрицу таким образом, чтобы при добавлении новой строки в матрицу она удаляла значения из ранее существовавшей матрицы.

Например, у меня есть матрица:

[,1] [,2] [,3] [,4]
 1     1    0    0
 0     1    0    0
 1     0    1    0
 0     0    1    1

Я хочу добавить еще один вектор, I.vec, который имеет два значения (I.vec=c(0,1,1,0)).Это достаточно легко сделать.Я просто привязываю это к матрице.Теперь для каждого столбца, где I.vec равен 1, я хочу случайным образом выбрать значение из других строк и сделать его нулевым.В идеале это должно получиться с такой матрицей:

[,1] [,2] [,3] [,4]
 1     0    0    0
 0     1    0    0
 1     0    0    0
 0     0    1    1
 0     1    1    0

Но каждый раз, когда я запускаю итерацию, я хочу, чтобы она снова производила случайную выборку.

Итак, вот что я попробовал:

mat1<-matrix(c(1,1,0,0,0,1,0,0,1,0,1,0,0,0,1,1),byrow=T, nrow=4)
I.vec<-c(0,1,1,0)
mat.I<-rbind(mat1,I.vec)
mat.I.r<-mat.I
d1<-mat.I[,which(mat.I[5,]==1)]
mat.I.r[sample(which(d1[1:4]==1),1),which(mat.I[5,]==1)]<-0

Но это удаляет только одно из двух значений, которые я хотел бы удалить.Я также пробовал варианты подмножества матрицы, но у меня ничего не получилось.

Еще раз спасибо!

Ответы [ 2 ]

4 голосов
/ 28 июля 2011

В описании от OP есть некоторая двусмысленность, поэтому предлагаются два решения:

При условии, что только существующие 1 s в соответствующих столбцах могут быть установлены на 0

Я просто изменю исходную функцию (см. Ниже).Изменение относится к строке, определяющей rows.Теперь у меня есть (была ошибка в оригинале - версия ниже пересмотрена, чтобы справиться с ошибкой):

rows <- sapply(seq_along(cols), 
                   function(x, mat, cols) {
                       ones <- which(mat[,cols[x]] == 1L)
                       out <- if(length(ones) == 1L) {
                                  ones
                              } else {
                                  sample(ones, 1)
                       }
                       out
                   }, mat = mat, cols = cols)

По сути, это то, что для каждого столбца нам нужно поменять местами 1 до 0, мы выясняем, какие строки столбца содержат 1 с, и выбираем один из них.

Редактировать : Мы должны обработать случай, когда естьтолько один 1 в столбце.Если мы просто сэмплируем из вектора длины 1, R sample() будет обрабатывать его так, как если бы мы хотели сэмплировать из набора seq_len(n), а не из набора длины 1 n.Теперь мы обрабатываем это с помощью оператора if, else.

Мы должны сделать это индивидуально для каждого столбца, чтобы получить правильные строки.Я полагаю, что мы могли бы сделать некоторые приятные манипуляции, чтобы избежать повторных вызовов which() и sample(), но как это ускользает от меня на данный момент, потому что мы должны обработать случай, когда в столбце есть только один 1.Вот готовая функция (обновленная для обработки ошибки образца длины 1 в оригинале):

foo <- function(mat, vec) {
    nr <- nrow(mat)
    nc <- ncol(mat)

    cols <- which(vec == 1L)
    rows <- sapply(seq_along(cols), 
                   function(x, mat, cols) {
                       ones <- which(mat[,cols[x]] == 1L)
                       out <- if(length(ones) == 1L) {
                                  ones
                              } else {
                                  sample(ones, 1)
                              }
                       out
                   }, mat = mat, cols = cols)

    ind <- (nr*(cols-1)) + rows
    mat[ind] <- 0

    mat <- rbind(mat, vec)
    rownames(mat) <- NULL

    mat
}

, и вот она в действии:

> set.seed(2)
> foo(mat1, ivec)
     [,1] [,2] [,3] [,4]
[1,]    1    0    0    0
[2,]    0    1    0    0
[3,]    1    0    1    0
[4,]    0    0    0    1
[5,]    0    1    1    0

, и она работает, когда есть толькоодин 1 в столбце, в который мы хотим выполнить обмен:

> foo(mat1, c(0,0,1,1))
     [,1] [,2] [,3] [,4]
[1,]    1    1    0    0
[2,]    0    1    0    0
[3,]    1    0    1    0
[4,]    0    0    0    1
[5,]    0    0    1    1

Оригинальный ответ: Предполагая, что любое значение в соответствующем столбце может быть установлено равным нулю

Вот векторизованный ответ, где мы рассматриваем матрицу как вектор при выполнении замены.Используя данные примера:

mat1 <- matrix(c(1,1,0,0,0,1,0,0,1,0,1,0,0,0,1,1), byrow = TRUE, nrow = 4)
ivec <- c(0,1,1,0)

## Set a seed to make reproducible
set.seed(2)

## number of rows and columns of our matrix
nr <- nrow(mat1)
nc <- ncol(mat1)

## which of ivec are 1L
cols <- which(ivec == 1L)

## sample length(cols) row indices, with replacement
## so same row can be drawn more than once
rows <- sample(seq_len(nr), length(cols), replace = TRUE)

## Compute the index of each rows cols combination
## if we treated mat1 as a vector
ind <- (nr*(cols-1)) + rows
## ind should be of length length(cols)

## copy for illustration
mat2 <- mat1

## replace the indices we want with 0, note sub-setting as a vector
mat2[ind] <- 0

## bind on ivec
mat2 <- rbind(mat2, ivec)

Это дает нам:

> mat2
     [,1] [,2] [,3] [,4]
        1    0    0    0
        0    1    0    0
        1    0    0    0
        0    0    1    1
ivec    0    1    1    0

Если бы я делал это более одного или двух раз, я бы обернул это в функцию:

foo <- function(mat, vec) {
    nr <- nrow(mat)
    nc <- ncol(mat)

    cols <- which(vec == 1L)
    rows <- sample(seq_len(nr), length(cols), replace = TRUE)

    ind <- (nr*(cols-1)) + rows
    mat[ind] <- 0

    mat <- rbind(mat, vec)
    rownames(mat) <- NULL

    mat
}

Что дает:

> foo(mat1, ivec)
     [,1] [,2] [,3] [,4]
[1,]    1    1    0    0
[2,]    0    1    0    0
[3,]    1    0    1    0
[4,]    0    0    0    1
[5,]    0    1    1    0

Если вы хотите сделать это в течение нескольких ivec с, увеличиваясь mat1 каждый раз, то вы, вероятно, не хотите делать это вЦикл растущих объектов идет медленно (он включает в себя копии и т. д.).Но вы можете просто изменить определение ind, добавив в него дополнительные строки n, которые вы привязываете для n ivec s.

1 голос
/ 28 июля 2011

Вы можете попробовать что-то вроде этого.Наличие nrow позволит вам запускать его несколько раз вместе с другими I.vec.Я пытался сделать это в одной строке с «apply», но не смог заставить матрицу появляться снова.

mat1<-matrix(c(1,1,0,0,0,1,0,0,1,0,1,0,0,0,1,1),byrow=T, nrow=4)
I.vec<-c(0,1,1,0)
mat.I.r<-rbind(mat1,I.vec)

for(i in 1:ncol(mat.I.r))
  {
  ifelse(mat.I.r[nrow(mat.I.r),i]==1, mat.I.r[sample(which(mat.I.r[1:(nrow(mat.I.r)-1),i]==1),1), i] <- 0, "")
  }
mat.I.r
...