【问题标题】:How to write a huge table (>10GB) column by column (column-wise) in Python?如何在 Python 中逐列(按列)编写一个巨大的表(>10GB)?
【发布时间】:2019-11-29 11:53:43
【问题描述】:

基本上我正在做的是计算一个向量,然后写出结果。现在我正在逐行编写它,然后我必须在 datamash transpose 之后转置,这有点烦人。 如果我可以逐列编写制表符分隔的表格,效率会更高。

如何在 Python 中执行此操作(可以通过 np.savetxt 关闭)?

这是我的以下尝试之一:

In [12]: import numpy as np

In [13]: data = np.random.normal(size=(10,3))

In [14]: data
Out[14]:
array([[-0.50469426, -0.9710173 ,  0.43285955],
       [ 0.71702597, -0.99998294, -1.00353228],
       [ 0.77699465, -0.66542361, -0.04594868],
       [-0.71012566, -1.46451086, -0.95308903],
       [ 0.47470605, -0.56792278,  0.95818696],
       [ 1.20729071, -0.04735589, -0.11576503],
       [ 1.03686861,  0.72149358,  0.35908901],
       [ 0.09520535,  0.24437775, -0.59554944],
       [-0.13346795,  0.29530724,  0.17524018],
       [-0.16433609,  0.05261348, -0.57545287]])

In [15]: with open("example.tsv", "w") as f:
    ...:     for row in data:
    ...:         print(*row, sep="\t\n", file=f, end="")
    ...:

In [16]: %%bash
    ...: cat example.tsv
    ...:
    ...:
-0.5046942610111921
-0.9710173002083825
0.43285954686999120.7170259682395401
-0.9999829435149956
-1.003532284093560.7769946455220355
-0.6654236121150638
-0.04594868270526936-0.7101256559235657
-1.4645108615674511
-0.95308903163753660.4747060486691834
-0.5679227787239494
0.9581869616594761.207290709055818
-0.047355888296561795
-0.115765032633420781.036868614074581
0.7214935810053711
0.35908901158512140.09520535113648704
0.2443777544867152
-0.5955494427027563-0.1334679518044996
0.29530724431573385
0.1752401825058493-0.1643360874489238
0.052613481327433834
-0.5754528683216069

【问题讨论】:

  • 您可以(1)使用两个文件:从一个文件中读取已写入列的每一行,添加新列并将其写入另一个文件。然后删除第一个文件并重复。 (2) 写入具有足够可用空间的行,以便稍后用其他列覆盖。
  • 先转置numpy行和列,然后保存到.csv文件如何? Python 中的一般 writeline 对象一次读取一行,因此没有干净的方法可以绕过它。或者,为大型数据集使用不同的库,例如 tensorflow 来为您处理这个问题?如果您经常处理 10GB 以上的数据集,Tensorflow 是您的最佳选择。
  • 我主要关心的是不必一次将所有内容加载到内存中。我想转置它,因为这样我就可以用 2 个并行文件(它们具有相同的结构)逐行迭代以进行特定分析。如果它们被转置,我只能这样做:/
  • @O.rka 啊哈,你第一次提到使用两个文件。这里更要注意 - 如下所述,在标准 python 中编写 for-iterator 的想法是破坏进程性能。最好的结果将是 python 让 { Heavy | numpy-tools 中的轻 }-计算和迭代(最好智能对齐到 numpy-broadcasts 和矢量化代码表达式)。这样才能获得最佳性能。

标签: python arrays file numpy io


【解决方案1】:

拥有 10 GB 的数据:首先值得一提的是:

除非您的数据处理管道不在您的控制范围内,并且您必须使用纯文本、GNU datamash 文本文件转换,否则numpy-side 是智能矢量/数组处理的绝对大师,如果有的话决定利用它的所有力量:

关键因素是:

  • in-RAM-layout:numpy 可以快速更改此设置,因为它通过处理轴抽象来欺骗您
  • on-disk-layout: numpy 可以同时读取 F-like (FORTRAN, column ) C-像(按行)数据文件

这就是说,您的 python 方面要感谢numpy,就像科学计算开发人员已经习惯于设计、开发和性能调整一样灵活。由于性能优势,Fortran 一直使用列优先排序,因此出于性能原因,它也一直被放入numpy-tools 中。


numpy 智能内存布局技巧:

|>>> from zmq import Stopwatch
|>>> aClk = Stopwatch()
|>>> aClk.start(); a = np.arange( 1E3 );aClk.stop() #_____ tiny arrays are cheapest
46                                                  #_____ ~ 46 [us]+may live in-cache
|>>> aClk.start(); _ = np.arange( 1E6 );aClk.stop() #_____ small arrays are "fast"
10131                                               #_____ ~ 10 [ms] to alloc. RAM
|
|
|>>> aClk.start(); _ = np.arange( 1E8 );aClk.stop() #_____ going 1E8+ SWAPs a lot
23970419                                            #_____ ~ 24 [s] + O/S SWAPs RAM
+0:00:34.974360
|
|>>> _.shape                      (100000000,)
|>>> _.size * _.itemsize           800000000
|>>> _.dtype                       dtype('float64')
|>>> _.flags
  C_CONTIGUOUS    : True  <--------+--- vectors are {C|F}-contiguous blocks
  F_CONTIGUOUS    : True  <--------+
  OWNDATA         : True  <------------ indeed owns own data + may have cheap "np.view"-s
  WRITEABLE       : True
  ALIGNED         : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY    : False

in-RAM-layout 是一个有点隐藏的问题,如果您从未接触过 numpy-tricks 和性能调整细节,可能是您第一次听说这个问题。

|>>> aClk.start();_ = _.reshape( _.shape[0]/10, 10 );aClk.stop() # ___ make 1E8 array into [1E7,1E1] shape
24                                                               # ___ ~ 24 [us]
# i.e. NOTHING HAS MOVED in memory, just the hidden indexing tricks were adapted
#      not a single byte from all of the 0.8 GB, now array, has been moved
#      the same will happen with numpy.ndarray.T smart-tricked operations
|>>> _.flags
  C_CONTIGUOUS    : True  <--------- vector ceased be vector, matrix is C-row-wise
  F_CONTIGUOUS    : False            as not a single byte was moved in in-RAM-layout
  OWNDATA         : False <--------- now, view-alike manipulations on "foreign"-owned data
  WRITEABLE       : True
  ALIGNED         : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY    : False

智能工具几乎可以在零时间内“转置”(实际上除了索引技巧什么都不做):

|>>> aClk.start();_.shape;aClk.stop()
(10000000, 10)                              #             BLOB is [1E7,1E1] ~0.8 GB
25                                          # ... 23 [us] to tell us the .shape
|>>> aClk.start();_.T.shape;aClk.stop()     #             go TRANSPOSE !
(10, 10000000)                              #             ok BLOB is [1E1,1E7]
43                                          # ...~43 [us]
                                            #    -23 [us] to tell us the .shape
                                            # ==  20 [us] to TRANSPOSE BLOB ??????
                                            #             NO transpose is tricked by adapted indexing only

转置操作只是改变了索引如何映射到已经分配了 RAM 的内存区域的方式,所有这些都无需在 RAM 中移动 0.8 GB 大小的 BLOB 数据的单个字节,只是智能索引技巧 -因此可以使用按列排列,因为它附加成本为零(不包括~ 20 微秒...)

|>>> _.T.flags
  C_CONTIGUOUS    : False
  F_CONTIGUOUS    : True <------------- now, F-column-wise arrangement gets set
  OWNDATA         : False <------------ and, again, on "foreign"-owned data
  WRITEABLE       : True                                               already
  ALIGNED         : True                                               put in-RAM
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY    : False

无论如何,为更大的数据集执行任何高性能、智能和矢量化代码,总是会受益于适当注意 RAM 布局并将矢量化代码调整为“跟随”(而不是对抗)实际使用的布局。关于您的处理器/计算平台的缓存内存层次结构和最终性能的最佳阅读总是在将矢量化代码放入 HPC 生产(缓存线, 缓存的关联性非常不同,如果您的代码“违反”了可能的最佳对齐方式,那么 HPC 影响确实是巨大的,并且由于这种低效率,此类代码会浪费大量时间。


在使用磁盘布局技巧时可能会花费大量资金或节省开支:

如上所述,除非禁止这样做,否则可以通过智能压缩首先将所有输出 numpy 存储在磁盘上来提高存储操作的性能。无论是numpy 方式还是dill.dump() 或类似开发的压缩库。

从上面的示例代码开始:

In [15]: with open("example.tsv", "w") as f:
    ...:     for row in data:
    ...:         print(*row, sep="\t\n", file=f, end="")

非常昂贵,因为 for 循环在 python 中特别慢(昂贵),并且可以使用“HPC”级numpy-工具更智能地完成整个工作。

额外添加的数据压缩本身既快速又比将数据移入存储和移回更便宜。 SSD 介质比传统介质更快,但(在 2019 年)压缩带来的好处仍然是更小的数据占用空间和更快的 I/O 数据流,这在大多数情况下(如果不是所有情况)证明了使用 @ 所花费的时间是合理的987654341@ 仅移动压缩的 (.ZIP_DEFLATED) blob,而不是始终在 ASCII 文件纯数字表示中花费每个数字 8 位。

numpy.load()numpy 的输入方向调用,dill 甚至可以保存/恢复整个 python-interpreter 会话,这可能在科学工作流自动化中有更广泛的用途(加上保存恢复-快照)

python 还提供 zipfile 工具用于直接处理压缩文件,因此诉诸 GNU 命令行工具的需要可能仅限于某些极端情况。

【讨论】:

  • 感谢您非常详细的回答。所以看起来 np 可以按列读取,但我们建议您编写文件,加载它,然后重塑它?
  • 不,我不建议添加任何人为步骤。我确实建议最好使用用于列数据文件的输入/输出和查看当前文件表示的工具来最好地“对齐”numpy矢量化处理(如果需要任何矩阵,可以便宜地转置),如果需要的话获得 GNU datamash-transposed,正如您在 O/P 中发布的那样(当它被证明 numpy-工具可以以非常聪明的方式做到这一点时,欺骗索引,好像所有的都有采取“就地”)。我还建议永远不要使用 python for-iterator,以防 numpy-vectorised 代码适合。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-02
  • 1970-01-01
  • 2018-07-15
  • 2017-02-07
  • 2013-12-20
  • 1970-01-01
相关资源
最近更新 更多