Генерация очень большой матрицы комбинаций строк с помощью combn() и пакета bigmemory

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

Я запускаю R на EC2 на экземпляре m1.large с 64-битной Ubuntu. При использовании combn(x, 3) я получаю сообщение об ошибке нехватки памяти:

Error: cannot allocate vector of size 9.0 Gb

Размер результирующей матрицы C1344,3 = 403 716 544 строк и трех столбцов, что является транспонированием результата функции combn().

Я подумал об использовании пакета bigmemory для создания файла с поддержкой big.matrix, чтобы затем я мог назначать результаты функции combn(). Я могу создать предварительно выделенную большую матрицу:

library(bigmemory)
x <- as.character(1:1344)
combos <- 403716544
test <- filebacked.big.matrix(nrow = combos, ncol = 3, 
        init = 0, backingfile = "test.matrix")

Но когда я пытаюсь выделить значения test <- combn(x, 3), я все равно получаю то же самое: Error: cannot allocate vector of size 9.0 Gb

Я даже пытался принудить результат combn(x,3), но я думаю, что поскольку функция combn() возвращает ошибку, функция big.matrix тоже не работает.

test <- as.big.matrix(matrix(combn(x, 3)), backingfile = "abc")
Error: cannot allocate vector of size 9.0 Gb
Error in as.big.matrix(matrix(combn(x, 3)), backingfile = "abc") : 
  error in evaluating the argument 'x' in selecting a method for function 'as.big.matrix'

Есть ли способ объединить эти две функции вместе, чтобы получить то, что мне нужно? Есть ли другие способы добиться этого? Спасибо.


person wahalulu    schedule 20.12.2010    source источник
comment
Рассматривали ли вы возможность использования combinadics для создания каждой комбинации? Я думаю, что у меня есть код R, чтобы сделать это, но мне придется его копать.   -  person Joshua Ulrich    schedule 21.12.2010
comment
@ Джош, я не уверен, что понимаю, чем комбинаторика отличается от того, чего я пытаюсь достичь. В качестве примера я использовал as.character(1:1344), но на самом деле мои значения не являются непрерывными. Если вы считаете, что код R, который у вас есть, может помочь, напишите. Спасибо!   -  person wahalulu    schedule 21.12.2010
comment
@Joshua @Joris @Dirk спасибо за все ваши предложения. Я выбрал подход Джориса из-за нехватки времени (хотя я не знаю, какой из них быстрее). Я даю системе поработать некоторое время для всех 400-метровых линий. @ Джошуа, я думаю, что многие люди найдут много полезного в твоей комбинированной функции.   -  person wahalulu    schedule 22.12.2010


Ответы (3)


Вы можете сначала найти все двусторонние комбинации, а затем просто объединить их с трехмерным значением, сохраняя их каждый раз. Это занимает намного меньше памяти:

combn.mod <- function(x,fname){
  tmp <- combn(x,2,simplify=F)
  n <- length(x)
  for ( i in x[-c(n,n-1)]){
    # Drop all combinations that contain value i
    id <- which(!unlist(lapply(tmp,function(t) i %in% t)))
    tmp <- tmp[id]
    # add i to all other combinations and write to file
    out <- do.call(rbind,lapply(tmp,c,i))
    write(t(out),file=fname,ncolumns=3,append=T,sep=",")
  }
}

combn.mod(x,"F:/Tmp/Test.txt")

Это не такой общий ответ, как ответ Джошуа, он специально для вашего случая. Я предполагаю, что это быстрее, опять же, для этого конкретного случая, но я не сравнивал. Функция работает на моем компьютере, используя немногим более 50 МБ (приблизительно) применительно к вашему x.

РЕДАКТИРОВАТЬ

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

ПОДТВЕРЖДЕНИЕ КОНЦЕПЦИИ:

Я изменил строку записи на tt[[i]]<-out, добавил tt <- list() перед циклом и return(tt) после него. Потом:

> do.call(rbind,combn.mod(letters[1:5]))
      [,1] [,2] [,3]
 [1,] "b"  "c"  "a" 
 [2,] "b"  "d"  "a" 
 [3,] "b"  "e"  "a" 
 [4,] "c"  "d"  "a" 
 [5,] "c"  "e"  "a" 
 [6,] "d"  "e"  "a" 
 [7,] "c"  "d"  "b" 
 [8,] "c"  "e"  "b" 
 [9,] "d"  "e"  "b" 
[10,] "d"  "e"  "c" 
person Joris Meys    schedule 20.12.2010

Вот функция, которую я написал на R, которая в настоящее время находит свой (неэкспортированный) дом в LSPM. пакет. Вы указываете общее количество элементов n, количество элементов для выбора r и индекс желаемой комбинации i; он возвращает значения в 1:n, соответствующие комбинации i.

".combinadic" <- function(n, r, i) {

  # http://msdn.microsoft.com/en-us/library/aa289166(VS.71).aspx
  # http://en.wikipedia.org/wiki/Combinadic

  if(i < 1 | i > choose(n,r)) stop("'i' must be 0 < i <= n!/(n-r)!")

  largestV <- function(n, r, i) {
    #v <- n-1
    v <- n                                  # Adjusted for one-based indexing
    #while(choose(v,r) > i) v <- v-1
    while(choose(v,r) >= i) v <- v-1        # Adjusted for one-based indexing
    return(v)
  }

  res <- rep(NA,r)
  for(j in 1:r) {
    res[j] <- largestV(n,r,i)
    i <- i-choose(res[j],r)
    n <- res[j]
    r <- r-1
  }
  res <- res + 1
  return(res)
}

Он позволяет генерировать каждую комбинацию на основе значения лексикографического индекса:

> .combinadic(1344, 3, 1)
[1] 3 2 1
> .combinadic(1344, 3, 2)
[1] 4 2 1
> .combinadic(1344, 3, 403716544)
[1] 1344 1343 1342

Так что вам просто нужно перебрать 1:403716544 и добавить результаты в файл. Это может занять некоторое время, но это, по крайней мере, осуществимо (см. Ответ Дирка). Вам также может понадобиться сделать это в несколько циклов, так как вектор 1:403716544 не помещается в памяти на моей машине.

Или вы можете просто перенести код R на C/C++ и выполнять цикл/запись там, так как это будет намного быстрее.

person Joshua Ulrich    schedule 20.12.2010
comment
Спасибо, Джош. Я попробую и дам вам знать, как это работает. - person wahalulu; 21.12.2010
comment
прекрасная реализация! Я должен был хорошенько взглянуть на то, как это работает, но это жемчужина наверняка. - person Joris Meys; 21.12.2010
comment
@Joris Спасибо Джеймсу Маккефри. Я нашел реализацию на MSDN. Честно говоря, мне потребовалось несколько дней, чтобы понять это и другие неэкспортированные комбинаторные функции в LSPM (также из MSDN). - person Joshua Ulrich; 21.12.2010
comment
Отличная работа, для тех, кто пытается понять, как заставить его работать со строками, вам нужно вычислить n0 <- length(n) и заменить любые n на n0 и в конце использовать n[res] для получения значений такой комбинации. - person llrs; 03.08.2016
comment
Кроме того, при использовании этого для заполнения матрицы расстояний он в основном возвращает значения матрицы расстояний верхнего треугольника по столбцам. - person chasemc; 05.10.2018

В первом приближении каждый алгоритм жертвует памятью ради скорости.

Вы натолкнулись на границу, пытаясь предварительно выделить полностью пронумерованную комбинационную матрицу. Так что, возможно, вам следует попробовать не предварительно выделять эту матрицу, а попробовать, скажем,

  1. Если вы считаете, что вам нужны комбинации, вычислите их где-нибудь еще и сохраните их в простой базе данных (или, черт возьми, в плоском файле) и найдите их — сэкономлено 9 ГБ.

  2. Воспользуйтесь преимуществом открытого исходного кода, прочитайте код combn() и измените его на клиент-сервер: при вызове с порядковым номером N он зациклится и вернет < em>N-я запись. Неэффективно, но, возможно, проще осуществимо.

person Dirk Eddelbuettel    schedule 20.12.2010
comment
Я не хочу иметь матрицу в памяти и не хочу ничего с ней делать в R. Я просто хочу сгенерировать значения и создать плоский файл, который затем становится входом для задания mapreduce, где бы ни была строка. уникальный случай для моделирования, которое я запускаю. Я думал о том, чтобы посмотреть код для combn() и, возможно, изменить его, чтобы играть с big.matrix, у меня просто не было времени. Когда вы предлагаете вычислить их где-то еще (1 выше), что бы вы порекомендовали? - person wahalulu; 21.12.2010
comment
Вы даже можете сделать сам R: взять функцию, но вместо того, чтобы записывать их в одну большую матрицу, постепенно записывать в файл или базу данных. - person Dirk Eddelbuettel; 21.12.2010