协慌网

登录 贡献 社区

使用熊猫的 “大数据” 工作流程

在学习大熊猫的过程中,我试图解决这个问题的答案已有好几个月了。我使用 SAS 进行日常工作,这非常适合它的核心支持。然而,由于其他许多原因,SAS 作为一款软件非常糟糕。

有一天,我希望用 python 和 pandas 替换我对 SAS 的使用,但我目前缺乏大型数据集的核心工作流程。我不是在谈论需要分布式网络的 “大数据”,而是说文件太大而无法容纳在内存中,但又足够小以适应硬盘驱动器。

我的第一个想法是使用HDFStore在磁盘上保存大型数据集,并仅将我需要的部分拉入数据帧进行分析。其他人提到 MongoDB 是一种更容易使用的替代品。我的问题是:

有哪些最佳实践工作流程可用于完成以下任务:

  1. 将平面文件加载到永久的磁盘数据库结构中
  2. 查询该数据库以检索数据以提供给 pandas 数据结构
  3. 在操作 pandas 中的片段后更新数据库

真实世界的例子将非常受欢迎,尤其是那些在 “大数据” 上使用熊猫的人。

编辑 - 我希望如何工作的示例:

  1. 迭代导入大型平面文件并将其存储在永久的磁盘数据库结构中。这些文件通常太大而无法放入内存中。
  2. 为了使用 Pandas,我想读取这些数据的子集(通常一次只有几列),它们可以适合内存。
  3. 我将通过对所选列执行各种操作来创建新列。
  4. 然后我必须将这些新列附加到数据库结构中。

我正在尝试找到执行这些步骤的最佳实践方法。阅读关于 pandas 和 pytables 的链接似乎附加一个新列可能是个问题。

编辑 - 特别回应杰夫的问题:

  1. 我正在构建消费者信用风险模型。数据种类包括电话,SSN 和地址特征; 财产价值; 犯罪记录,破产等贬损信息...... 我每天使用的数据集平均有近 1,000 到 2,000 个字段的混合数据类型:数字和字符数据的连续,名义和序数变量。我很少附加行,但我会执行许多创建新列的操作。
  2. 典型操作涉及使用条件逻辑将多个列组合到新的复合列中。例如, if var1 > 2 then newvar = 'A' elif var2 = 4 then newvar = 'B' 。这些操作的结果是我的数据集中每条记录的新列。
  3. 最后,我想将这些新列附加到磁盘上的数据结构中。我将重复第 2 步,使用交叉表和描述性统计数据探索数据,试图找到有趣,直观的模型关系。
  4. 典型的项目文件通常约为 1GB。文件被组织成一种行,其中一行包括消费者数据的记录。每行对每条记录都有相同的列数。情况总是如此。
  5. 在创建新列时,我很少会按行进行子集化。但是,在创建报告或生成描述性统计信息时,对行进行子集化非常常见。例如,我可能想为特定的业务线创建一个简单的频率,比如零售信用卡。要做到这一点,除了我要报告的列之外,我只会选择那些业务线 = 零售的记录。但是,在创建新列时,我会提取所有数据行,只提取操作所需的列。
  6. 建模过程要求我分析每一列,寻找与某些结果变量的有趣关系,并创建描述这些关系的新化合物列。我探索的列通常以小集合完成。例如,我将专注于一组 20 个列,只处理属性值并观察它们与贷款违约的关系。一旦探索了这些并创建了新的列,我就转到另一组列,比如大学教育,然后重复这个过程。我正在做的是创建候选变量来解释我的数据和某些结果之间的关系。在这个过程的最后,我应用了一些学习技术,从这些复合列中创建一个方程式。

我很少会在数据集中添加行。我几乎总是会创建新的列(统计 / 机器学习用语中的变量或特征)。

答案

我通常以这种方式使用数十亿字节的数据,例如我在磁盘上有表格,我通过查询读取,创建数据并追加。

值得阅读文档,在本主题的后期提供有关如何存储数据的若干建议。

详细信息将影响您存储数据的方式,例如:
尽可能多地提供细节; 我可以帮你建立一个结构。

  1. 数据大小,行数,列数,列数; 你是追加行还是只是列?
  2. 典型的操作会是什么样子。例如,对列进行查询以选择一堆行和特定列,然后执行操作(内存中),创建新列,保存这些列。
    (给出一个玩具示例可以让我们提供更具体的建议。)
  3. 经过那个处理,那你做什么?第 2 步是临时的,还是可重复的?
  4. 输入平面文件:Gb 中有多少粗略的总大小。这些如何通过记录组织起来?每个文件是否包含不同的字段,或者每个文件中是否包含每个文件中包含所有字段的记录?
  5. 您是否曾根据条件选择行(记录)的子集(例如,选择字段 A> 5 的行)?然后做一些事情,或者你只是选择包含所有记录的字段 A,B,C(然后做一些事情)?
  6. 您是否 “处理” 所有列(成组),或者是否有一个很好的比例,您只能用于报告(例如,您希望保留数据,但不需要明确列出该列最终结果时间)?

确保您的熊猫安装至少0.10.1

读取chunk-by-chunk多个表查询的 迭代文件

由于 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 hmay 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 所做的)。 通过这种方式,结构对您来说非常透明。
  • 某些数据列上的索引(使行子集更快)。
  • 启用压缩。

如果您有疑问,请告诉我!

我认为上面的答案缺少一个我发现非常有用的简单方法。

当我的文件太大而无法加载到内存中时,我会将文件分解为多个较小的文件(按行或列)

示例:如果 30 天大小的交易数据为 30 天,我将其分成每天约 1GB 大小的文件。我随后分别处理每个文件并在最后汇总结果

其中一个最大的优点是它允许并行处理文件(多个线程或进程)

另一个优点是文件操作(如在示例中添加 / 删除日期)可以通过常规 shell 命令来完成,这在更高级 / 复杂的文件格式中是不可能的

这种方法并不涵盖所有场景,但在很多场景中非常有用

如果您的数据集在 1 到 20GB 之间,那么您应该得到一个具有 48GB RAM 的工作站。然后 Pandas 可以将整个数据集保存在 RAM 中。我知道这不是你在这里寻找的答案,但在 4GB 内存的笔记本电脑上进行科学计算是不合理的。