为什么字符串转换会减慢我的代码?
Why are string conversions slowing down my code?
我是 python 的新手,编写并编写了以下代码:
- 读取文本文件。
- 保存在列表中。
- 执行正则表达式。
- 将列表转换为字符串
- 删除不需要的特殊字符。
- 将内容放回列表中。
- 在列表中使用一个计数器,然后将它们打包到字典中。
- 最终使用 Pandas 绘制键和值。
如您所见,我的 Python 经验很低。我的代码非常适合较小的文件,但是当我使用类似 700 MB 的文件时,它似乎永远 运行!
如何优化我的代码?
这是我的输入文件格式。
74M2S
73M
74M2S
*
73M
75M1S
这是我的代码:
import matplotlib.pyplot as plt
import re
import pandas as pd
from collections import Counter
f = open('/PathTpFile/MyFILE.txt','r+')
listToStr: str
str2: str
mylist1 = []
for line in f.readlines():
mylist1.append([re.findall(r'[\d]+M', line)])
mylist1.sort(reverse=True)
listToStr = ' '.join(map(str, mylist1))
specialChars = "M[]'"
for specialChar in specialChars:
listToStr = listToStr.replace(specialChar, '')
words: list = listToStr.split()
counts = Counter(words)
dict(counts)
print(counts)
f.close()
keys = counts.keys()
values = counts.values()
print(counts.keys())
print(counts.values())
plt.bar(keys, values)
plt.savefig("out.png")
- 不要逐行阅读,阅读整个文件。
- 对整个文件使用
re.findall()
以获得所有匹配的数字。
- 使用仅 returns 数字的正则表达式,因此您无需使用
re.sub()
删除多余的字符。您可以使用前瞻来匹配 M
而无需将其包含在结果中。
- 计数前无需对单词进行排序。
with open('/PathTpFile/MyFILE.txt','r') as f:
text: str = f.read()
mylist1: list = re.findall(r'\d+(?=M)', text)
counts: dict = Counter(mylist1)
对于大文件,按行的方法是可以的,但是每行的正则表达式就有点矫枉过正了。您可以检查每一行是否包含 'M'
如果是,则将其拆分并将第一部分立即添加到计数字典中:
with open("d.txt","w") as f:
f.write("""74M2S
73M
74M2S
*
73M
75M1S""")
counts = {}
with open("d.txt") as f:
for line in f:
if "M" in line:
key = line.split("M")[0]
counts.setdefault(key,0)
counts[key] += 1
print(counts.keys())
print(counts.values())
如果 dict.setdefault
减慢了速度,您可以使用 defaultdict(int)
轻松加快速度 - 但这应该足够快了。
输出:
dict_keys(['74', '73', '75'])
dict_values([2, 2, 1])
让我们来看看这段代码的作用,给定您的示例输入:
- 它读取行
74M2S
。
- 它在该行中找到
74M
并将其放入列表中。
- 它对列表进行排序。
- 它从
74M
中搜索并删除 M
。
- 它读取行
73M
。
- 它找到
73M
并将其放入列表中。
- 它对列表进行排序。
- 它从
74M
和 73M
中搜索并删除 M
。
- 它读取行
74M2S
。
- 它找到
74M
并将其放入列表中。
- 它对列表进行排序。
- 它从
74M
、73M
和 74M
中搜索并删除 M
。
- 它读取行
*
。
- 它什么也没找到并将其放入列表中。
- 它对列表进行排序。
- 它从
74M
、73M
和 74M
中搜索并删除 M
。
- 它读取行
73M
。
- 它找到
73M
并将其放入列表中。
- 它对列表进行排序。
- 它从
74M
、73M
、74M
和 73M
中搜索并删除 M
。
- 它读取行
75M1S
。
- 它找到
75M
并将其放入列表中。
- 它对列表进行排序。
- 它从
74M
、73M
、74M
、73M
和 75M
中搜索并删除 M
。
查看子步骤 3 及其进展情况。请注意,您在已经搜索过的字符串中反复搜索 M
,一遍又一遍。除了首次读取时,每一行都会冗余搜索特殊字符,以便为它之后的每一行删除一次。如果你有一百万行,第一行会被扫描一百万次。这是此代码中主要的时间浪费。通过扫描单独的行而不是整个列表,对每一行扫描一次,并且只扫描一次。
子步骤 2 也毫无意义地多余。如果你有一百万行,那么你会对这些行中越来越大的部分进行一百万次排序。如果排序真的很重要,可以在最后完成一次,当所有行都已经在列表中时。
我是 python 的新手,编写并编写了以下代码:
- 读取文本文件。
- 保存在列表中。
- 执行正则表达式。
- 将列表转换为字符串
- 删除不需要的特殊字符。
- 将内容放回列表中。
- 在列表中使用一个计数器,然后将它们打包到字典中。
- 最终使用 Pandas 绘制键和值。
如您所见,我的 Python 经验很低。我的代码非常适合较小的文件,但是当我使用类似 700 MB 的文件时,它似乎永远 运行!
如何优化我的代码?
这是我的输入文件格式。
74M2S
73M
74M2S
*
73M
75M1S
这是我的代码:
import matplotlib.pyplot as plt
import re
import pandas as pd
from collections import Counter
f = open('/PathTpFile/MyFILE.txt','r+')
listToStr: str
str2: str
mylist1 = []
for line in f.readlines():
mylist1.append([re.findall(r'[\d]+M', line)])
mylist1.sort(reverse=True)
listToStr = ' '.join(map(str, mylist1))
specialChars = "M[]'"
for specialChar in specialChars:
listToStr = listToStr.replace(specialChar, '')
words: list = listToStr.split()
counts = Counter(words)
dict(counts)
print(counts)
f.close()
keys = counts.keys()
values = counts.values()
print(counts.keys())
print(counts.values())
plt.bar(keys, values)
plt.savefig("out.png")
- 不要逐行阅读,阅读整个文件。
- 对整个文件使用
re.findall()
以获得所有匹配的数字。 - 使用仅 returns 数字的正则表达式,因此您无需使用
re.sub()
删除多余的字符。您可以使用前瞻来匹配M
而无需将其包含在结果中。 - 计数前无需对单词进行排序。
with open('/PathTpFile/MyFILE.txt','r') as f:
text: str = f.read()
mylist1: list = re.findall(r'\d+(?=M)', text)
counts: dict = Counter(mylist1)
对于大文件,按行的方法是可以的,但是每行的正则表达式就有点矫枉过正了。您可以检查每一行是否包含 'M'
如果是,则将其拆分并将第一部分立即添加到计数字典中:
with open("d.txt","w") as f:
f.write("""74M2S
73M
74M2S
*
73M
75M1S""")
counts = {}
with open("d.txt") as f:
for line in f:
if "M" in line:
key = line.split("M")[0]
counts.setdefault(key,0)
counts[key] += 1
print(counts.keys())
print(counts.values())
如果 dict.setdefault
减慢了速度,您可以使用 defaultdict(int)
轻松加快速度 - 但这应该足够快了。
输出:
dict_keys(['74', '73', '75'])
dict_values([2, 2, 1])
让我们来看看这段代码的作用,给定您的示例输入:
- 它读取行
74M2S
。- 它在该行中找到
74M
并将其放入列表中。 - 它对列表进行排序。
- 它从
74M
中搜索并删除M
。
- 它在该行中找到
- 它读取行
73M
。- 它找到
73M
并将其放入列表中。 - 它对列表进行排序。
- 它从
74M
和73M
中搜索并删除M
。
- 它找到
- 它读取行
74M2S
。- 它找到
74M
并将其放入列表中。 - 它对列表进行排序。
- 它从
74M
、73M
和74M
中搜索并删除M
。
- 它找到
- 它读取行
*
。- 它什么也没找到并将其放入列表中。
- 它对列表进行排序。
- 它从
74M
、73M
和74M
中搜索并删除M
。
- 它读取行
73M
。- 它找到
73M
并将其放入列表中。 - 它对列表进行排序。
- 它从
74M
、73M
、74M
和73M
中搜索并删除M
。
- 它找到
- 它读取行
75M1S
。- 它找到
75M
并将其放入列表中。 - 它对列表进行排序。
- 它从
74M
、73M
、74M
、73M
和75M
中搜索并删除M
。
- 它找到
查看子步骤 3 及其进展情况。请注意,您在已经搜索过的字符串中反复搜索 M
,一遍又一遍。除了首次读取时,每一行都会冗余搜索特殊字符,以便为它之后的每一行删除一次。如果你有一百万行,第一行会被扫描一百万次。这是此代码中主要的时间浪费。通过扫描单独的行而不是整个列表,对每一行扫描一次,并且只扫描一次。
子步骤 2 也毫无意义地多余。如果你有一百万行,那么你会对这些行中越来越大的部分进行一百万次排序。如果排序真的很重要,可以在最后完成一次,当所有行都已经在列表中时。