使用R生成稀疏矩阵

5

我有一个大文件,格式如下,我将其读入为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
...

这个功能提供了用户购买的产品和其频率。我想将其转换为矩阵形式,其中所有产品ID作为列,用户ID作为行,频率值作为条目。因此,期望的输出如下:

       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大小的向量,所以不明白为什么它无法处理文件大小。我有4000万行,总文件大小为741M。提前致谢。


如果您想要真正的稀疏矩阵,那么请查看“Matrix”包。 - IRTFM
你尝试过使用aggregate(freq ~ userid + productid,data = df,sum)吗? - Andy Clifton
或者 library(tidyr); spread(x,productid,freq,fill = 0) - AndrewMacDonald
一直在尝试使用“aggregate”,但速度非常慢。 - broccoli
聚合操作没有得到期望的结果。 - broccoli
3个回答

3

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

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


2
我得到了以下错误 Error in dcast.data.table(x, deviceid ~ cxbrandid, fun = sum, fill = 0L) : long vectors not supported yet: ../../src/include/Rinlinedfuns.h:137 此外:警告信息: In setattr(l, "row.names", .set_row_names(length(l[[1L]]))) : 强制转换时引入了NAs - broccoli
@broccoli 或许你可以提供一个 dput 样本来展示你的数据 - 对于 OP 中的数据它是有效的。 - eddi
该方法适用于小数据集,但在我在问题中提到的规模下,它会出现上面显示的错误消息。 - broccoli
@broccoli 也许在执行 dcast 操作之前进行聚合操作会更有帮助?例如,可以尝试执行以下代码:dcast.data.table(dt[, sum(freq), by = list(userid, productid)], userid ~ productid, fill = 0L) - eddi
不行,不起作用。问题“长向量尚未支持:”让我感到很奇怪,因为我正在使用的是R 3.1.0版本,应该支持长向量高达2^51或类似的大小。对吧? - broccoli
我不知道,但我相信@Arun知道 :) - eddi

1
#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.

0
这是一个关于tidyr的方法:
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 并不能正常工作。这似乎是一个已知问题,不幸的是,它仍未得到解决。

我尝试了在大规模数据(200万条记录)上使用 tidyr 方法,但无法在我的本地机器上运行。因此,您需要将其分割(然后使用 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)
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接