【问题标题】:Why are string conversions slowing down my code?为什么字符串转换会减慢我的代码速度?
【发布时间】:2021-06-15 04:35:04
【问题描述】:

我是 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")

【问题讨论】:

  • 为什么要将findall() 结果包装在另一个列表中:[re.findall(r'[\d]+M', line)]?
  • 为什么使用[\d]+M而不是\d+M?
  • 为什么每次循环都重新分配words?最后做一次。
  • 你每次都在通过for line 循环做很多事情,应该在最后完成。
  • 如果您的目标只是计算字数,为什么还需要对任何内容进行排序?

标签: regex string list substring counter


【解决方案1】:
  1. 不要逐行读取,要读取整个文件。
  2. 对整个文件使用re.findall() 以获取所有匹配的数字。
  3. 使用只返回数字的正则表达式,因此您不需要使用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)

【讨论】:

    【解决方案2】:

    根据您的示例输入,让我们看一下这段代码的作用:

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

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

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

    【讨论】:

      【解决方案3】:

      对于大文件,逐行方法是可以的,但是对于每一行的正则表达式是多余的。您可以检查每一行是否包含'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])
      

      【讨论】:

        猜你喜欢
        • 2018-11-30
        • 2012-01-02
        • 2019-01-03
        • 1970-01-01
        • 1970-01-01
        • 2018-01-02
        • 2017-01-16
        • 2019-05-29
        • 1970-01-01
        相关资源
        最近更新 更多