在python中高效处理〜5000万条记录文件

我们有一个CSV格式的大约4600万条记录的文件.每条记录有大约18个字段,其中一个是64字节ID.我们有另一个文件,其中包含大约167K个唯一ID.需要将与ID对应的记录拉出.因此,我们编写了一个python程序,它将167K ID读入一个数组,并处理4600万条记录文件,检查每条记录中是否存在ID.这是代码的片段:

import csv
...
csvReadHandler = csv.reader(inputFile, delimiter=chr(1))
csvWriteHandler = csv.writer(outputFile, delimiter=chr(1), lineterminator='\n')
for fieldAry in csvReadHandler:
    lineCounts['orig'] += 1
    if fieldAry[CUSTOMER_ID] not in idArray:
        csvWriteHandler.writerow(fieldAry)
        lineCounts['mod'] += 1

在一小组数据上测试程序,这里是处理时间:

lines: 117929 process time: 236.388447046 sec
lines: 145390 process time: 277.075321913 sec

昨晚〜美国东部时间凌晨3点,我们已经开始在4600万个记录文件(大小约为13 GB)上运行该程序,现在它是美国东部时间上午10点左右,它仍在处理中!

问题:

>有没有更好的方法来处理这些记录以改善处理时间?
> python是正确的选择吗? awk或其他工具会有帮助吗?
>我猜在以下语句中对167K数组进行64字节ID查找是罪魁祸首:
 如果fieldAry [CUSTOMER_ID]不在idArray中:

还有更好的选择吗?

谢谢!

更新:这是在具有EBS附加卷的EC2实例上处理的.

解决方法:

您应该使用集合而不是列表;在for循环之前做:

idArray = set(idArray)

csvReadHandler = csv.reader(inputFile, delimiter=chr(1))
csvWriteHandler = csv.writer(outputFile, delimiter=chr(1), lineterminator='\n')
for fieldAry in csvReadHandler:
    lineCounts['orig'] += 1
    if fieldAry[CUSTOMER_ID] not in idArray:
        csvWriteHandler.writerow(fieldAry)
        lineCounts['mod'] += 1

并看到令人难以置信的加速;你只是因为选择了错误的数据结构而使用了几天不必要的处理时间.

在具有集合的运算符中具有O(1)时间复杂度,而具有列表的O(n)时间复杂度.这可能听起来像“不是什么大不了”,但实际上它是你脚本中的瓶颈.即使set对于那个O也会有一些更高的常量.所以你的代码在运行中使用的时间比使用30000多一些.如果在最佳版本中它需要30秒,那么现在你只需花费10天就可以在这一行上使用.

请参阅以下测试:我生成100万个ID并将10000个放入另一个列表中 – to_remove.然后我像你一样做一个for循环,为每条记录执行操作:

import random
import timeit

all_ids = [random.randint(1, 2**63) for i in range(1000000)]
to_remove = all_ids[:10000]
random.shuffle(to_remove)
random.shuffle(all_ids)


def test_set():
    to_remove_set = set(to_remove)
    for i in all_ids:
        if i in to_remove_set:
            pass

def test_list():
    for i in all_ids:
        if i in to_remove:
            pass


print('starting')
print('testing list', timeit.timeit(test_list, number=1))
print('testing set', timeit.timeit(test_set, number=1))

结果如下:

testing list 227.91903045598883
testing set 0.14897623099386692

设置版本花了149毫秒;列表版本需要228秒.现在这些都是小数字:在你的情况下,你有100万输入记录,而我的100万;因此,您需要将测试设置时间乘以50:使用您的数据集需要大约7.5秒.

另一方面,列表版本需要将该时间乘以50 * 17 – 不仅输入记录多50倍,而且要匹配的记录多17倍.因此我们得到227 * 50 * 17 = 192950.

所以你的算法花了2.2天做一些事情,通过使用正确的数据结构可以在7.5秒内完成.当然,这并不意味着您可以在7.5秒内扫描整个50 GB文档,但也可能不超过2.2天.所以我们改变了:

             2 days                           2.2 days 
 |reading and writing the files||------- doing id in list ------|

喜欢的东西

             2 days            7.5 seconds
 |reading and writing the files||
上一篇:匹配python字典的键中是否存在子字符串的最佳方法


下一篇:c# – 缓存查找性能