更有效的方法从Python中的大文件中选择部分记录

时间:2016-03-01 19:35:45

标签: python performance

我想基于给定的ID从大文件(列表,10M+行)中过滤记录。

selected_id = list()            # 70k+ elements

for line in in_fp:              # input file: 10M+ lines
    id = line.split()[0]        # id (str type), such as '10000872820081804' 

    if id in selected_id:
        out_fp.write(line)

以上代码非常耗时。我想到了一个想法。将selected_id存储为dict而不是list

有更好的解决方案吗?

2 个答案:

答案 0 :(得分:1)

首先,为了从你的行中获取第一列,你可以使用csv模块读取你的文件,使用正确的分隔符,使用zip()函数(在python 3和pyhton 2 {{ 1}})和itertools.izip()函数,以获取第一列,然后将结果传递给next()函数,以保留唯一值。

set()

如果您想保留订单,可以使用import csv with open('file_name') as f: spam_reader = csv.reader(f, delimiter=' ') unique_ids = set(next(zip(*spam_reader)))

collections.OrderedDict()

答案 1 :(得分:1)

你有一些问题,但只有第一个问题真的很讨厌:

  1. (到目前为止,最有可能的代价)检查list的成员身份是O(n);对于70K元素list,这是很多工作。设为set / frozenset,查询通常为O(1),可节省数千次比较。如果类型不可删除,您可以预先sort selected_list并使用bisect模块在​​O(log n)时间内进行查找,这仍然会达到多个数量级如此大的list加速。
  2. 如果你的线很大,有几个空格,所有点都会分裂浪费时间;您可以指定maxsplit仅拆分足以获取ID
  3. 如果ID总是整数值,可能值得花时间selected_id存储int而不是str并在读取时进行转换以便查找比较运行得快一点(这需要测试)。这可能不会产生重大影响,所以我将从示例中省略它。
  4. 结合所有建议:

    selected_id = frozenset(... Your original list of 70k+ str elements ...)
    
    for line in in_fp:               # input file: 10M+ lines
        id, _ = line.split(None, 1)  # id (str type), such as '10000872820081804' 
    
        if id in selected_id:
            out_fp.write(line)
    

    您甚至可以使用生成器表达式将for循环转换为单个调用(尽管它有点过于紧凑),这会将更多工作推送到CPython中的C层,从而减少Python字节代码执行开销: / p>

    out_fp.writelines(x for x in in_fp if x.split(None, 1)[0] in selected_id)