Как превратить список списков в разреженную матрицу в R без использования lapply?

У меня есть список списков, полученных в результате операции bigsplit() (из пакета biganalytics, части пакетов bigmemory).

Каждый список представляет столбец в матрице, а каждый элемент списка является индексом значения 1 в двоичной матрице.

Каков наилучший способ превратить этот список в разреженную двоичную (0/1) матрицу? Является ли использование lapply() в lapply() единственным решением? Как сохранить факторы, именующие списки, как имена столбцов?


person Ron    schedule 09.02.2011    source источник
comment
Если бы вы предоставили некоторые примеры данных, если это возможно, в виде исполняемого кода, это сделало бы жизнь намного проще. Итак, у вас есть list(col1=list(1,4),col2=list(2,6,8), что-то в этом роде?   -  person Joris Meys    schedule 09.02.2011
comment
Я очень медленно отвечал, но ваши ответы были превосходны - спасибо! Это было именно то, что я искал.   -  person Ron    schedule 10.02.2011


Ответы (5)


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

library(Matrix)
Test <- list(
    col1=list(2,4,7),
    col2=list(3,2,6,8),
    col3=list(1,4,5,3,7)
)
n.ids <- sapply(Test,length)
vals <- unlist(Test)
out <- sparseMatrix(vals, rep(seq_along(n.ids), n.ids))

Результат

> out
8 x 3 sparse Matrix of class "ngCMatrix"

[1,] . . |
[2,] | | .
[3,] . | |
[4,] | . |
[5,] . . |
[6,] . | .
[7,] | . |
[8,] . | .
person Aaron left Stack Overflow    schedule 09.02.2011
comment
Именно то, что я искал. Основная хитрость заключается в использовании n.ids <- sapply(Test,length)`, а затем rep(seq_along(n.ids), n.ids) для создания индексов в матрице. Спасибо! - person Ron; 10.02.2011
comment
rep(seq_along(n.ids), n.ids) гений. Спасибо. - person Zach; 01.05.2013

Вы можете сделать это вообще без лаппли, если вам нужна матрица.

Скажем, у вас есть список, построенный следующим образом:

Test <- list(
    col1=list(2,4,7),
    col2=list(3,2,6,8),
    col3=list(1,4,5,3,7)
)

Сначала вы строите матрицу с нулями правильных размеров. Если вы знаете их заранее, это легко. В противном случае вы можете легко получить:

n.cols <- length(Test)
n.ids <- sapply(Test,length)
n.rows <- max(unlist(Test))
out <- matrix(0,nrow=n.rows,ncol=n.cols)

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

id <- unlist(Test)+rep(0:(n.cols-1),n.ids)*n.rows
out[id] <- 1
colnames(out) <- names(Test)

Это дает :

> out
     col1 col2 col3
[1,]    0    0    1
[2,]    1    1    0
[3,]    0    1    1
[4,]    1    0    1
[5,]    0    0    1
[6,]    0    1    0
[7,]    1    0    1
[8,]    0    1    0
person Joris Meys    schedule 09.02.2011
comment
+1 это довольно умно. Приятное напоминание о том, что вы можете использовать одномерный индексный вектор для присвоения (или извлечения) из матрицы. Кроме того, заполнение предварительно выделенной матрицы происходит значительно быстрее, чем другие подходы, которые естественным образом приходят на ум и включают повторяющиеся операции cbind (например, использование do.call( cbind, lapply(...)) ). - person Prasad Chalasani; 09.02.2011

Используя пример Йориса, вот синтаксически простой способ использования sapply/replace. Я подозреваю, что подход Джориса быстрее, потому что он заполняет предварительно выделенную матрицу, тогда как мой подход неявно включает cbindобработку группы столбцов и поэтому потребует повторного выделения памяти для столбцов (это правда?).

Test <- list( 
col1=list(2,4,7), 
col2=list(3,2,6,8), 
col3=list(1,4,5,3,7) 
) 

> z <- rep(0, max(unlist(Test)))
> sapply( Test, function(x) replace(z,unlist(x),1))
     col1 col2 col3
[1,]    0    0    1
[2,]    1    1    0
[3,]    0    1    1
[4,]    1    0    1
[5,]    0    0    1
[6,]    0    1    0
[7,]    1    0    1
[8,]    0    1    0
person Prasad Chalasani    schedule 09.02.2011
comment
Тайминги на моем компьютере показывают, что у вас быстрее, если у вас есть список векторов. Но ваш код возвращает ошибку: Error in x[list] <- values : invalid subscript type 'list'. Вы должны изменить c(x) на unlist(x). - person Joris Meys; 10.02.2011
comment
@Joris - на самом деле у меня сначала было unlist(x), потом я почему-то подумал, что это сработало, когда я изменил его на c(x)... Думаю, я что-то напутал в своем вырезании/вставке. Теперь я получаю ошибку с c(x). - person Prasad Chalasani; 10.02.2011

Вот некоторые примеры данных, которые, кажется, соответствуют вашему описанию.

a <- as.list(sample(20, 5))
b <- as.list(sample(20, 5))
c <- as.list(sample(20, 5))
abc <- list(a = a, b = b, c = c)

Я не вижу способа сделать это с вложенным lapply(), но есть другой способ. Было бы неплохо устранить unlist(), но, возможно, кто-то еще сможет это улучшить.

sp_to_bin <- function(splist) {
  binlist <- numeric(100)
  binlist[unlist(splist)] <- 1
  return(binlist)
}
bindf <- data.frame(lapply(abc, sp_to_bin))
person J. Win.    schedule 09.02.2011

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

Test <- list(
    col1=list(2,4,7),
    col2=list(3,2,6,8),
    col3=list(1,4,5,3,7)
)

n.cols <- length(Test)
n.ids <- sapply(Test,length)
vals <- unlist(Test)
n.rows <- max(vals)
idx <- cbind(vals, rep(seq_along(n.ids), n.ids))
out <- matrix(0,nrow=n.rows,ncol=n.cols)
out[idx] <- 1
colnames(out) <- names(Test)

Результат тот же.

person Aaron left Stack Overflow    schedule 09.02.2011