【问题标题】:Large matrix algebra calculations in rubyruby 中的大型矩阵代数计算
【发布时间】:2026-02-27 08:55:01
【问题描述】:

我正在从事一个涉及使用大型数据矩阵进行计算的项目。我有 10,000 行和 100 列的 CSV 文件,其中有 10 个。目前,我正在运行一个后台作业,该作业从每个 CSV 读取数据,将其拉入一个数组,对数据运行一些矩阵乘法计算,然后移动到下一个 CSV。我确信有更好的方法来做到这一点,因为处理工作所需的大部分时间似乎都花在了打开 CSV 上。我的问题实际上归结为我应该如何存储当前在这些 CSV 文件中的数据,以便轻松访问它并以更有效的方式运行计算。任何帮助将不胜感激

编辑

根据 cmets 的建议,我想补充一点,矩阵密度是 100%,数字都是浮点数。

【问题讨论】:

  • 您的 CSV 文件有多大(以 MB 为单位,而不是行/列)。
  • 是否所有单元格值都在 CSV 数字中?
  • 您是否考虑将非零值存储在数据库中,每条记录为row, col, value?对矩阵进行操作,你看过Matrix class吗?还有一些矩阵运算的gem,比如NMatrix
  • 每个 CSV 文件大约 12 mb。这些值都是数字。
  • 对于矩阵运算,我使用的是 Matrix 类。效果很好。我想存储在数据库中是一种选择,尽管在 Heroku 上可能需要昂贵的数据库计划,因为这需要数百万行。

标签: ruby csv matrix


【解决方案1】:

CSV 对于任何类型的大数据都是一种非常、非常低效的格式。鉴于您的所有数据都是数字,并且您的数据大小是一致的,因此最好使用紧凑的二进制格式。如果您以网络字节顺序将数据存储为 1,000,000 个 4 字节整数的二进制文件,其中前一百个是第一行,第二个是第二行,依此类推,它会将您的文件大小从 12MB 减少到 ~8MB,并且完全消除解析 CSV 的低效率(这真的是低效的)。要将您的数据转换为这种格式,请尝试运行此 Ruby 代码(我假设数据是 CSV 的二维数组):

newdat = data.flatten.map {|e| e.to_f}.pack("G*")

然后将 newdat 作为新数据写入文件:

f = File.open("data.dat", 'wb')
f.write(newdat)
f.close

从文件中解析这些数据:

data = File.open("data.dat", 'rb').read.unpack("G*").each_slice(100).to_a

这会将数据设置为二维数组。

注意:我实际上无法为您提供确切的数字来说明效率,因为我没有任何巨大的 CSV 文件,里面到处都是浮点数。但是,这应该更有效率。

【讨论】:

  • 看起来 CSV 中的数据实际上是作为字符串存储的。在运行您建议的代码之前,有没有一种快速转换为浮点数的方法?
  • @dvanderb:现在就试试吧。
  • 接近。这有效,但我收到以下错误:当我尝试将 newdat 写入文件时,Encoding::UndefinedConversionError: "\x93" from ASCII-8BIT to UTF-8
  • @dvanderb:你必须以'wb'模式打开文件。
  • 更近了。现在我在尝试解析时得到了这个: data = File.read("data.dat", 'rb').unpack("G*").each_slice(100).to_a TypeError: can't convert String into Integer
【解决方案2】:

您是否考虑过使用Marshal 将数组保存为二进制?我没用过,但看起来很简单:

FNAME = 'matrix4.mtx'
a = [2.3, 1.4, 6.7]

File.open(FNAME, 'wb') {|f| f.write(Marshal.dump(a))}
b = Marshal.load(File.binread(FNAME)) # => [2.3,1.4,6.7]

当然,您必须将整个数组读入内存,但按照当前标准,数组似乎没有那么大。

【讨论】:

  • 好建议。 Marshal 往往是将 Ruby 数据转储到文件并重新读入的最快方法。但是,如果数据必须由非 Ruby 工具读取,则不是很好。
  • 这似乎也有效。我真的不知道哪个更快(这个或公认的解决方案),但对于我的应用程序而言,两者似乎都足够快。感谢您的帮助。
【解决方案3】:

您始终可以将文件加载到 NMatrix 中,然后使用 NMatrix#write 以 NMatrix 二进制格式保存。 NMatrix 仍然需要一个 CSV 读取器和写入器,但我猜它的实现非常简单——或者你可以在 issue tracker 中请求它。

x.write("mymatrix.binary")

及以后:

y = NMatrix.read("mymatrix.binary")
# => NMatrix

它可以处理密集和稀疏存储。

【讨论】:

    最近更新 更多