如何更好地管理Pandas中的内存使用?
我正在使用Pandas来存储、加载和处理金融数据。一个典型的数据文件是一个6000x4000的表格(6000只股票 x 4000个交易日期),如果说在某个日期有一半的股票的值是N/A,那么这个文件在CSV格式下的大小大约是200MB。我一直在用一台16GB内存的工作站,这对于加载这种大小的CSV文件、进行各种计算,然后存储结果来说是足够的。在一个典型的工作日,我在高峰使用时大约会用到10GB的内存。不过,我感觉我可以做得更高效一些。我希望能把这个内存使用量降到大约2GB,这样我就可以在一台只有4GB内存的普通笔记本上运行我每天的多个模型更新。这合理吗?我现在的内存使用量是否过高,不管我的硬件如何?
我知道上面的问题的答案取决于我具体在做什么。这里有一个我可能会运行的函数的例子:
def momentum_strategy():
# prices.csv is a matrix containing stock prices for 6000 stocks
# and 4000 trading dates
prices = pd.read_csv("prices.csv")
# Daily stock returns
returns = prices/prices.shift(1) -1
# Annualized return volatility
volatility = pd.rolling_std(returns, 21, 21) * 252**0.5
# 6-month stock returns
trail6monthreturns = prices/prices.shift(21*6) - 1
# Rank of 6 month stock returns
retrank = trail6monthreturns.rank(axis=1, ascending=False)
# Portfolio of the top 100 stocks as measured by 6 month return
positions = retrank.apply(lambda x: np.where(x<= 100, 1, np.nan))
# Daily returns for top 100 stocks
uptrendreturns = positions * returns
# Daily return for 100 stock portfolio
portfolioreturns = uptrendreturns.mean(1)
return positions, portfolioreturns
我想到的一个办法是使用HDF5存储格式,而不是CSV格式。因为通过最近的测试和查看Pandas的文档以及StackOverflow,我发现HDF5在输入/输出时要快得多,并且在这些操作中占用的内存也更少。对此你有什么看法?例如,我每天会把开盘价、最高价、最低价、收盘价、成交量、流通股数、市盈率、盈利增长以及其他30个类似的指标存储在一个单独的CSV文件中(就像上面的例子,通常是6000只股票 x 4000个交易日期)。如果推荐切换到HDF5,我是否应该把这30多个DataFrame存储在30多个单独的H5文件中?
在上面的函数中,如果我想在函数完成后访问一些中间结果,但又不想占用太多内存,是否可以考虑把结果存储在一个包含HDF5文件的“临时”文件夹中?例如:
def momentum_strategy_hdf5():
# prices.csv is a matrix containing stock prices for 6000 stocks
# and 4000 trading dates
prices = pd.read_csv("prices.csv")
s = pd.HDFStore("temp.h5")
# Daily stock returns
s['returns'] = prices/prices.shift(1) -1
# Annualized return volatility
s['volatility'] = pd.rolling_std(s['returns'], 21, 21) * 252**0.5
# 6-month stock returns
s['trail6monthreturns'] = prices/prices.shift(21*6)
# Rank of 6 month stock returns
s['retrank'] = s['trail6monthreturns'].rank(axis=1, ascending=False)
# Portfolio of the top 100 stocks as measured by 6 month return
s['positions'] = s['retrank'].apply(lambda x: np.where(x<= 100, 1, np.nan))
# Daily returns for top 100 stocks
s['uptrendreturns'] = s['positions'] * s['returns']
# Daily return for 100 stock portfolio
s['portfolioreturns'] = s['uptrendreturns'].mean(1)
return s['positions'], s['portfolioreturns']
补充:我刚刚测试了上面这两个函数,第一个用了15秒,而第二个用了42秒。所以第二个函数的速度明显慢得多,但希望有更好的方法?
2 个回答
虽然我对使用HDF5文件的经验不多,但我可以推荐三个Python库,可能会让你更顺利地开始。
H5py是一个专门用来处理二进制文件的Python库。虽然我不敢说它比Pandas的HDFstore更好(我觉得Pandas在处理大数据方面表现得相当不错,比如2.2M x 24的数据),但它可能也能满足你的需求。
PyTables在讨论内存管理时被提到过几次。我自己没有使用过这个库,但我在一些关于内存/HDF5问题的讨论中见过它。
mmap是一个用于内存映射的库(也就是把数据从硬盘移动到内存中,以便在不使用二进制格式的情况下进行处理)。如果你猜我没有使用过这个库,那你就猜对了。
总之,我在这方面的经验不多,但我觉得这三个库可能会帮助你在处理大数据集时,更好地利用Python的内存。
下面是处理这种数据的一个典型工作流程:
1) 首先读取csv格式的数据,把它转换成数据框(DataFrame),然后调整数据类型,最后用
HDFStore
写出数据(根据需要可以选择'固定'或'表格'格式)。这个步骤最好在一个单独的进程中完成,完成后退出这个进程。当数据集很大的时候,我会以逻辑的方式读取,比如说按日期范围,然后输出一个'表格'格式的HDF5文件。之后可以在这个文件上继续添加数据。2) 接下来进行查询(可以是按日期或其他标准)。进行一些计算,然后写出新的 HDF5文件。这一步可以并行处理(也就是同时用多个进程)。一定要确保每个进程写出的是不同的文件。
3) 把之前的数据文件合并成一个单独的HDF5文件。这一步是一个单独的进程。
4) 根据需要重复第2步和第3步。
关键是要分步骤进行,每一步之间写出中间数据,并在每个步骤后退出进程。这样可以保持内存中数据的大小在可控范围内,使得内存计算更快。此外,这样做还可以让多个进程同时处理对只读HDF5文件的CPU密集型操作。
在不同的系统进程中进行这些操作很重要,这样系统才能回收内存。
希望这对你有帮助!