高效访问CSV文件中的特定记录-Python

高效访问CSV文件中的特定记录-Python,python,performance,csv,memory-efficient,Python,Performance,Csv,Memory Efficient,我有一个巨大的csv文件,我正在使用Python csv库的DictReader读取它。它有序列号和一些相关信息。在我的应用程序中,我将获取用户提供的序列号列表,并检查这些序列号是否存在于CSV文件中。 第一次实施: reader=csv.DictReader(open('sample.csv','rb')) arr=[1000,7777,3434,2121,9999] for row in reader: if row['Id'] in arr: print row['

我有一个巨大的csv文件,我正在使用Python csv库的DictReader读取它。它有序列号和一些相关信息。在我的应用程序中,我将获取用户提供的序列号列表,并检查这些序列号是否存在于CSV文件中。 第一次实施:

reader=csv.DictReader(open('sample.csv','rb'))
arr=[1000,7777,3434,2121,9999]
for row in reader:
    if row['Id'] in arr:
        print row['Title']
reader=csv.DictReader(open('sample.csv','rb'))
arr=[1000,7777,3434,2121,9999]
arr.sort()
i=0
for row in reader:
    if row['Id']==arr[i]:
        print row['Title']
        i=i+1
但由于我的csv文件包含超过100000个条目,这需要花费太长的时间

第二次执行:

reader=csv.DictReader(open('sample.csv','rb'))
arr=[1000,7777,3434,2121,9999]
for row in reader:
    if row['Id'] in arr:
        print row['Title']
reader=csv.DictReader(open('sample.csv','rb'))
arr=[1000,7777,3434,2121,9999]
arr.sort()
i=0
for row in reader:
    if row['Id']==arr[i]:
        print row['Title']
        i=i+1
但这会产生不明确的结果,即有时它只打印arr中前2个或前3个序列号的标题

我想要一种更有效的方式,直接点击特定的序列号,这可能吗


请不要建议使用linecache或基于行的内容,因为我的标题分布在多行上,因此基本上1条csv记录不等于文件中的1行。

arr在实际代码中有多大?如果它比这个大得多,那么使用一台电视机可能是值得的

arr={1000,7777,3434,2121,9999}

集合具有更快的包含检查,这似乎是这里的主要瓶颈。

如果要多次访问csv文件,请读取一次并将数据保存在随机可访问的索引形式中,如数据库。或者,如果要对其进行过滤以获得可用行的一小部分,则只需进行一次第一步操作,即可扔掉所有垃圾,并编写一个新的较小的csv文件,其中只包含有用的数据

如果您编写的代码从csv文件的这个实例中提取了您需要的所有内容,那么我认为您没有什么可以改进的


至于它工作不正常,您确定在找到arr[0](1000)之前不想查找arr[1](7777)吗?如果希望所有行的行['Id']与arr中的任何内容匹配,而不考虑顺序,则需要测试arr中的
行['Id']。另一个潜在问题是,csv可能在某些行中包含数字1000(甚至999.99999999),在其他行中包含字符串“1000”(或“1000”等),与原始电子表格匹配<代码>1000!=“1000”
“1000”!=“1000”
,因此在比较相等值之前,可能需要进行一些仔细的数据处理。

从算法上讲,您的第一个实现是可行的,但如果速度太慢,则可能需要进行两到三次优化

  • 使用
    集合
    而不是
    列表

  • 使用列表列表而不是字典列表,即不要使用
    csv.DictReeader
    ,而是更简单的
    csv.reader

  • 使用编译的
    re
    匹配目标,并根据编译的
    re
    测试当前id

  • 我写了两个或三个,因为我不太确定第三个是否是真正的优化,如果所有其他都失败了,那么我认为值得测试最后一个可能性,但是。。。
    顺便说一句,什么是10万行?

    您正在尝试读取一个10万行的文本文件,以查找少量匹配项。
    在这些查找之前,我会认真考虑将CSV文件预处理到SqLITE3数据库中。 我怀疑每次用户请求一些查找详细信息时都会提供csv文件,因此这应该是可能的。
    当然,这取决于csv文件的更新频率,但我敢打赌,它不会经常更新。在sqlite数据库中对csv进行一次预处理(用于多次查找)将带来好处

    当你唯一的工具是锤子时,一切看起来都像钉子

    <>编辑:另一件要考虑的事情是,你认为你现在有问题,当CSV文件变为2或3 LAKH时会发生什么。在某个时刻,您将不得不咬紧牙关,要么以某种结构化格式交付csv文件,要么自己构建它。
    还有csv文件包含什么的问题。目前,您无法保证它没有完全重复,这可能会严重扰乱您的处理。如果您对数据应用一种结构,不仅搜索速度会大大加快,而且您还可以确保同时获得干净的数据

    编辑2:

    下面是一个小python脚本,用于创建一个包含20万条记录的数据库。
    显然,在您的情况下,您必须读取csv文件并填充更多字段,但在旧的64位PC上,此简单测试只需4.5秒

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    import os,sqlite3
    db_name = "2lakh.db"
    try:
        os.remove(db_name)
    except:
        pass
    db = sqlite3.connect(db_name)
    cursor = db.cursor()
    result = cursor.execute('CREATE TABLE if not exists Big (Big_id INTEGER NOT NULL PRIMARY KEY UNIQUE, Big_data CHAR)')
    cursor.execute('PRAGMA synchronous = 0') #Hands off data handling to OS
    n = 0
    while n < 200001:
        try:
            db.execute("insert into Big (Big_id,Big_data) values (?,?)",(n,"This is some data"));
        except sqlite3.Error as e:
            print 'Big Insert Error '+str(e), 'Error'
        n += 1
        # only report progress and commit database every 10000 records (speeds things up immensely) 
        if (n % 10000) == 0:
            db.commit()
            print n, "records written"
    db.commit()
    db.close()
    
    #/usr/bin/python
    #-*-编码:utf-8-*-
    导入操作系统,sqlite3
    db_name=“2lakh.db”
    尝试:
    删除操作系统(数据库名称)
    除:
    通过
    db=sqlite3.connect(db\u名称)
    cursor=db.cursor()
    结果=cursor.execute('CREATE TABLE if not exists Big(Big_id INTEGER not NULL PRIMARY KEY UNIQUE,Big_data CHAR)'))
    cursor.execute('PRAGMA synchronous=0')#将数据处理交给操作系统
    n=0
    当n<200001时:
    尝试:
    db.execute(“插入大(Big_id,Big_data)值(?)”,(n,“这是一些数据”);
    除了sqlite3。错误为e:
    打印“大插入错误”+str(e),“错误”
    n+=1
    #每10000条记录只报告进度并提交数据库(大大加快了速度)
    如果(n%10000)==0:
    db.commit()
    打印n,“记录已写入”
    db.commit()
    db.close()
    
    如果仅每100000个事务执行一次
    db.commit()
    ,则创建整个数据库所需的时间不到3秒。
    我希望这能有所帮助。

    不过,你可以试着用一套。如果“第二次实现”对您来说足够快,那么sets可能也会很快或者,您可以尝试使用reader而不是DictReader:这可能会快得多,但您需要分别计算字段的偏移量。我的实用工具需要DictReader,而且我肯定会尝试集合。谢谢@维基,我衷心赞同你对
    DictReader
    的(相对)缓慢的评论@Pranay Mathur A
    DictReader
    可能更方便,但如果它太慢(请注意:我不是说它太慢,我是说你,有数据的人,你必须检查)