繁体   English   中英

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

[英]Fastest way to cast values to their respective datatypes in Python

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

共有三种不同的数据类型: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个值,我需要对成千上万个这样的列表进行转换。 以上代码的性能非常差。 最快的方法是什么?

谢谢

就像是:

>>> 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)复杂度,因此这不应该成为问题。

如果您的日期时间值始终是一致的,为什么不让类型转换处理您要在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)]

由于您知道要转换为的类型,因此尝试优化转换可能不会提高性能。 糟糕的性能可能是由于反复对error_data迭代。 如果可能,请将您的error_data列表重新error_data为一set以利用该类型的性质:

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

然后照常进行。 进一步的改进将需要对您的代码进行概要分析,以实际确定将时间花费在哪里。

您的代码中存在不一致之处:

如果列表中的所有元素都是字符串,则不能以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

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

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) ]

正如其他人提到的那样,我正在使用一组错误值,这将比您的列表更快。

作为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])

即,首先假设没有问题,请尝试转换整行。 如果发生任何错误,请返回并一次转换一个值,并修复所有错误值。

谢谢你们提供的所有这些方法。 是的,我几乎尝试了所有提及的方法,但是效果都不佳。

我尝试了以下方法,该方法非常适合我的性能需求。 这就是我所做的。

  1. 我按照utdemir的建议处理了日期时间值
  2. 我已使用以下代码为所有int错误值插入了值0:

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

  3. 我没有使用字典强制逐个值,而是使用列表一次强制了所有值。

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

我的观察:

  1. 分别处理错误情况并立即进行强制操作很有帮助。
  2. 让代码从异常中恢复需要花费大量时间。
  3. 多次遍历列表并没有真正的伤害(我这样做是为了处理错误情况)
  4. 首先,我花了几个小时的数据(60万条记录),将运行时间从6-7分钟缩短到2m30s

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM