【问题标题】:Conversion to FEATHER file creates huge file转换为 FEATHER 文件会创建巨大的文件
【发布时间】:2022-01-27 08:22:12
【问题描述】:

我正在尝试将.rds file 转换为.feather file,以便在Python 中使用Pandas 阅读。

library(feather)

# Set working directory
data = readRDS("file.rds")
data_year = data[["1986"]]

# Try 1
write_feather(
  data_year,
  "data_year.feather"
  )

# Try 2
write_feather(
  as.data.frame(as.matrix(data_year)),
  "data_year.feather"
)

Try 1 返回 Error: 'x' must be a data frame,而 Try 2 实际上写入了一个 *.feather 文件,但该文件一年的大小为 4.5GB,而原始 @ 987654337@ 文件的大小为 0.055GB 多年。

如何将文件转换为每年单独或非单独的*.feather 文件,同时保持足够的文件大小?

data 看起来像这样:

data_year 看起来像这样:

*更新

我愿意接受任何建议,以使数据可用于 NumPy/Pandas,同时保持适度的文件大小!

【问题讨论】:

  • data_year 的长度为 576M,但它是 dgCMatrix 类的稀疏矩阵。当强制到 data.frame 时,它​​会变大,我没有看到避免它的方法。
  • 非常感谢您的评论!是否有任何其他方法可以使数据在 NumPy/Pandas 中可用,同时保持适度的文件大小?
  • 你可以在here找到你的答案。
  • @ErfanGhasemi 谢谢您的评论。 pyreadr.read_r('file.rds') 返回LibrdataError: The file contains an unrecognized object。用户 mgalardini 通过您的链接的答案返回一个列表向量,其中每个项目都是一个 RS4 object。我不知道那是什么。当然不是熊猫数据框。我无法通过您提供的链接找到答案。
  • 也许不是从稀疏矩阵转换,您可以查看是否是一种 python 方法,以Matrix::writeMM 写出的稀疏矩阵格式读取编辑:您可以尝试使用docs.scipy.org/doc/scipy-0.14.0/reference/generated/…docs.scipy.org/doc/scipy/reference/generated/… 取决于您使用 R 写出的格式

标签: python r feather


【解决方案1】:

也许像下面这样的函数会有所帮助。

该函数将稀疏矩阵重塑为长格式,消除其中的零。这将减少最终的 data.frame 大小和磁盘文件大小。

library(Matrix)
library(feather)

dgcMatrix_to_long_df <- function(x) {
  res <- NULL
  if(nrow(x) > 0L) {
    for(i in 1:nrow(x)){
      d <- as.matrix(x[i, , drop = FALSE])
      d <- as.data.frame(d)
      d$row <- i
      d <- tidyr::pivot_longer(d, cols = -row, names_to = "col")
      d <- d[d$value != 0,]
      res <- rbind(res, d)
    }
  }
  res
}

y <- dgcMatrix_to_long_df(data_year)
head(y)
## A tibble: 6 x 3
#    row col      value
#  <int> <chr>    <dbl>
#1     1 Col_0103    51
#2     1 Col_0149     6
#3     1 Col_0188     5
#4     1 Col_0238    89
#5     1 Col_0545    14
#6     1 Col_0547    58


path <- "my_data.feather"
write_feather(y, path)
z <- read_feather(path)
identical(y, z)
#[1] TRUE

# The file size is 232 KB though the initial matrix
# had 1 million elements stored as doubles, 
# for a total of 8 MB, a saving of around 97%
file.size(path)/1024
#[1] 232.0234

编辑

下面的函数要快得多。

dgcMatrix_to_long_df2 <- function(x) {
  res <- NULL
  if(nrow(x) > 0L) {
    for(i in 1:nrow(x)){
      d <- as.matrix(x[i, , drop = FALSE])
      inx <- which(d != 0, arr.ind = TRUE)
      d <- cbind(inx, value = c(d[d != 0]))
      d[, "row"] <- i
      res <- rbind(res, d)
    }
  }
  as.data.frame(res)
}

system.time(y <- dgcMatrix_to_long_df(data_year))
#   user  system elapsed 
#   7.89    0.04    7.92 
system.time(y <- dgcMatrix_to_long_df2(data_year))
#   user  system elapsed 
#   0.14    0.00    0.14

测试数据

set.seed(2022)
n <- 1e3
x <- rep(0L, n*n)
inx <- sample(c(FALSE, TRUE), n*n, replace = TRUE, prob = c(0.99, 0.01))
x[inx] <- sample(100, sum(inx), replace = TRUE)
data_year <- Matrix(x, n, n, dimnames = list(NULL, sprintf("Col_%04d", 1:n)))

【讨论】:

  • 非常感谢您为此付出的努力。我需要一个安静的时间来彻底查看答案,并会尽快发表评论。谢谢!
  • 再次,非常感谢您的回答!我跑了test_data = readRDS("file.rds"),然后跑了test_year = dgcMatrix_to_long_df2(test_data[["1986"]])。如果我理解正确,结果是data.frame 的行和列索引为非零值。不幸的是,这使得结构的导入和“重新创建”有点棘手。我希望有一个 simple 导出/导入选项,但我想这根本不存在。 ...
  • ps Matrix 有一个汇总方法(我认为这是你在这里所做的)例如summary(data_year)
【解决方案2】:

使用scipyrpy2,您可以将每个dgCMatrix 对象作为scipy.sparse.csc_matrix 对象直接读入Python。两者都使用compressed sparse column (CSC) 格式,因此实际上 需要预处理。您需要做的就是将dgCMatrix 对象的属性作为参数传递给csc_matrix 构造函数。

为了测试它,我使用 R 创建了一个 RDS 文件,其中存储了 dgCMatrix 对象的列表:

library("Matrix")
set.seed(1L)

d <- 6L
n <- 10L
l <- replicate(n, sparseMatrix(i = sample(d), j = sample(d), x = sample(d), repr = "C"), simplify = FALSE)
names(l) <- as.character(seq(1986L, length.out = n))

l[["1986"]]
## 6 x 6 sparse Matrix of class "dgCMatrix"
##                 
## [1,] . . 5 . . .
## [2,] 3 . . . . .
## [3,] . . . . . 6
## [4,] . 2 . . . .
## [5,] . . . . 1 .
## [6,] . . . 4 . .

saveRDS(l, file = "list_of_dgCMatrix.rds")

然后,在 Python 中:

from scipy import sparse
from rpy2  import robjects
readRDS = robjects.r['readRDS']

l = readRDS('list_of_dgCMatrix.rds')
x = l.rx2('1986') # in R: l[["1986"]]
x
## <rpy2.robjects.methods.RS4 object at 0x120db7b00> [RTYPES.S4SXP]
## R classes: ('dgCMatrix',)

print(x)
## 6 x 6 sparse Matrix of class "dgCMatrix"
##                 
## [1,] . . 5 . . .
## [2,] 3 . . . . .
## [3,] . . . . . 6
## [4,] . 2 . . . .
## [5,] . . . . 1 .
## [6,] . . . 4 . .

data    = x.do_slot('x')   # in R: x@x
indices = x.do_slot('i')   # in R: x@i
indptr  = x.do_slot('p')   # in R: x@p
shape   = x.do_slot('Dim') # in R: x@Dim or dim(x)

y = sparse.csc_matrix((data, indices, indptr), tuple(shape))
y
## <6x6 sparse matrix of type '<class 'numpy.float64'>'
##         with 6 stored elements in Compressed Sparse Column format>

print(y)
##   (1, 0)       3.0
##   (3, 1)       2.0
##   (0, 2)       5.0
##   (5, 3)       4.0
##   (4, 4)       1.0
##   (2, 5)       6.0

这里,yscipy.sparse.csc_matrix 类的对象。您不需要使用toarray 方法将y 强制转换为具有密集存储的阵列。 scipy.sparse 实现了我能想象到的所有矩阵运算。比如这里是y的行列总和:

y.sum(1) # in R: as.matrix(rowSums(x))
## matrix([[5.],
##         [3.],
##         [6.],
##         [2.],
##         [1.],
##         [4.]])

y.sum(0) # in R: t(as.matrix(colSums(x)))
## matrix([[3., 2., 5., 4., 1., 6.]])

【讨论】:

  • 太好了,谢谢!这正是我需要的 + 您还提到的最后一步 array = sparse.csc_matrix.toarray(y)。谢谢!
  • 我试图表达toarray 将消耗大量内存给定矩阵的大小。使用scipy.sparse 中提供的稀疏矩阵方法,您也许可以在没有toarray 的情况下做您需要的事情。当然,决定权在你。很高兴这有帮助。
猜你喜欢
  • 2012-09-13
  • 1970-01-01
  • 2017-03-20
  • 2023-03-24
  • 1970-01-01
  • 2020-11-06
  • 1970-01-01
  • 1970-01-01
  • 2016-02-29
相关资源
最近更新 更多