在学习大熊猫的过程中,我试图解决这个问题的答案已有好几个月了。我使用SAS进行日常工作,这非常适合它的核心支持。然而,由于其他许多原因,SAS作为一款软件非常糟糕。
有一天,我希望用python和pandas取代我对SAS的使用,但我目前缺乏大型数据集的核心工作流程。我不是在谈论需要分布式网络的“大数据”,而是文件太大而无法容纳在内存中,但又足够小以适应硬盘驱动器。
我的第一个想法是使用HDFStore
在磁盘上保存大型数据集,并仅将我需要的部分拉入数据帧进行分析。其他人提到MongoDB是一种更容易使用的替代品。我的问题是:
有哪些最佳实践工作流程可用于实现以下目标:
非常感谢真实世界的例子,尤其是那些在“大数据”上使用熊猫的人。
编辑 - 我希望如何运作的示例:
我正在尝试找到执行这些步骤的最佳实践方法。阅读关于pandas和pytables的链接似乎附加一个新列可能是个问题。
编辑 - 特别回应杰夫的问题:
if var1 > 2 then newvar = 'A' elif var2 = 4 then newvar = 'B'
。这些操作的结果是我的数据集中每条记录的新列。我很少会在数据集中添加行。我将几乎总是创建新的列(统计/机器学习用语中的变量或特征)。
答案 0 :(得分:543)
我通常以这种方式使用数十亿字节的数据 例如我在磁盘上有表,我通过查询读取,创建数据并追加回来。
值得一读the docs和late in this thread,以获取有关如何存储数据的若干建议。
将影响您存储数据的方式的详细信息,例如:
尽可能多地提供细节;我可以帮你建立一个结构。
确保您已安装pandas at least 0.10.1
。
阅读iterating files chunk-by-chunk和multiple table queries。
由于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中,但可能这不是必需的)。< / p>
这是您获取列并创建新列的方法:
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)
准备好进行后期处理时:
# 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,您实际上并不需要定义 ANY data_columns;它们允许您根据列子选择行。例如。类似的东西:
store.select(group, where = ['field_1000=foo', 'field_1001>0'])
在最终报告生成阶段,它们可能对您最感兴趣(实际上,数据列与其他列隔离,如果您定义了很多,这可能会影响效率)。
您可能还想:
如果您有疑问,请告诉我们!
答案 1 :(得分:116)
我认为上面的答案缺少一种我发现非常有用的简单方法。
如果我的文件太大而无法在内存中加载,我会将文件拆分为多个较小的文件(按行或列)
示例:如果30天大小的交易数据为30天,我将其分成每天约1GB大小的文件。我随后分别处理每个文件并在结尾汇总结果
其中一个最大的优点是它允许并行处理文件(多个线程或进程)
另一个优点是文件操作(如在示例中添加/删除日期)可以通过常规shell命令来完成,这在更高级/复杂的文件格式中是不可能的
这种方法并不涵盖所有场景,但在很多场景中非常有用
答案 2 :(得分:59)
在问题发生两年后,现在有一个“核心外”大熊猫等同于:dask。太棒了!虽然它不支持所有的熊猫功能,但你可以用它来实现它。
答案 3 :(得分:58)
如果你的数据集在1到20GB之间,你应该得到一个48GB RAM的工作站。然后Pandas可以将整个数据集保存在RAM中。我知道这不是你在这里寻找的答案,但在4GB内存的笔记本电脑上进行科学计算是不合理的。
答案 4 :(得分:50)
我知道这是一个旧线程,但我认为Blaze库值得一试。它是为这些类型的情况而构建的。
来自文档:
Blaze将NumPy和Pandas的可用性扩展到分布式和核心外计算。 Blaze提供类似于NumPy ND-Array或Pandas DataFrame的界面,但将这些熟悉的界面映射到Postgres或Spark等各种其他计算引擎上。
编辑顺便提一下,它由ContinuumIO和NumPy的作者Travis Oliphant提供支持。
答案 5 :(得分:45)
这是pymongo的情况。我还在python中使用sql server,sqlite,HDF,ORM(SQLAlchemy)进行原型设计。首先,pymongo是一个基于文档的数据库,因此每个人都是一个文档(dict
属性)。许多人组成了一个集合,你可以拥有许多集合(人,股票市场,收入)。
pd.dateframe - &gt; pymongo注意:我使用chunksize
中的read_csv
来保持5到10k记录(如果更大,pymongo会丢弃套接字)
aCollection.insert((a[1].to_dict() for a in df.iterrows()))
查询:gt =大于......
pd.DataFrame(list(mongoCollection.find({'anAttribute':{'$gt':2887000, '$lt':2889000}})))
.find()
返回一个迭代器,所以我通常使用ichunked
来切入较小的迭代器。
加入怎么样,因为我通常会将10个数据源粘贴在一起:
aJoinDF = pandas.DataFrame(list(mongoCollection.find({'anAttribute':{'$in':Att_Keys}})))
然后(在我的情况下,有时我必须首先在aJoinDF
聚合“可合并”之前。)
df = pandas.merge(df, aJoinDF, on=aKey, how='left')
然后,您可以通过下面的更新方法将新信息写入主集合。 (逻辑集合与物理数据源)。
collection.update({primarykey:foo},{key:change})
在较小的查找中,只是非规范化。例如,您在文档中有代码,只需添加字段代码文本,并在创建文档时执行dict
查找。
现在你有一个基于一个人的好数据集,你可以在每个案例中释放你的逻辑并创建更多属性。最后,你可以阅读大熊猫你的3到内存最大关键指标,并进行枢轴/聚合/数据探索。这适用于我有300万条记录,包括数字/大文/类别/代码/浮动/...
您还可以使用MongoDB中内置的两种方法(MapReduce和聚合框架)。 See here for more info about the aggregate framework,因为它似乎比MapReduce更容易,并且看起来很方便快速的聚合工作。注意我不需要定义我的字段或关系,我可以添加项目到文档。在快速变化的numpy,pandas,python工具集的当前状态下,MongoDB帮助我开始工作:)
答案 6 :(得分:37)
我发现这有点晚了,但我处理类似的问题(按揭预付款模式)。我的解决方案是跳过pandas HDFStore层并使用直接pytables。我将每列保存为最终文件中的单个HDF5数组。
我的基本工作流程是首先从数据库中获取CSV文件。我gzip它,所以它不是那么大。然后我将其转换为面向行的HDF5文件,通过在python中迭代它,将每一行转换为实际数据类型,并将其写入HDF5文件。这需要几十分钟,但它不使用任何内存,因为它只是逐行操作。然后我将面向行的HDF5文件“转置”为面向列的HDF5文件。
表转置如下:
def transpose_table(h_in, table_path, h_out, group_name="data", group_path="/"):
# Get a reference to the input data.
tb = h_in.getNode(table_path)
# Create the output group to hold the columns.
grp = h_out.createGroup(group_path, group_name, filters=tables.Filters(complevel=1))
for col_name in tb.colnames:
logger.debug("Processing %s", col_name)
# Get the data.
col_data = tb.col(col_name)
# Create the output array.
arr = h_out.createCArray(grp,
col_name,
tables.Atom.from_dtype(col_data.dtype),
col_data.shape)
# Store the data.
arr[:] = col_data
h_out.flush()
回读它然后看起来像:
def read_hdf5(hdf5_path, group_path="/data", columns=None):
"""Read a transposed data set from a HDF5 file."""
if isinstance(hdf5_path, tables.file.File):
hf = hdf5_path
else:
hf = tables.openFile(hdf5_path)
grp = hf.getNode(group_path)
if columns is None:
data = [(child.name, child[:]) for child in grp]
else:
data = [(child.name, child[:]) for child in grp if child.name in columns]
# Convert any float32 columns to float64 for processing.
for i in range(len(data)):
name, vec = data[i]
if vec.dtype == np.float32:
data[i] = (name, vec.astype(np.float64))
if not isinstance(hdf5_path, tables.file.File):
hf.close()
return pd.DataFrame.from_items(data)
现在,我通常在拥有大量内存的计算机上运行它,所以我可能对内存使用情况不够谨慎。例如,默认情况下,加载操作会读取整个数据集。
这通常对我有用,但它有点笨重,我不能使用花哨的pytables魔法。
编辑:这种方法的真正优势在于记录数组的pytables默认值,然后我可以使用h5r将数据加载到R中,而h5r无法处理表。或者,至少,我无法让它加载异构表。
答案 7 :(得分:26)
我发现对大数据用例有帮助的一个技巧是通过将浮点精度降低到32位来减少数据量。它并不适用于所有情况,但在许多应用中,64位精度是过度的,节省2倍的内存是值得的。使一个明显的观点更加明显:
>>> df = pd.DataFrame(np.random.randn(int(1e8), 5))
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float64(5)
memory usage: 3.7 GB
>>> df.astype(np.float32).info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float32(5)
memory usage: 1.9 GB
答案 8 :(得分:15)
正如其他人所指出的那样,经过几年的发展,出现了“核心外”的大熊猫等同物:dask。虽然dask不是大熊猫及其所有功能的直接替代品,但它有以下几个原因:
Dask是一个灵活的分析计算并行计算库,针对交互式计算工作负载的动态任务调度进行了优化 “大数据”集合,如并行数组,数据帧和列表,可将常见接口(如NumPy,Pandas或Python迭代器)扩展到大于内存或分布式环境,并可从笔记本电脑扩展到集群。
Dask强调以下优点:
- 熟悉:提供并行化的NumPy数组和Pandas DataFrame对象
- 灵活:为更多自定义工作负载和与其他项目的集成提供任务调度界面。
- Native:在Pure Python中启用分布式计算,可以访问PyData堆栈。
- 快速:以低开销,低延迟和快速数值算法所需的最小序列化运行
- 扩展:在具有1000个核心的群集上弹性运行缩小:在单个进程中设置并在笔记本电脑上运行的简单方法
- 响应:以交互式计算为设计理念,提供快速反馈和诊断以帮助人类
并添加一个简单的代码示例:
import dask.dataframe as dd
df = dd.read_csv('2015-*-*.csv')
df.groupby(df.user_id).value.mean().compute()
替换了一些像这样的pandas代码:
import pandas as pd
df = pd.read_csv('2015-01-01.csv')
df.groupby(df.user_id).value.mean()
,尤其值得注意的是,通过concurrent.futures接口提供了一个提交自定义任务的通用:
from dask.distributed import Client
client = Client('scheduler:port')
futures = []
for fn in filenames:
future = client.submit(load, fn)
futures.append(future)
summary = client.submit(summarize, futures)
summary.result()
答案 9 :(得分:12)
还有一个变种
在pandas中完成的许多操作也可以作为db查询(sql,mongo)完成
使用RDBMS或mongodb可以在数据库查询中执行一些聚合(针对大数据进行优化,并有效地使用缓存和索引)
稍后,您可以使用pandas执行后期处理。
这种方法的优点是你可以获得用于处理大数据的数据库优化,同时仍然用高级声明性语法定义逻辑 - 而不必处理决定在内存中做什么以及做什么的细节没有核心。
虽然查询语言和pandas不同,但将部分逻辑从一个转换为另一个通常并不复杂。
答案 10 :(得分:9)
值得一提的还有Ray,
它是一个分布式计算框架,它以分布式方式为pandas提供了自己的实现。
只需替换pandas导入,代码应该按原样运行:
# import pandas as pd
import ray.dataframe as pd
#use pd as usual
可以在这里阅读更多详细信息:
答案 11 :(得分:8)
如果您选择创建数据管道的简单路径,请考虑Ruffus,该管道可分解为多个较小的文件。
答案 12 :(得分:5)
我最近遇到过类似的问题。我发现只是以块的形式读取数据并附加它,因为我以块的形式将它写入相同的csv。我的问题是根据另一个表中的信息添加日期列,使用某些列的值,如下所示。这可能会帮助那些被dask和hdf5困惑的人,但更熟悉像我这样的熊猫。
<table>
<tbody>
<tr>
<td width="50%" style="background-color:green;">
<p>
Some content
</p>
</td>
<td width="50%">
<img src="https://placehold.it/300x300?text=%20">
</td>
</tr>
<tr>
<td width="50%" style="background-color:green;">
<p>
Some content
</p>
</td>
<td width="50%" style="background-color:green;">
<p>
Some content
</p>
</td>
</tr>
</tbody>
</table>
答案 13 :(得分:2)
我想指出一下Vaex软件包。
Vaex是用于惰性核心数据框架(类似于Pandas)的python库,用于可视化和探索大型表格数据集。它可以在高达每秒十亿(10 9 )个对象/行的N维网格上计算统计信息,例如平均值,总和,计数,标准差等。可视化使用直方图,密度图和3d体积渲染完成,从而可以交互式探索大数据。 Vaex使用内存映射,零内存复制策略和惰性计算来获得最佳性能(不会浪费内存)。
查看文档:{{3}} 该API与熊猫API非常接近。
答案 14 :(得分:0)
镶木地板文件格式非常适合您描述的用例。您可以使用 pd.read_parquet(path_to_file, columns=["foo", "bar"])
https://pandas.pydata.org/docs/reference/api/pandas.read_parquet.html
答案 15 :(得分:-1)
此刻,我正在“像”您一样工作,只是规模较小,这就是为什么我没有为您的建议提供PoC的原因。
但是,我似乎在使用pickle作为缓存系统并将各种功能的执行外包到文件中找到了成功-从我的commando / main文件执行这些文件;例如,我使用prepare_use.py转换对象类型,将数据集拆分为测试,验证和预测数据集。
使用咸菜进行缓存如何工作? 我使用字符串来访问动态创建的pickle文件,具体取决于传递的参数和数据集(我尝试捕获并确定程序是否已在运行,使用.shape设置数据集,使用dict传递参数)。 尊重这些措施,我得到了一个String来尝试查找和读取.pickle文件,并且如果找到该文件,可以跳过处理时间,以便跳转到我现在正在处理的执行。
使用数据库时,我遇到了类似的问题,这就是为什么我在使用此解决方案时感到高兴的原因,但是-有很多确定的限制-例如由于冗余而存储大量的泡菜集。 可以通过正确的索引从转换前到更新表进行更新-验证信息可以打开另一本完整的书(我尝试合并爬网的租金数据,基本上在2小时后停止使用数据库-因为我想在之后跳回每个转换过程)
我希望我的2美分能以某种方式帮助您。
问候。
答案 16 :(得分:-9)
为什么熊猫?您是否尝试过标准Python ?
使用标准库python。即使已发布稳定版,Pandas也会经常更新。
使用标准的python库,您的代码将始终运行。
一种实现方法是对要存储数据的方式有所了解,并对数据要解决哪些问题。然后绘制一个如何组织数据(思考表)的模式,这将有助于您查询数据,而不必进行规范化。
您可以充分利用:
Ram和HDD随着时间的流逝越来越便宜,并且标准python 3广泛可用且稳定。
您要解决的基本问题是“如何查询大数据集?”。 hdfs体系结构或多或少是我在这里描述的(数据建模,数据存储在磁盘上)。
比方说,您有1000 PB的数据,您将无法将其存储在Dask或Pandas中,您最好的机会是将其存储在磁盘上并使用生成器进行处理。