如何在python中优化正则表达式搜索? ################################################################ # #用于/r/wallstreetbets中评论的Reddit情绪脚本 #从数据库加载注释,使用单词列表扫描注释, #创建情绪分数并将结果写入数据库 # ################################################################ 导入sqlite3 进口稀土 从日期时间导入日期时间 #从文本文件数据库加载关键字搜索词 def从文件(ftext文件)读取单词: 加载单词=() f=打开(ftext_文件'r') #对于f中的项目: #加载单词.append(item.lower().strip()) load_words=tuple([x.lower().strip(),表示f中的x]) 返回(加载单词) #根据评论搜索关键词 #给出+/-表示积极或消极情绪词 定义单词搜索(数据库列表、FPO列表、fneg列表): 位置计数=0 负计数=0 fdb_结果_列表=[] 扫描的总行数=0 打印(“开始单词搜索…”) #循环的第一个是注释数据 #第二个循环是关键词 有关db_列表中的注释: 总扫描行数=总扫描行数+1 对于fpos清单中的pos项目: word\u search=re.findall(r“\b”+位置项+r“\b”,注释[0]) pos\u count=pos\u count+len(单词搜索) 对于fneg_表中的neg_项: word\u search=re.findall(r“\b”+neg\u item+r“\b”,注释[0]) 负计数=负计数+长(单词搜索) #根据评论中的频率确定pos/neg情绪得分 如果pos_计数>neg_计数: 正计数=正计数/(正计数+负计数) 负计数=0 elif正计数0或负计数>0: fdb_结果列表。追加([pos_计数、neg_计数、注释[1])) 如果扫描的总行数为%100000==0: 打印(“到目前为止统计的行:,扫描的行总数”) 位置计数=0 负计数=0 打印(“单词搜索完成”) 返回(fdb_结果_列表) #将结果写入新数据库。删除奇数db。 #pos=项目[0],neg=项目[1],时间戳=项目[2] def写入数据库结果(写入数据库列表): 打印(“将结果写入数据库…”) conn=sqlite3.connect('testdb.sqlite',超时=30) cur=连接光标() cur.executescript(“”)如果存在,则删除表redditresultstable ''') 当前执行脚本(“”) 创建表redditresultstable( id整数不为NULL主键唯一, pos_count整数, 负计数整数, 时间戳文本 ); ''') 对于写入数据库列表中的项目: 当前执行(''插入redditresultstable(正计数、负计数、时间戳) 数值(?,,?),(第[0]项、第[1]项、第[2]项) 康涅狄格州提交 康涅狄格州关闭 打印(“将结果写入数据库完成”) #从db加载注释项[2]和时间戳项[4] def load_db_comments(): 打印(“正在加载数据库…”) conn=sqlite3.connect('redditusertrack.sqlite')) cur=连接光标() cur.execute('SELECT*FROM redditcomments') row_db=cur.fetchall() 康涅狄格州关闭 打印(“加载完成”) db_列表=() db_list=tuple([(第[2]行中的项为lower(),第[4]行]) 返回db_列表 #主程序从这里开始 打印(datetime.now()) db_list=加载db_注释() pos\u words\u list=从文件(“simple\u positive\u words.txt”)中读取单词 neg_words_list=从_文件(“simple_negative_words.txt”)读取_words_ db_结果_列表=单词搜索(db_列表、pos_单词列表、neg_单词列表) db_results_list=元组(db_results_list) 写入数据库结果(数据库结果列表) 打印(datetime.now())
这个脚本从SQLite将130万条评论加载到内存中,然后针对每条评论扫描147个关键词,然后计算情感分数1.91亿次迭代 执行时间为5分32秒 我将大多数变量更改为元组(来自列表),并使用列表理解而不是循环(用于附加)。与仅使用列表和For循环进行追加时的脚本相比,此脚本的执行效率提高了约5%。5%可能是误差范围,因为我的测量方法可能不准确 Stackoverflow和其他资源似乎表明,对于这种类型的迭代,使用元组更快,尽管一些海报提供的证据表明,在某些情况下,列表更快 此代码是否针对元组和列表理解进行了正确优化 编辑:谢谢大家的建议/评论。有很多工作要做。我实现了YuriyP的建议,运行时间从5分钟增加到了26秒。问题在于regex For循环搜索函数 已更新附加图像中的代码。我删除了红色的划线代码,并用绿色进行了更新如何在python中优化正则表达式搜索? ################################################################ # #用于/r/wallstreetbets中评论的Reddit情绪脚本 #从数据库加载注释,使用单词列表扫描注释, #创建情绪分数并将结果写入数据库 # ################################################################ 导入sqlite3 进口稀土 从日期时间导入日期时间 #从文本文件数据库加载关键字搜索词 def从文件(ftext文件)读取单词: 加载单词=() f=打开(ftext_文件'r') #对于f中的项目: #加载单词.append(item.lower().strip()) load_words=tuple([x.lower().strip(),表示f中的x]) 返回(加载单词) #根据评论搜索关键词 #给出+/-表示积极或消极情绪词 定义单词搜索(数据库列表、FPO列表、fneg列表): 位置计数=0 负计数=0 fdb_结果_列表=[] 扫描的总行数=0 打印(“开始单词搜索…”) #循环的第一个是注释数据 #第二个循环是关键词 有关db_列表中的注释: 总扫描行数=总扫描行数+1 对于fpos清单中的pos项目: word\u search=re.findall(r“\b”+位置项+r“\b”,注释[0]) pos\u count=pos\u count+len(单词搜索) 对于fneg_表中的neg_项: word\u search=re.findall(r“\b”+neg\u item+r“\b”,注释[0]) 负计数=负计数+长(单词搜索) #根据评论中的频率确定pos/neg情绪得分 如果pos_计数>neg_计数: 正计数=正计数/(正计数+负计数) 负计数=0 elif正计数0或负计数>0: fdb_结果列表。追加([pos_计数、neg_计数、注释[1])) 如果扫描的总行数为%100000==0: 打印(“到目前为止统计的行:,扫描的行总数”) 位置计数=0 负计数=0 打印(“单词搜索完成”) 返回(fdb_结果_列表) #将结果写入新数据库。删除奇数db。 #pos=项目[0],neg=项目[1],时间戳=项目[2] def写入数据库结果(写入数据库列表): 打印(“将结果写入数据库…”) conn=sqlite3.connect('testdb.sqlite',超时=30) cur=连接光标() cur.executescript(“”)如果存在,则删除表redditresultstable ''') 当前执行脚本(“”) 创建表redditresultstable( id整数不为NULL主键唯一, pos_count整数, 负计数整数, 时间戳文本 ); ''') 对于写入数据库列表中的项目: 当前执行(''插入redditresultstable(正计数、负计数、时间戳) 数值(?,,?),(第[0]项、第[1]项、第[2]项) 康涅狄格州提交 康涅狄格州关闭 打印(“将结果写入数据库完成”) #从db加载注释项[2]和时间戳项[4] def load_db_comments(): 打印(“正在加载数据库…”) conn=sqlite3.connect('redditusertrack.sqlite')) cur=连接光标() cur.execute('SELECT*FROM redditcomments') row_db=cur.fetchall() 康涅狄格州关闭 打印(“加载完成”) db_列表=() db_list=tuple([(第[2]行中的项为lower(),第[4]行]) 返回db_列表 #主程序从这里开始 打印(datetime.now()) db_list=加载db_注释() pos\u words\u list=从文件(“simple\u positive\u words.txt”)中读取单词 neg_words_list=从_文件(“simple_negative_words.txt”)读取_words_ db_结果_列表=单词搜索(db_列表、pos_单词列表、neg_单词列表) db_results_list=元组(db_results_list) 写入数据库结果(数据库结果列表) 打印(datetime.now()),python,regex,performance,Python,Regex,Performance,这个脚本从SQLite将130万条评论加载到内存中,然后针对每条评论扫描147个关键词,然后计算情感分数1.91亿次迭代 执行时间为5分32秒 我将大多数变量更改为元组(来自列表),并使用列表理解而不是循环(用于附加)。与仅使用列表和For循环进行追加时的脚本相比,此脚本的执行效率提高了约5%。5%可能是误差范围,因为我的测量方法可能不准确 Stackoverflow和其他资源似乎表明,对于这种类型的迭代,使用元组更快,尽管一些海报提供的证据表明,在某些情况下,列表更快 此代码是否针对元组和列表
您应该首先使用cProfile来分析代码,以便更好地进行度量。是的,元组比列表快一点点,因为列表需要2块内存,元组需要1块内存。而且,列表理解也稍微快一点,但前提是循环中有非常简单的表达式
################################################################
#
# Reddit sentiment script for comments in /r/wallstreetbets
# Loads comments from db, scans comments using word list,
# creates sentiment score and writes results to database
#
################################################################
import sqlite3
import re
from datetime import datetime
# Load key search words from text file db
def read_words_from_file(ftext_file):
load_words = ()
f = open(ftext_file, 'r')
# for item in f:
# load_words.append(item.lower().strip())
load_words = tuple([x.lower().strip() for x in f])
return(load_words)
# Search key words against comments
# Gives +/- for positive or negative sentiment words
def word_search(db_list, fpos_flist, fneg_flist):
pos_count = 0
neg_count = 0
fdb_results_list = []
total_lines_scanned = 0
print("Starting word search...")
# 1st for loop is comment data
# 2nd for loop is key words
for comment in db_list:
total_lines_scanned = total_lines_scanned + 1
for pos_item in fpos_flist:
word_search = re.findall(r"\b" + pos_item + r"\b", comment[0])
pos_count = pos_count + len(word_search)
for neg_item in fneg_flist:
word_search = re.findall(r"\b" + neg_item + r"\b", comment[0])
neg_count = neg_count + len(word_search)
# Determine pos/neg sentiment score based on frequency in comment
if pos_count > neg_count:
pos_count = pos_count / (pos_count+neg_count)
neg_count = 0
elif pos_count < neg_count:
neg_count = neg_count / (pos_count+neg_count)
pos_count = 0
elif pos_count == neg_count:
pos_count = 0
neg_count = 0
if pos_count > 0 or neg_count > 0:
fdb_results_list.append([pos_count, neg_count, comment[1]])
if total_lines_scanned % 100000 == 0:
print("Lines counted so far:", total_lines_scanned)
pos_count = 0
neg_count = 0
print("Word search complete.")
return(fdb_results_list)
# Write results to new DB. Deletes odd db.
# pos = item[0], neg = item[1], timestamp = item[2]
def write_db_results(write_db_list):
print("Writing results to database...")
conn = sqlite3.connect('testdb.sqlite', timeout=30)
cur = conn.cursor()
cur.executescript('''DROP TABLE IF EXISTS redditresultstable
''')
cur.executescript('''
CREATE TABLE redditresultstable (
id INTEGER NOT NULL PRIMARY KEY UNIQUE,
pos_count INTEGER,
neg_count INTEGER,
timestamp TEXT
);
''')
for item in write_db_list:
cur.execute('''INSERT INTO redditresultstable (pos_count, neg_count, timestamp)
VALUES (?, ?, ?)''', (item[0], item[1], item[2]))
conn.commit()
conn.close()
print("Writing results to database complete.")
# Load comments item[2] and timestamp item[4] from db
def load_db_comments():
print("Loading database...")
conn = sqlite3.connect('redditusertrack.sqlite')
cur = conn.cursor()
cur.execute('SELECT * FROM redditcomments')
row_db = cur.fetchall()
conn.close()
print("Loading complete.")
db_list = ()
db_list = tuple([(item[2].lower(), item[4]) for item in row_db])
return db_list
# Main Program Starts Here
print(datetime.now())
db_list = load_db_comments()
pos_word_list = read_words_from_file("simple_positive_words.txt")
neg_word_list = read_words_from_file("simple_negative_words.txt")
db_results_list = word_search(db_list, pos_word_list, neg_word_list)
db_results_list = tuple(db_results_list)
write_db_results(db_results_list)
print(datetime.now())
在代码中可以做两件事,比如使用枚举器for代替裸for循环,并用简单的生成器替换理解
我认为,为这样简单的逻辑分析代码并试图使其更快,可能不会带来任何好处。另外,不要使用datetime
而是使用类似于配置文件装饰器的东西,它将锁定到函数中,并为您提供调用报告
tuple((item[2].lower(), item[4]) for item in row_db) -> you don't need list comprehensions here.
祝你好运 使用正则表达式从commaent获取正反两个单词的总数,而不是对每个正反两个单词进行O(N+M)请求,而是使用O(1) 例子:
pos\u word\u list=从\u文件中读取\u words\u(“简单的\u肯定的\u wo
tuple((item[2].lower(), item[4]) for item in row_db) -> you don't need list comprehensions here.
import cProfile, pstats, io
def profile(fnc):
"""A decorator that uses cProfile to profile a function"""
def inner(*args, **kwargs):
pr = cProfile.Profile()
pr.enable()
retval = fnc(*args, **kwargs)
pr.disable()
s = io.StringIO()
sortby = 'cumulative'
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print(s.getvalue())
return retval
return inner