【问题标题】:Python - get column iterator from a file (without reading the whole file)Python - 从文件中获取列迭代器(不读取整个文件)
【发布时间】:2012-09-22 21:58:13
【问题描述】:

我的主要目标是从一个巨大的浮点矩阵计算中位数(按列)。示例:

a = numpy.array(([1,1,3,2,7],[4,5,8,2,3],[1,6,9,3,2]))

numpy.median(a, axis=0)

Out[38]: array([ 1.,  5.,  8.,  2.,  3.])

矩阵太大,无法放入 Python 内存(约 5 TB),因此我将其保存在 csv 文件中。 所以我想遍历每一列并计算中位数。

有什么方法可以让我在不读取整个文件的情况下获取列迭代器?

关于计算矩阵中位数的任何其他想法也很好。谢谢!

【问题讨论】:

标签: python numpy median


【解决方案1】:

如果您可以将每一列放入内存中(您似乎暗示可以),那么这应该可以:

import itertools
import csv

def columns(file_name):
   with open(file_name) as file:
       data = csv.reader(file)
       columns = len(next(data))
   for column in range(columns):
       with open(file_name) as file:
           data = csv.reader(file)
           yield [row[column] for row in data]

这通过找出我们有多少列,然后遍历文件,从每一行中取出当前列的项目来工作。这意味着,我们最多一次使用列的大小加上一行内存的大小。这是一个非常简单的生成器。请注意,我们必须不断重新打开文件,因为我们在循环遍历它时会耗尽迭代器。

【讨论】:

  • 如果重新打开文件有问题,只需将 with 移到 for 循环之外,然后将 file.seek(0) 移到里面。
  • @MuMind 这是一次又一次重新打开的好选择(也意味着你可以传递一个文件对象,以防你因为某种原因没有文件名)。
【解决方案2】:

我会通过初始化 N 个空文件来做到这一点,每列一个。然后一次读取一行矩阵并将每个列条目发送到正确的文件。处理完整个矩阵后,返回并依次计算每个文件的中位数。

这基本上使用文件系统进行矩阵转置。转置后,计算每行的中位数很容易。

【讨论】:

  • 感谢您的回复!我的矩阵大小约为 5 TB,恐怕我没有足够的存储空间来执行此操作:(
【解决方案3】:

可能没有直接的方法可以使用 csv 文件执行您所要求的操作(除非我误解了您的意思)。问题在于,除非文件专门设计为具有固定宽度的行,否则任何文件都没有“列”的意义。 CSV 文件通常不是这样设计的。在磁盘上,它们只不过是一个巨大的字符串:

>>> import csv
>>> with open('foo.csv', 'wb') as f:
...     writer = csv.writer(f)
...     for i in range(0, 100, 10):
...         writer.writerow(range(i, i + 10))
... 
>>> with open('foo.csv', 'r') as f:
...     f.read()
... 
'0,1,2,3,4,5,6,7,8,9\r\n10,11,12,13,14,15,16,17,18,19\r\n20..(output truncated)..

如您所见,列字段无法按预期排列;第二列从索引 2 开始,但在下一行中,列的宽度增加了 1,从而导致对齐失效。当输入长度变化时,情况会更糟。结果是 csv 阅读器将不得不读取整个文件,丢弃您不使用的数据。 (如果你不介意,那就是答案——逐行读取整个文件,丢弃你不会使用的数据。)

如果您不介意浪费一些空间并且知道您的数据不会超过某个固定宽度,您可以创建一个具有固定宽度字段的文件,然后您可以使用偏移量来查找它。但是,一旦你这样做了,你还不如开始使用真正的数据库。 PyTables 似乎是许多人最喜欢存储 numpy 数组的选择。

【讨论】:

  • +1 如果您要多次执行此操作,CSV 是保存它的一种糟糕的格式选择。
  • @senderle DB 是我的目标。你知道 numpy.loadtxt(file_path, usecols=[1,2,3]) 现在能不能解决问题?
  • @dbaron,这取决于你所说的“做这个把戏”是什么意思。我很确定usecols=[1, 2, 3] 会避免一次将整个矩阵加载到内存中,所以从这个意义上说,是的。我也很确定它会逐行读取整个文件,丢弃未使用的数据,所以从这个意义上说,不会。
【解决方案4】:

您可以使用 bucketsort 对磁盘上的每一列进行排序,而无需将它们全部读入内存。然后你可以简单地选择中间值。

或者您可以使用 UNIX 的 awksort 命令来拆分列,然后在选择中位数之前对列进行排序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-20
    相关资源
    最近更新 更多