5

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

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 文件中。所以我想遍历每一列并计算中位数。

有没有办法让我在不读取整个文件的情况下获得列迭代器?

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

4

4 回答 4

3

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

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]

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

于 2012-09-22T22:16:52.420 回答
1

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

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

于 2012-09-22T22:05:02.223 回答
1

使用 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 数组的首选。

于 2012-09-22T22:34:28.123 回答
0

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

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

于 2012-09-22T22:18:54.477 回答