【问题标题】:How to turn a list of lists to a sparse matrix in R without using lapply?如何在不使用 lapply 的情况下将列表列表转换为 R 中的稀疏矩阵?
【发布时间】:2011-09-26 23:57:39
【问题描述】:

我有一个由 bigsplit() 操作产生的列表列表(来自 biganalytics 包,是 bigmemory 包的一部分)。

每个列表代表矩阵中的一列,每个列表项是二进制矩阵中值 1 的索引。

将此列表转换为稀疏二进制 (0/1) 矩阵的最佳方法是什么? 在 lapply() 中使用 lapply() 是唯一的解决方案吗?如何将命名列表的因素保留为列的名称?

【问题讨论】:

  • 如果你能提供一些示例数据,如果可能的话以可运行代码的形式,那会让生活变得更轻松。所以你有 list(col1=list(1,4),col2=list(2,6,8) 之类的?
  • 我的反应很慢,但您的回答非常好 - 谢谢!这正是我想要的。

标签: r sparse-matrix


【解决方案1】:

如果你需要一个矩阵,你可以在没有任何 lapply 的情况下做到这一点。

假设你有一个这样构造的列表:

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

【讨论】:

  • +1 这很聪明。很好地提醒您,您可以使用一维索引向量来分配(或提取)矩阵。此外,与其他涉及重复 cbind 操作(例如使用 do.call( cbind, lapply(...)) )的自然想到的方法相比,填充预分配矩阵的速度要快得多。
【解决方案2】:

您也可以考虑使用 Matrix 包,它以比基本 R 更有效的方式处理大型稀疏矩阵。您可以通过描述哪些行和列应该是 1 来构建一个 0 和 1 的稀疏矩阵。

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,] . | .

【讨论】:

  • 正是我想要的。主要技巧是使用n.ids &lt;- sapply(Test,length)` 然后rep(seq_along(n.ids), n.ids) 在矩阵中创建索引。谢谢!
  • rep(seq_along(n.ids), n.ids) 是天才。谢谢。
【解决方案3】:

使用 Joris 的示例,这是一个使用 sapply/replace 的语法简单方法。我怀疑 Joris 的方法更快,因为它填充了一个预先分配的矩阵,而我的方法隐含地涉及 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

【讨论】:

  • 如果你有一个向量列表,我的电脑上的时间表明你的速度更快。但是您的代码返回错误:Error in x[list] &lt;- values : invalid subscript type 'list'。您必须将 c(x) 更改为 unlist(x)。
  • @Joris -- 实际上我确实首先有unlist(x),然后出于某种原因,当我将其更改为c(x) 时我认为它起作用了......我想我在剪辑中搞砸了一些东西/粘贴。现在我确实收到了c(x) 的错误。
【解决方案4】:

这里有一些样本数据似乎符合您的描述。

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

【讨论】:

    【解决方案5】:

    要建立在 Joris 的答案(使用标量索引向量来填充输出矩阵)的基础上,您还可以使用矩阵索引向量来填充输出矩阵;这有时可能会更清楚一些,以便以后编写或理解。

    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)
    

    结果是一样的。

    【讨论】:

      猜你喜欢
      • 2015-07-24
      • 1970-01-01
      • 2016-12-05
      • 2021-07-02
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-09
      相关资源
      最近更新 更多