高效地访问 CSV 文件中的特定记录- Python
Accessing a particular record in a CSV FIle Efficiently- Python
我有一个巨大的 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']
但这需要很长时间,因为我的 csv 文件包含超过 100 000 个条目
第二次实施:
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 个序列 no.s 打印标题
我想要一种更有效的方式和一种直接命中特定序列号的方式,这可能吗?
请不要建议 linecache 或基于行的东西,因为我的标题分布在多行中,所以基本上 1 个 csv 记录不等于文件中的 1 行。
你的真实代码中的 arr 有多大?如果比这个大很多,用一套应该是划算的。
arr={1000,7777,3434,2121,9999}
集合的包含检查要快得多,这似乎是这里的主要瓶颈。
如果要多次访问csv文件,读取一次并将数据保存在随机可访问的索引形式中,例如数据库。或者,如果您正在过滤它以获得可用行的一小部分,请进行一次第一次传递以丢弃所有某些垃圾并编写一个仅包含有用数据的新的较小的 csv 文件。
如果您编写的代码从 csv 文件的这个实例中提取了您需要的所有内容,我认为您可以做很多改进。
至于它不能正常工作,您确定在找到 arr[0] (1000) 之前不想查找 arr[1] (7777) 吗?如果您希望所有具有 row['Id'] 的行与 arr 中的任何内容匹配而不考虑顺序,则需要测试 row['Id'] in arr
。另一个潜在的问题是 csv 可能在某些行上包含数字 1000(甚至 999.999999999),在其他行上包含字符串“1000”(或“1000”等),与原始电子表格相匹配。 1000 != "1000"
和 "1000" != "1000 "
,因此在比较值是否相等之前可能需要仔细处理数据。
从算法上讲,您的第一个实现是可行的方法,但如果它太慢,您有两个或三个可能的优化。
使用 set
而不是 list
。
使用列表的列表而不是字典的列表,即不使用 csv.DictReeader
而使用更简单的 csv.reader
.
使用已编译的 re
来匹配您的目标并根据已编译的 re
.
测试当前 ID
我写了两三个,因为我不太确定第三个是真正的优化,如果所有其他方法都失败了,那么恕我直言,值得测试最后一种可能性,但是......
顺便说一句,什么是十万?
您正在尝试读取一个 100,000 行的文本文件以查找少量匹配项。
我会认真考虑在这些查找之前将该 csv 文件预处理到 sqlite3 数据库中。
我怀疑每次用户请求一些查找详细信息时都会提供 csv 文件,所以应该可以。
当然,这取决于 csv 文件的更新频率,但我敢打赌它不会那么频繁。将 csv 单次预处理到 sqlite 数据库中,用于多次查找会带来好处。
当你唯一的工具是锤子时,一切看起来都像钉子!
编辑:要考虑的另一件事是,您认为您现在遇到了问题,当 csv 文件的大小变成 2 或 30 万时会发生什么。在某些时候,您将不得不硬着头皮,要么以某种结构化格式交付 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()
如果您每 100,000 个事务只执行 db.commit()
,则创建整个数据库所用的时间不到 3 秒。
希望对您有所帮助。
我有一个巨大的 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']
但这需要很长时间,因为我的 csv 文件包含超过 100 000 个条目
第二次实施:
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 个序列 no.s 打印标题我想要一种更有效的方式和一种直接命中特定序列号的方式,这可能吗?
请不要建议 linecache 或基于行的东西,因为我的标题分布在多行中,所以基本上 1 个 csv 记录不等于文件中的 1 行。
你的真实代码中的 arr 有多大?如果比这个大很多,用一套应该是划算的。
arr={1000,7777,3434,2121,9999}
集合的包含检查要快得多,这似乎是这里的主要瓶颈。
如果要多次访问csv文件,读取一次并将数据保存在随机可访问的索引形式中,例如数据库。或者,如果您正在过滤它以获得可用行的一小部分,请进行一次第一次传递以丢弃所有某些垃圾并编写一个仅包含有用数据的新的较小的 csv 文件。
如果您编写的代码从 csv 文件的这个实例中提取了您需要的所有内容,我认为您可以做很多改进。
至于它不能正常工作,您确定在找到 arr[0] (1000) 之前不想查找 arr[1] (7777) 吗?如果您希望所有具有 row['Id'] 的行与 arr 中的任何内容匹配而不考虑顺序,则需要测试 row['Id'] in arr
。另一个潜在的问题是 csv 可能在某些行上包含数字 1000(甚至 999.999999999),在其他行上包含字符串“1000”(或“1000”等),与原始电子表格相匹配。 1000 != "1000"
和 "1000" != "1000 "
,因此在比较值是否相等之前可能需要仔细处理数据。
从算法上讲,您的第一个实现是可行的方法,但如果它太慢,您有两个或三个可能的优化。
使用
set
而不是list
。使用列表的列表而不是字典的列表,即不使用
csv.DictReeader
而使用更简单的csv.reader
.使用已编译的
re
来匹配您的目标并根据已编译的re
. 测试当前 ID
我写了两三个,因为我不太确定第三个是真正的优化,如果所有其他方法都失败了,那么恕我直言,值得测试最后一种可能性,但是...... 顺便说一句,什么是十万?
您正在尝试读取一个 100,000 行的文本文件以查找少量匹配项。
我会认真考虑在这些查找之前将该 csv 文件预处理到 sqlite3 数据库中。
我怀疑每次用户请求一些查找详细信息时都会提供 csv 文件,所以应该可以。
当然,这取决于 csv 文件的更新频率,但我敢打赌它不会那么频繁。将 csv 单次预处理到 sqlite 数据库中,用于多次查找会带来好处。
当你唯一的工具是锤子时,一切看起来都像钉子!
编辑:要考虑的另一件事是,您认为您现在遇到了问题,当 csv 文件的大小变成 2 或 30 万时会发生什么。在某些时候,您将不得不硬着头皮,要么以某种结构化格式交付 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()
如果您每 100,000 个事务只执行 db.commit()
,则创建整个数据库所用的时间不到 3 秒。
希望对您有所帮助。