【发布时间】:2017-07-24 09:27:07
【问题描述】:
在寻找 SAS 排序的替代方案时,我决定尝试 Python 2.6(都在同一个 Unix 服务器上)。在 SAS 中对 5 亿行的窄表进行排序需要 20 分钟。 我将 20% 的表(1 亿行)导出到 CSV 文件,如下所示:
X|||465097434|912364420|0.00|0.00|0.00|0.00|1.00|01FEB2016|X|0|0
X|||465097434|912364420|0.00|0.00|0.00|0.00|0.00|02FEB2016|X|0|0
X|||465097434|912364420|0.00|0.00|0.00|0.00|2.00|03FEB2016|X|0|0
X|||465097434|912364421|0.00|0.00|0.00|0.00|3.00|04FEB2016|X|0|0
X|||465097434|912364421|0.00|0.00|0.00|0.00|6.00|05FEB2016|X|0|0
X|||965097411|912364455|0.00|0.00|0.00|0.00|4.00|04FEB2016|X|0|0
X|||965097411|912364455|0.00|0.00|0.00|0.00|1.00|05FEB2016|X|0|0
目标是按第 5 列和第 11 列对其进行排序。 首先我用代码检查了python读取文件的速度:
from __future__ import print_function
import csv
import time
linesRead=0
with open ('/path/to/file/CSV_FILE.csv','r') as dailyFile:
allLines=csv.DictReader(dailyFile, delimiter='|')
startTime=time.time()
for row in allLines:
linesRead += 1
if (linesRead) % 1000000 == 0:
print(linesRead, ": ", time.time()-startTime, " sec.")
startTime=time.time()
结果是读取每百万行需要 6 秒。
1000000 : 6.6301009655 sec.
2000000 : 6.33900094032 sec.
3000000 : 6.26246404648 sec.
4000000 : 6.56919789314 sec.
5000000 : 6.17433309555 sec.
...
98000000 : 6.61627292633 sec.
99000000 : 7.14683485031 sec.
100000000 : 7.08069109917 sec.
所以我扩展了代码以将其加载到字典中(key=column 5(帐户标识符)),value 是该帐户的列表(行)列表。 这就是我意识到在字典增长时将列表加载到字典的速度变慢的地方(非常合乎逻辑,因为要检查的键数量越来越多):
import csv
import time
myDictionary = {}
linesRead=0
with open ('/path/to/file/CSV_FILE.csv','r') as dailyFile:
allLines=csv.DictReader(dailyFile, delimiter='|')
startTime=time.time()
for row in allLines:
accountID=row['account_id'].strip('\'')
linesRead += 1
if accountID in myDictionary:
myDictionary[accountID].append([row['date'].strip('\''), row['balance1'], row['balance2'], row['balance3']])
else:
myDictionary[accountID]=[]
if (linesRead) % 1000000 == 0:
print(linesRead, ": ", time.time()-startTime, " sec.")
startTime=time.time()
时间是:
1000000, ': ', 8.9685721397399902, ' sec.')
(2000000, ': ', 10.344831943511963, ' sec.')
(3000000, ': ', 11.637137889862061, ' sec.')
(4000000, ': ', 13.024128913879395, ' sec.')
(5000000, ': ', 13.508150815963745, ' sec.')
(6000000, ': ', 14.94166088104248, ' sec.')
(7000000, ': ', 16.307464122772217, ' sec.')
(8000000, ': ', 17.130259990692139, ' sec.')
(9000000, ': ', 17.54616379737854, ' sec.')
(10000000, ': ', 20.254321813583374, ' sec.')
...
(39000000, ': ', 55.350741863250732, ' sec.')
(40000000, ': ', 56.762171983718872, ' sec.')
(41000000, ': ', 57.876702070236206, ' sec.')
(42000000, ': ', 54.548398017883301, ' sec.')
(43000000, ': ', 60.040227890014648, ' sec.')
这意味着没有机会在合理的时间内加载 5 亿行(5 亿行中的最后一百万行将加载 600 秒)。 我的猜测是每次迭代中最慢的部分是检查字典中的键是否存在:
if accountID in myDictionary:
所以我将字典更改为列表,希望简单的附加会更快:
with open ('/path/to/file/CSV_FILE.csv','r') as dailyFile:
allLines=csv.DictReader(dailyFile, delimiter='|')
startTime=time.time()
for row in allLines:
linesRead += 1
myList.append([row['account_id'].strip('\''), row['date'].strip('\''), row['balance1'], row['balance2'], row['balance3']])
if (linesRead) % 1000000 == 0:
print(linesRead, ": ", time.time()-startTime, " sec.")
startTime=time.time()
不幸的是,性能根本没有提高:
1000000 : 9.15476489067 sec.
2000000 : 10.3512279987 sec.
3000000 : 12.2600080967 sec.
4000000 : 13.5473120213 sec.
5000000 : 14.8431830406 sec.
6000000 : 16.5556428432 sec.
7000000 : 17.6754620075 sec.
8000000 : 19.1299819946 sec.
9000000 : 19.7615978718 sec.
10000000 : 22.5903761387 sec.
加载列表不应该比在输入时使用键检查加载字典快得多吗?
我是否在滥用 python 来处理这种数据? 为了比较,我使用 unix sort 命令对文件进行了排序:
$ date ; sort -t'|' -k5,9 CSV_FILE.csv > delete.txt; date;
Sun Jul 23 18:46:16 CEST 2017
Sun Jul 23 19:06:53 CEST 2017
完成这项工作需要 20 分钟。在 python 中,我无法将数据加载到内存中。
【问题讨论】:
-
Linux
sort的字典排序非常快。除了使用更强大的硬件,我不希望你能更快地获得。 -
实际上
SASsort 在同一时间内完成了 5 倍的数据集。相同大小(1 亿)数据集SAS不到 2 分钟排序
标签: python performance list sorting dictionary