【问题标题】:R generating a sparse matrixR生成稀疏矩阵
【发布时间】:2014-06-24 22:43:10
【问题描述】:

我有一个大文件,格式如下:x

userid,productid,freq
293994,8,3
293994,5,3
949859,2,1
949859,1,1
123234,1,1
123234,3,1
123234,4,1
...

它提供给定用户购买的产品及其频率。我正在尝试将其制成一个矩阵,该矩阵将所有产品标识作为列,将用户标识作为行,频率值作为条目。所以预期的输出是

       1 2 3 4 5 8
293994 0 0 0 0 3 3
949859 1 1 0 0 0 0
123234 1 0 1 1 0 0

这是一个稀疏矩阵。我试过做table(x[[1]],x[[2]]),它适用于小文件,但超过一点table会出错

Error in table(x[[1]], x[[2]]) : 
 attempt to make a table with >= 2^31 elements
Execution halted

有没有办法让它工作?我在 R-3.1.0 上,它应该支持 2^51 大小的向量,所以很困惑为什么它不能处理文件大小。我有 40MM 行,总文件大小为 741M。提前致谢

【问题讨论】:

  • 如果您想要真正的稀疏矩阵,请查看“Matrix”包。
  • 你试过aggregate(freq ~ userid + productid,data = df,sum)吗?
  • 或者library(tidyr); spread(x,productid,freq,fill = 0)
  • 一直在尝试aggregate,但速度很慢。
  • 聚合没有给出想要的结果。

标签: r data.table sparse-matrix reshape2


【解决方案1】:

data.table 的一种做法是:

library(data.table)
library(reshape2)

# adjust fun.aggregate as necessary - not very clear what you want from OP
dcast.data.table(your_data_table, userid ~ productid, fill = 0L)

您可以检查这是否适用于您的数据。

【讨论】:

  • 我收到以下错误Error in dcast.data.table(x, deviceid ~ cxbrandid, fun = sum, fill = 0L) : long vectors not supported yet: ../../src/include/Rinlinedfuns.h:137 In addition: Warning message: In setattr(l, "row.names", .set_row_names(length(l[[1L]]))) : NAs introduced by coercion
  • @broccoli 也许您可以提供dput 数据样本 - 它适用于 OP 中的数据
  • 该方法适用于小型数据集,但在我在问题中提到的规模上,它给出了我上面显示的错误消息。
  • @broccoli 也许聚合 beforedcast 会有所帮助吗?即做dcast.data.table(dt[, sum(freq), by = list(userid, productid)], userid ~ productid, fill = 0L)
  • 不行不行。问题“尚不支持长向量:”让我觉得很奇怪,因为我在 R 3.1.0 上,它应该支持长达 2^51 或其他东西的长向量。对吗?
【解决方案2】:
#This is old, but worth noting the Matrix package sparseMatrix() to directly format object without reshaping.

    userid <- c(293994,293994,949859,949859,123234,123234,123234)
    productid <- c(8,5,2,1,1,3,4)
    freq <- c(3,3,1,1,1,1,1)

    library(Matrix)

#The dgCMatrix sparseMatrix is a fraction of the size and builds much faster than reshapeing if the data gets large

    x <- sparseMatrix(i=as.integer(as.factor(userid)),
                      j=as.integer(as.factor(productid)),
                      dimnames = list(as.character(levels(as.factor(userid))),
                                   as.character(levels(as.factor(productid)))
                                   ),
                      x=freq)


#Easily converted to a matrix.
    x <- as.matrix(x)

#Learned this the hard way using recommenderlab (package built on top of Matrix) to build a binary matrix, so in case it helps someone else.

【讨论】:

    【解决方案3】:

    这是一个更整洁的方法:

    library(tidyverse)
    library(magrittr)
    
    # Replicate your example data
    example_data <- matrix(
      c(293994,8,3,
        293994,5,3,
        949859,2,1,
        949859,1,1,
        123234,1,1,
        123234,3,1,
        123234,4,1),
      ncol = 3,
      byrow = TRUE) %>%
      as.data.frame %>%
      set_colnames(c('userid','productid','freq'))
    
    # Convert data into wide format
    spread(example_data, key = productid, value = freq, fill = 0)
    

    spread 将比基本的 R table 操作快很多,但在规模上,data.table 反过来又会轻松胜过 tidyr / dplyr。但是,如上一个答案中所述,data.table 等效 dcast 无法正常工作。这似乎是一个known issue,不幸的是,它仍未解决。

    我大规模尝试了tidyr 方法(2 个 mio 记录)。我无法让它在我的本地机器上工作。所以你要么把它切碎(然后使用rbind),要么把它放到一个集群中(使用rhadoopsparklyr)。

    不过,如果其他人想添加一些东西,下面是可重现的“大数据”示例的代码。

    # Make some random IDs
    randomkey <- function(digits){
      paste(sample(LETTERS, digits, replace = TRUE), collapse = '')
    }
    
    products <- replicate(10, randomkey(20)) %>% unique
    customers <- replicate(500000, randomkey(50)) %>% unique
    
    big_example_data <- data.frame(
      useruid = rep(sample(customers, length(customers), replace = FALSE), 4),
      productid = sample(products, replace = TRUE),
      freq = sample(1:5)
    )
    # 2 mio rows of purchases
    dim(big_example_data)
    # With useruid, productid, freq
    head(big_example_data)
    
    # Test tidyr approach
    system.time(
      big_matrix <- spread(big_example_data, key = productid, value = freq, fill = 0)
    )
    

    【讨论】: