Excel CSV 转换为嵌套字典;列表推导式
我有一个包含员工记录的Excel CSV文件,内容大概是这样的:
mail,first_name,surname,employee_id,manager_id,telephone_number
blah@blah.com,john,smith,503422,503423,+65(2)3423-2433
foo@blah.com,george,brown,503097,503098,+65(2)3423-9782
....
我正在使用DictReader把这些数据放进一个嵌套的字典里:
import csv
gd_extract = csv.DictReader(open('filename 20100331 original.csv'), dialect='excel')
employees = dict([(row['employee_id'], row) for row in gp_extract])
这样做是不是正确的方式?虽然它能工作,但这真的是最好的方法吗?有没有更高效的做法?还有一件有趣的事,在IDLE里,如果我尝试打印“employees”,IDLE似乎会崩溃(大约有1051行数据)。
2. 从内部字典中移除employee_id
第二个问题是,我把数据放进一个以employee_id为索引的字典,值是一个包含所有值的嵌套字典。但是,employee_id在嵌套字典里也作为一个键值存在,这样有点重复。有没有办法把它从内部字典中排除掉?
3. 在列表推导中处理数据
第三,我们需要对导入的数据进行一些处理。例如,所有的电话号码格式都不对,所以我们需要用正则表达式来处理一下。此外,我们还需要把manager_id转换成实际的经理名字和他们的电子邮件地址。大多数经理的信息在同一个文件里,而其他一些则在一个外部承包商的CSV文件中,格式类似但不完全相同,不过我可以把它导入到一个单独的字典里。
这两个任务可以在一个列表推导中完成吗?还是说我应该使用for循环?或者多个列表推导可以一起用吗?(如果能给个示例代码就太好了)。或者在Python中有没有更聪明的做法?
谢谢,
Victor
1 个回答
你提到的第一个部分有一个简单的问题(其实可能根本算不上问题)。你没有处理键的冲突(除非你打算直接覆盖)。
>>> dict([('a', 'b'), ('a', 'c')])
{'a': 'c'}
如果你能保证 employee_id
是唯一的,那就没什么问题。
第二点,当然你可以把它排除掉,但其实没什么坏处。实际上,特别是在Python中,如果 employee_id
是字符串或整数(或者其他一些基本类型),内部字典的引用和键实际上指向的是同一个东西。它们都指向内存中的同一个位置。唯一的重复只是引用(这并不算大)。如果你担心内存消耗,其实可能不需要太担心。
第三点,不要在一个列表推导式中做太多事情。在第一个列表推导式之后,直接使用一个for循环就可以了。
总的来说,听起来你真的很担心循环迭代两次的性能。一开始不要担心性能。 性能问题通常来自算法的问题,而不是像for循环和列表推导式这样的特定语言结构。
如果你熟悉大O符号,列表推导式和之后的for循环(如果你决定这样做)都有一个大O为O(n)。把它们加在一起你会得到O(2n),但根据大O符号的定义,我们可以把它简化为O(n)。我在这里简化了很多,但重点是,你真的不需要担心。
如果有性能方面的担忧,可以等你写完代码后再提出来,并用代码分析工具来验证。
对评论的回应
关于你的第二个回复,Python其实没有很多机制来让一行代码看起来特别好看和炫酷。它的设计是为了让你简单地写出代码,而不是把所有东西都塞在一行里。话虽如此,还是可以在一行中做很多事情。我的建议是,不要担心你能把多少代码放在一行里。Python写得更清晰(在我看来)时,代码是分开的,而不是挤在一行里。
关于你的第一个回复,你可以尝试这样的方式:
employees = {}
for row in gd_extract:
if row['employee_id'] in employees:
... handle duplicates in employees dictionary ...
else:
employees[row['employee_id']] = row
至于你的第三个回复,我不太确定你想要什么,以及你想修复的电话号码是什么,但……这可能会给你一个开始:
import re
retelephone = re.compile(r'[-\(\)\s]') # remove dashes, open/close parens, and spaces
for empid, row in employees.iteritems():
retelephone.sub('',row['telephone'])