我们有一个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||