我有一个值列表 - 所有字符串。我想将这些值转换为各自的数据类型。我将值映射到可用的类型信息。
有三种不同的数据类型: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个值一样,我需要为成千上万个这样的列表执行此操作。上面代码的性能非常差。最快的方法是什么?
谢谢
答案 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)
谢谢你们所有这些方法。是的,我几乎尝试了所提到的所有方法,但没有一个表现良好。
我尝试了以下方法,它对我的性能需求非常有效。这就是我所做的。
我为所有int错误值插入值0,代码如 -
[i] = value if value != '' else 0
我没有使用字典按值强制值,而是使用列表一次强制所有值。
def coerce(l):
return [ l[0], int(l[1]), int(l[2]) ... ]
我的观察: