在Python中将值转换为各自数据类型的最快方法

时间:2011-09-09 15:36:13

标签: python performance casting

我有一个值列表 - 所有字符串。我想将这些值转换为各自的数据类型。我将值映射到可用的类型信息。

有三种不同的数据类型:int,str,datetime。 代码需要能够处理数据的错误情况。

我正在做类似的事情: -

tlist =  [ 'some datetime value', '12', 'string', .... ]

#convert it to: [ datetime object, 12, 'string', ....]

error_data = ['', ' ', '?', ...]

d = { 0: lambda x: datetime.strptime(x,...) if x not in error_data else x, 
      1: lambda x: int(x) if x not in error_data else 0,
      2: lambda x: x 
      ...
     }

result = [ d[i](j) for i, j in enumerate(tlist) ]

要转换的列表很长,就像180个值一样,我需要为成千上万个这样的列表执行此操作。上面代码的性能非常差。最快的方法是什么?

谢谢

7 个答案:

答案 0 :(得分:2)

类似的东西:

>>> values = ["12", "a", "bcd", "2.2"]
>>> types = [int, int, str, float]
>>> defaults = {int: 0, float: 0.0}
>>> res = []
>>> for v, f in itertools.izip(values, types): #Just use zip for Python 3+.
    try:
        res.append(f(v))
    except ValueError:
        res.append(defaults[f])
>>> print(res)
[12, 0, 'bcd', 2.2]

修改

这不处理日期时间值。我的解决方案是使用str,并在循环后转换为datetime,如:

res[0] = datetime.strptime(res[0], "...")

获取和设置列表项都具有O(1)复杂性,因此它应该不是问题。

答案 1 :(得分:2)

如果您的日期时间值始终一致,为什么不让类型转换处理您尝试在error_data中管理的无效数据。这不像某些解决方案那样性感,但是基于列表中的数据位置管理类型转换更容易维护和扩展。

def convert(position, val):
    if position == 0:
        try:
            return datetime.strptime(val, '%Y-%m-%d %H:%M:%S') # assuming date is in a constant format
        except ValueError:
            return val
    elif position in (1, 15, 16): # assuming that you have other int values in other "columns"
        try:
            return int(val)
        except ValueError:
            return 0
    else: # string type
       return val

result = [convert(i,j) for i, j in enumerate(tlist)]

答案 2 :(得分:1)

由于您知道要转换为的类型,因此尝试优化转化时可能无法获得性能提升。表现不佳可能来自反复迭代error_data。如果可能,请将error_data列表重新构建为set以利用该类型的性质:

error_set = set((err, None) for err in error_data)

然后继续前进。进一步的改进需要分析您的代码以实际确定花费的时间。

答案 3 :(得分:1)

您的代码中存在不协调:

如果列表中的所有元素都是字符串,则无法在 x 为字符串的情况下编写datetime(x)

修改

它没有描述任何东西,因为它不协调。代码中没有的内容的复杂性并不能证明代码中的怪异性。只要您不解释如何将字符串作为参数传递给函数 datetime.datetime(),就没有人能够帮助您,IMO。

修改

我认为最好在读取文件时直接创建列表。

我写了一个例子:

首先,我使用以下代码创建了一个CSV文件:

import csv
from random import randint,choice
from time import gmtime


xx = ['Whose', 'all', 'birth', 'just', 'infant', 'William',
      'dearest', 'rooms', 'find', 'Deserts', 'saucy', 'His',
      'how', 'considerate', 'only', 'other', 'Houses', 'has',
      'Fanny', 'them', 'his', 'very', 'dispense', 'early',
      'words', 'not', 'thus', 'now', 'pettish', 'Worth']

def gen(n):
    for i in xrange(n):
        yield ['AAAA','%d/%02d/%02d %02d:%02d:%02d' % gmtime(randint(0,80000000))[0:6],'@@@']
        yield ['BBBB',randint(100,999),'^^^^^^']
        yield ['CCCC',choice(xx),'-----------------']

with open('zzz.txt','wb') as f:
    writ = csv.writer(f, delimiter='#')
    writ.writerows(x for x in gen(60))

CSV文件的结构是这样的:

AAAA#1972/02/11 08:53:53#@@@
BBBB#557#^^^^^^
CCCC#dearest#-----------------
AAAA#1971/10/15 06:55:20#@@@
BBBB#668#^^^^^^
CCCC#?#-----------------
AAAA#1972/07/13 11:10:05#@@@
BBBB#190#^^^^^^
CCCC#infant#-----------------
AAAA#1971/11/22 19:31:42#@@@
BBBB#202#^^^^^^
CCCC##-----------------
AAAA#1971/06/12 05:48:39#@@@
BBBB#81#^^^^^^
CCCC#find#-----------------
AAAA#1970/12/09 06:26:29#@@@
BBBB#72#^^^^^^
CCCC#find#-----------------
AAAA#1972/07/05 10:45:32#@@@
BBBB#270#^^^^^^
CCCC#rooms#-----------------
AAAA#1972/06/23 05:52:20#@@@
BBBB#202#^^^^^^
CCCC##-----------------
AAAA#1972/03/21 23:06:47#@@@
BBBB#883#^^^^^^
CCCC#William#-----------------
...... etc

以下代码以与您想要的方式类似的方式提取数据。

不需要字典,元组就足够了。鉴于创建的CSV文件的结构,我定义了funcs = 60 * (to_dt, int, lambda x: x),但您将使用一系列函数,这些函数是您字典的值(已排序)

import re
import csv
from datetime import datetime
from itertools import izip

reg = re.compile('(\d{4})/(\d\d)/(\d\d) (\d\d):(\d\d):(\d\d)')

def to_dt(x, error_data = ('', ' ', '?')):
    if x in error_data:
        return x
    else:
        return datetime(*map(int,reg.match(x).groups()))

def teger(x,  error_data = ('', ' ', '?')):
    if x in error_data:
        return 0
    else:
        return int(x)

funcs = 60 * (to_dt, int, lambda y: y)

with open('zzz.txt','rb') as f:
    rid = csv.reader(f, delimiter='#')
    li = [fct(x[1]) for fct,x in izip(funcs,rid)]



# display
it = (str(el) for el in li).next
print '\n'.join('%-21s %4s  %10s' % (it(),it(),it()) for i in xrange(60))

结果

1972-02-11 08:53:53    557     dearest
1971-10-15 06:55:20    668           ?
1972-07-13 11:10:05    190      infant
1971-11-22 19:31:42    202            
1971-06-12 05:48:39     81        find
1970-12-09 06:26:29     72        find
1972-07-05 10:45:32    270       rooms
1972-06-23 05:52:20    202            
1972-03-21 23:06:47    883     William
1970-02-08 23:47:26    617            
1970-10-08 09:09:33    387     William
1971-04-30 11:05:07    721           ?
1970-02-12 11:57:48    827     Deserts
1972-03-27 21:30:39    363        just
1971-06-02 00:23:52    977            
1970-04-20 04:38:38    113     William
1971-01-20 23:10:26     75       Whose
1971-07-01 12:46:13    352     dearest
1971-01-31 17:01:34    220     William
1970-06-09 20:38:52    148       rooms
1971-08-08 07:42:10    146            
1970-01-28 15:17:41    903        find
...............etc

答案 4 :(得分:0)

我不知道这会更快,但对我来说更清楚:

tlist =  [ 'some datetime value', '12', 'string', .... ]

#convert it to: [ datetime object, 12, 'string', ....]

error_data = set(['', ' ', '?', ...])

def s(x):
    return x

def d(x):
    return datetime(x) if x not in error_data else x

def i(x):
    return int(x) if x not in error_data else 0

types = [ d, i, s, s, s, i, i, d, i, ... ]

result = [ t(x) for t, x in zip(types, tlist) ]

正如其他人所提到的,我正在使用一个集合作为错误值,这将比你的列表更快。

答案 5 :(得分:0)

作为utdemir答案的一个变体,如果错误值很少,那么你可以优化常见的情况:

>>> values = ["12", "a", "bcd", "2.2"]
>>> types = [int, int, str, float]
>>> defaults = {int: 0, float: 0.0}
>>> try: res = [f(v) for v,f in zip(values,types)]
... except: 
...     res = []
...     for v, f in zip(values, types):
...        try:
...             res.append(f(v))
...         except ValueError:
...             res.append(defaults[f])

即,首先尝试转换整行,假设什么都不会出错。如果出现任何问题,请返回并一次转换一个值,修复任何错误值。

答案 6 :(得分:0)

谢谢你们所有这些方法。是的,我几乎尝试了所提到的所有方法,但没有一个表现良好。

我尝试了以下方法,它对我的​​性能需求非常有效。这就是我所做的。

  1. 我按照utdemir
  2. 的建议处理了日期时间值
  3. 我为所有int错误值插入值0,代码如 -

    [i] = value if value != '' else 0

  4. 我没有使用字典按值强制值,而是使用列表一次强制所有值。

    def coerce(l):
    return [ l[0], int(l[1]), int(l[2]) ... ]

  5. 我的观察:

    1. 单独处理错误情况并立即强制执行有很大帮助。
    2. 让代码从异常中恢复需要花费很多时间。
    3. 多次查看列表并没有真正受到伤害(我是为处理错误案例而做的)
    4. 为了给出一个想法,对于几个小时的数据(60万条记录),我的方法将运行时间从6-7分钟缩短到2分30秒