带有python/pandas和大型左外部连接的MemoryError

2024-10-06 11:21:19 发布

您现在位置:Python中文网/ 问答频道 /正文

我对Python和Pandas都是相当陌生的,我试图找出在大约1100万行的left数据集和大约160K行4列的right数据集之间执行庞大的left-outer连接的最快方法。这应该是一个多对一的情况,但我希望join不要在右侧出现重复行时抛出错误。我在Windows7的64位系统上使用了Canopy Express,内存为8GB,我很难接受这一点。在

以下是我迄今为止整理的代码模型:

import pandas as pd

leftcols = ['a','b','c','d','e','key']
leftdata = pd.read_csv("LEFT.csv", names=leftcols)

rightcols = ['x','y','z','key']
rightdata = pd.read_csv("RIGHT.csv", names=rightcols)

mergedata = pd.merge(leftdata, rightdata, on='key', how='left')
mergedata.to_csv("FINAL.csv")

这适用于小文件,但在我的系统上会产生内存错误,文件大小比实际需要合并的文件大小小两个数量级。在

我已经浏览了相关的问题(onetwothree),但是没有一个答案真正触及到这个基本问题,或者说如果它们真的找到了,它的解释也不够好,我无法识别潜在的解决方案。而公认的答案也无济于事。我已经在一个64位系统上使用了最新的稳定版本的Canopy(1.5.5 64位,使用python2.7.10)。在

什么是最快和/或最Python式的方法来避免这个记忆错误的问题?在


Tags: csv数据方法key内存readnames系统
3条回答

为什么不直接将您的右文件读入pandas(或者甚至读入一个简单的字典),然后使用csv模块遍历左文件来读取、扩展和写入每一行呢?处理时间是否是一个重要的限制因素(相对于开发时间)?在

正如另一个问题"Large data" work flows using pandas所建议的,dask(http://dask.pydata.org)可能是一个简单的选择。在

简单的例子

import dask.dataframe as dd
df1 = dd.read_csv('df1.csv')
df2 = dd.read_csv('df2.csv')
df_merge = dd.merge(df1, df2, how='left')

这种方法最终奏效了。下面是我的代码模型:

import csv

idata = open("KEY_ABC.csv","rU")
odata = open("KEY_XYZ.csv","rU")

leftdata = csv.reader(idata)
rightdata = csv.reader(odata)

def gen_chunks(reader, chunksize=1000000):
    chunk = []
    for i, line in enumerate(reader):
        if (i % chunksize == 0 and i > 0):
            yield chunk
            del chunk[:]
        chunk.append(line)
    yield chunk

count = 0

d1 = dict([(rows[3],rows[0]) for rows in rightdata])
odata.seek(0)    
d2 = dict([(rows[3],rows[1]) for rows in rightdata])
odata.seek(0)
d3 = dict([(rows[3],rows[2]) for rows in rightdata])

for chunk in gen_chunks(leftdata):
    res = [[k[0], k[1], k[2], k[3], k[4], k[5], k[6], 
                d1.get(k[6], "NaN")] for k in chunk]
    res1 = [[k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7], 
                d2.get(k[6], "NaN")] for k in res]
    res2 = [[k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7], k[8],
                d3.get(k[6], "NaN")] for k in res1]
    namestart = "FINAL_"
    nameend = ".csv"
    count = count+1
    filename = namestart + str(count) + nameend
    with open(filename, "wb") as csvfile:
        output = csv.writer(csvfile)
        output.writerows(res2)

通过将左数据集拆分为块,将右数据集转换为每个非键列的一个字典,并将列添加到左数据集(使用字典和键匹配填充这些列),脚本成功地在大约4分钟内完成了整个左连接,并且没有内存问题。在

还要感谢用户miku,他在对this post的评论中提供了区块生成器代码。在

也就是说:我非常怀疑这是最有效的方法。如果有人对改进这种方法有任何建议,请立即提出。在

相关问题 更多 >