为什么字符串转换会减慢我的代码?

Why are string conversions slowing down my code?

我是 python 的新手,编写并编写了以下代码:

如您所见,我的 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")
  1. 不要逐行阅读,阅读整个文件。
  2. 对整个文件使用 re.findall() 以获得所有匹配的数字。
  3. 使用仅 returns 数字的正则表达式,因此您无需使用 re.sub() 删除多余的字符。您可以使用前瞻来匹配 M 而无需将其包含在结果中。
  4. 计数前无需对单词进行排序。
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])

让我们来看看这段代码的作用,给定您的示例输入:

  1. 它读取行 74M2S
    1. 它在该行中找到 74M 并将其放入列表中。
    2. 它对列表进行排序。
    3. 它从 74M 中搜索并删除 M
  2. 它读取行 73M
    1. 它找到 73M 并将其放入列表中。
    2. 它对列表进行排序。
    3. 它从 74M73M 中搜索并删除 M
  3. 它读取行 74M2S
    1. 它找到 74M 并将其放入列表中。
    2. 它对列表进行排序。
    3. 它从 74M73M74M 中搜索并删除 M
  4. 它读取行 *
    1. 它什么也没找到并将其放入列表中。
    2. 它对列表进行排序。
    3. 它从 74M73M74M 中搜索并删除 M
  5. 它读取行 73M
    1. 它找到 73M 并将其放入列表中。
    2. 它对列表进行排序。
    3. 它从 74M73M74M73M 中搜索并删除 M
  6. 它读取行 75M1S
    1. 它找到 75M 并将其放入列表中。
    2. 它对列表进行排序。
    3. 它从 74M73M74M73M75M 中搜索并删除 M

查看子步骤 3 及其进展情况。请注意,您在已经搜索过的字符串中反复搜索 M,一遍又一遍。除了首次读取时,每一行都会冗余搜索特殊字符,以便为它之后的每一行删除一次。如果你有一百万行,第一行会被扫描一百万次。这是此代码中主要的时间浪费。通过扫描单独的行而不是整个列表,对每一行扫描一次,并且只扫描一次。

子步骤 2 也毫无意义地多余。如果你有一百万行,那么你会对这些行中越来越大的部分进行一百万次排序。如果排序真的很重要,可以在最后完成一次,当所有行都已经在列表中时。