我经常以这种方式使用数十千兆字节的数据,例如,我在磁盘上有表,我通过查询读取,创建数据并追加回来。
值得一读的是文档和此线程的后期,以获取有关如何存储数据的几项建议。
影响你如何存储数据的细节,比如:
尽可能多地提供细节;我可以帮助你开发一个结构。
- 数据大小,行数,列数,列类型;您是追加行,还是仅追加列?
- 典型操作将是什么样子。例如,对列进行查询以选择一堆行和特定列,然后执行操作(在内存中),创建新列,保存这些列。
(举一个玩具的例子可以让我们提供更具体的建议。
- 处理完毕后,您该怎么办?步骤 2 是临时的还是可重复的?
- 输入平面文件:多少个,粗略的总大小(以 Gb 为单位)。例如,如何按记录组织这些?是每个都包含不同的字段,还是每个文件都有一些记录,每个文件中的所有字段都包括在内?
- 您是否曾经根据条件选择行(记录)的子集(例如,选择字段为 A > 5 的行)?然后做某事,或者你只是选择字段A,B,C和所有记录(然后做某事)?
- 您是否“处理”了所有列(分组),或者是否有一个良好的比例只能用于报告(例如,您希望保留数据,但在最终结果时间之前不需要拉入该列的显式)?
溶液
确保您至少安装了熊猫 0.10.1
。
逐块读取迭代文件和多个表查询。
由于 pytables 已针对行进行优化(这是您查询的内容),因此我们将为每组字段创建一个表。通过这种方式,可以轻松选择一小组字段(这将与大表一起使用,但这样做更有效率...我想我将来也许能够解决这个限制...无论如何,这更直观):
(以下是伪代码。
import numpy as np
import pandas as pd
# create a store
store = pd.HDFStore('mystore.h5')
# this is the key to your storage:
# this maps your fields to a specific group, and defines
# what you want to have as data_columns.
# you might want to create a nice class wrapping this
# (as you will want to have this map and its inversion)
group_map = dict(
A = dict(fields = ['field_1','field_2',.....], dc = ['field_1',....,'field_5']),
B = dict(fields = ['field_10',...... ], dc = ['field_10']),
.....
REPORTING_ONLY = dict(fields = ['field_1000','field_1001',...], dc = []),
)
group_map_inverted = dict()
for g, v in group_map.items():
group_map_inverted.update(dict([ (f,g) for f in v['fields'] ]))
读取文件并创建存储(实质上是执行以下操作):append_to_multiple
for f in files:
# read in the file, additional options may be necessary here
# the chunksize is not strictly necessary, you may be able to slurp each
# file into memory in which case just eliminate this part of the loop
# (you can also change chunksize if necessary)
for chunk in pd.read_table(f, chunksize=50000):
# we are going to append to each table by group
# we are not going to create indexes at this time
# but we *ARE* going to create (some) data_columns
# figure out the field groupings
for g, v in group_map.items():
# create the frame for this group
frame = chunk.reindex(columns = v['fields'], copy = False)
# append it
store.append(g, frame, index=False, data_columns = v['dc'])
现在,您已经在文件中拥有了所有表(实际上,如果您愿意,可以将它们存储在单独的文件中,并且可能必须将文件名添加到group_map,但可能这不是必需的)。
以下是获取列并创建新列的方式:
frame = store.select(group_that_I_want)
# you can optionally specify:
# columns = a list of the columns IN THAT GROUP (if you wanted to
# select only say 3 out of the 20 columns in this sub-table)
# and a where clause if you want a subset of the rows
# do calculations on this frame
new_frame = cool_function_on_frame(frame)
# to 'add columns', create a new group (you probably want to
# limit the columns in this new_group to be only NEW ones
# (e.g. so you don't overlap from the other tables)
# add this info to the group_map
store.append(new_group, new_frame.reindex(columns = new_columns_created, copy = False), data_columns = new_columns_created)
当您准备好post_processing时:
# This may be a bit tricky; and depends what you are actually doing.
# I may need to modify this function to be a bit more general:
report_data = store.select_as_multiple([groups_1,groups_2,.....], where =['field_1>0', 'field_1000=foo'], selector = group_1)
关于data_columns,您实际上不需要定义任何data_columns;它们允许您根据列对行进行子选择。例如:
store.select(group, where = ['field_1000=foo', 'field_1001>0'])
在最终的报告生成阶段,您可能最感兴趣的是它们(实质上,数据列与其他列隔离开来,如果您定义很多,这可能会对效率产生一些影响)。
您可能还希望:
- 创建一个函数,该函数获取字段列表,在groups_map中查找组,然后选择这些组并连接结果,以便获得结果帧(这实质上是select_as_multiple所做的)。这样,结构对您来说将非常透明。
- 某些数据列上的索引(使行子集化速度更快)。
- 启用压缩。
当您有问题时,请告诉我!