一个简单的问题,实际上:在TSV(制表符分隔值)文件中,您有十亿(1e + 9)个无符号32位整数存储为十进制ASCII字符串。与使用同一数据集的其他工具相比,使用int()
进行转换的速度非常慢。为什么?更重要的是:如何让它更快?
因此问题是:在Python中将字符串转换为整数的最快方法是什么?
我真正想到的是一些半隐藏的Python功能,可以(ab)用于此目的,与Guido在"Optimization Anecdote"中使用array.array
不同。
示例数据(标签扩展为空格)
38262904 "pfv" 2002-11-15T00:37:20+00:00
12311231 "tnealzref" 2008-01-21T20:46:51+00:00
26783384 "hayb" 2004-02-14T20:43:45+00:00
812874 "qevzasdfvnp" 2005-01-11T00:29:46+00:00
22312733 "bdumtddyasb" 2009-01-17T20:41:04+00:00
读取数据所花费的时间与此无关,处理数据是瓶颈。
微基准
以下所有语言均为解释语言。主机正在运行64位Linux。
使用IPython 0.9.1的Python 2.6.2,每秒约214k转换(100%):
In [1]: strings = map(str, range(int(1e7)))
In [2]: %timeit map(int, strings);
10 loops, best of 3: 4.68 s per loop
REBOL 3.0版本2.100.76.4.2,~231kcps(108%):
>> strings: array n: to-integer 1e7 repeat i n [poke strings i mold (i - 1)]
== "9999999"
>> delta-time [map str strings [to integer! str]]
== 0:00:04.328675
REBOL 2.7.6.4.2(2008年3月15日),约523kcps(261%):
正如John在评论中指出的那样,这个版本不构建转换整数的列表,因此给出的速度比是相对于for str in strings: int(str)
的Python的4.99s运行时。
>> delta-time: func [c /local t] [t: now/time/precise do c now/time/precise - t]
>> strings: array n: to-integer 1e7 repeat i n [poke strings i mold (i - 1)]
== "9999999"
>> delta-time [foreach str strings [to integer! str]]
== 0:00:01.913193
KDB + 2.6t 2009.04.15,~2016kcps(944%):
q)strings:string til "i"$1e7
q)\t "I"$strings
496
答案 0 :(得分:3)
我可能会建议,对于原始速度,Python不适合执行此任务。手动编码的C实现将轻松击败Python。
答案 1 :(得分:3)
通过确保在最严格的循环中仅使用“本地”变量,您将获得一定比例的速度。 int
函数是全局函数,因此查找它将比本地函数更昂贵。
你真的需要内存中的所有十亿个数字吗?考虑使用一些迭代器一次只给你几个值。十亿个数字会占用一些存储空间。将这些附加到列表中,一次一个,将需要几次大的重新分配。
如果可能的话,完全从Python中循环出来。这里的地图功能可以是你的朋友。我不确定您的数据是如何存储的。如果每行只有一个数字,则可以将代码减少到
values = map(int, open("numberfile.txt"))
如果每行有多个值是空格分隔的,请深入研究itertools以保持循环代码不受Python影响。这个版本还有一个额外的好处,就是创建一个数字迭代器,这样你就可以一次只从文件中取出一个或多个数字,而不是一次性使用10亿个。
numfile = open("numberfile.txt")
valIter = itertools.imap(int, itertools.chain(itertools.imap(str.split, numfile)))
答案 2 :(得分:3)
以下最简单的C扩展已经大大改进了内置,管理转换速度超过每秒三倍的字符串(650kcps vs 214kcps):
static PyObject *fastint_int(PyObject *self, PyObject *args) {
char *s; unsigned r = 0;
if (!PyArg_ParseTuple(args, "s", &s)) return NULL;
for (r = 0; *s; r = r * 10 + *s++ - '0');
return Py_BuildValue("i", r);
}
这显然不适合任意长度的整数和其他各种特殊情况,但在我们的场景中没有问题。
答案 3 :(得分:1)
同意格雷格; Python作为一种解释语言通常很慢。您可以尝试使用Psyco library动态编译源代码,或者使用较低级别的语言(如C / C ++)对应用程序进行编码。
答案 4 :(得分:1)
正如其他人所说,您可以编写自己的C模块来为您进行解析/转换。然后你可以简单地导入它并调用它。您可以使用Pyrex或其Cython衍生物从Python生成C(通过向Python添加一些类型约束提示)。
您可以阅读有关Cython的更多信息,看看是否有帮助。
我想到的另一个问题是......你将用这十亿个整数做什么?是否有可能将它们作为字符串加载,将它们作为字符串搜索并根据需要执行延迟转换?或者您可以使用threading
或multiprocessing
模块和队列并行转换和其他计算吗? (让一个或多个线程/进程执行转换并提供处理引擎从中获取它们的队列)。换句话说,生产者/消费者设计会缓解这个问题吗?
答案 5 :(得分:0)
它可能不是你的选择,但我会非常努力地使用二进制文件而不是文本。它经常变化吗?如果没有,你可以预先处理它。
答案 6 :(得分:0)
numpy的这一点做得很好:
np.fromstring(line,dtype = np.float,sep =“”)