python - 当读取具有某些字符的某些数据时, string.count() 会大幅减慢

标签 python string performance

我需要读取一个大型 csv 数据文件,但是该文件充满了换行符,而且通常非常困惑。因此,我不是使用 pandas 手动执行此操作,但是我遇到了奇怪的减速问题,这似乎取决于文件中出现的字符。

在尝试通过随机创建一个看起来相似的 csv 文件来重现问题时,我认为问题可能出在 count 函数中。

考虑这个示例,它创建一个包含混沌随机数据的大文件,读取该文件,然后使用计数对其进行排序,以便可以将其作为柱状数据读取。

请注意,在文件的第一次运行中,我仅使用 string.ascii_letters 作为随机数据,第二次运行时,我使用 string.printable 中的字符.

import os
import random as rd
import string
import time

# Function to create random data in a specific pattern with separator ";":
def createRandomString(num,io,fullLength):
    lineFull = ''
    nl = True
    randstr = ''.join(rd.choice(string.ascii_letters) for _ in range(7))
    for i in range(num):
        if i == 0:
            line = 'Start;'
        else:
            line = ''
            bb = rd.choice([True,True,False])
            if bb:
                line = line+'\"\";'
            else:
                if rd.random() < 0.999:
                    line = line+randstr
                else:
                    line = line+rd.randint(10,100)*randstr
                if nl and i != num-1:
                    line = line+';\n'
                    nl = False
                elif rd.random() < 0.04 and i != num-1:
                    line = line+';\n'
                    if rd.random() < 0.01:
                        add = rd.randint(1,10)*'\n'
                        line = line+add
                else:
                    line = line+';'
        lineFull = lineFull+line
    return lineFull+'\n'

# Create file with random data:
outputFolder = "C:\\DataDir\\Output\\"
numberOfCols = 38
fullLength = 10000
testLines = [createRandomString(numberOfCols,i,fullLength) for i in range(fullLength)]
with open(outputFolder+"TestFile.txt",'w') as tf:
    tf.writelines(testLines)

# Read in file:
with open(outputFolder+"TestFile.txt",'r') as ff:
    lines = []
    for line in ff.readlines():
        lines.append(unicode(line.rstrip('\n')))

# Restore columns by counting the separator:
linesT = ''
lines2 = []
time0 = time.time()
for i in range(len(lines)):
    linesT = linesT + lines[i]
    count = linesT.count(';')
    if count == numberOfCols:
        lines2.append(linesT)
        linesT = ''
    if i%1000 == 0:
        print time.time()-time0
        time0 = time.time()
print time.time()-time0

打印语句输出如下:

0.0
0.0019998550415
0.00100016593933
0.000999927520752
0.000999927520752
0.000999927520752
0.000999927520752
0.00100016593933
0.0019998550415
0.000999927520752
0.00100016593933
0.0019998550415
0.00100016593933
0.000999927520752
0.00200009346008
0.000999927520752
0.000999927520752
0.00200009346008
0.000999927520752
0.000999927520752
0.00200009346008
0.000999927520752
0.00100016593933
0.000999927520752
0.00200009346008
0.000999927520752

始终如一的快速性能。

现在我将 createRandomString 中的第三行更改为 randstr = ''.join(rd.choice(string.printable) for _ in range(7)),我的输出现在变成这样:

0.0
0.0759999752045
0.273000001907
0.519999980927
0.716000080109
0.919999837875
1.11500000954
1.25199985504
1.51200008392
1.72199988365
1.8820002079
2.07999992371
2.21499991417
2.37400007248
2.64800000191
2.81900000572
3.04500007629
3.20299983025
3.55500006676
3.6930000782
3.79499983788
4.13900017738
4.19899988174
4.58700013161
4.81799983978
4.92000007629
5.2009999752
5.40199995041
5.48399996758
5.70299983025
5.92300009727
6.01099991798
6.44200015068
6.58999991417
3.99399995804

不仅性能非常慢,而且随着时间的推移,它会持续变慢。

唯一的区别在于写入随机数据的字符范围。

我的真实数据中出现的完整字符集是这样的:

charSet = [' ','"','&',"'",'(',')','*','+',',','-','.','/','0','1','2','3','4','5','6',
           '7','8','9',':',';','<','=','>','A','B','C','D','E','F','G','H','I','J','K',
           'L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','\\','_','`','a',
           'b','d','e','g','h','i','l','m','n','o','r','s','t','x']

让我们对 count 函数进行一些基准测试:

import random as rd
rd.seed()

def Test0():
    randstr = ''.join(rd.choice(string.digits) for _ in range(10000))
    randstr.count('7')

def Test1():
    randstr = ''.join(rd.choice(string.ascii_letters) for _ in range(10000))
    randstr.count('a')

def Test2():
    randstr = ''.join(rd.choice(string.printable) for _ in range(10000))
    randstr.count(';')

def Test3():
    randstr = ''.join(rd.choice(charSet) for _ in range(10000))
    randstr.count(';')

我仅测试数字、字母、可打印以及数据中的字符集。

%timeit的结果:

%timeit(Test0())
100 loops, best of 3: 9.27 ms per loop
%timeit(Test1())
100 loops, best of 3: 9.12 ms per loop
%timeit(Test2())
100 loops, best of 3: 9.94 ms per loop
%timeit(Test3())
100 loops, best of 3: 8.31 ms per loop

性能是一致的,并且在某些字符集上没有出现任何count问题。

我还测试了用 + 连接字符串是否会导致速度变慢,但情况也并非如此。

谁能解释一下或者给我一些提示吗?

编辑:使用Python 2.7.12

编辑2:在我的原始数据中发生了以下情况:

该文件大约有 550000 行,这些行经常被随机换行符中断,但始终由 38 个 ";" 分隔符定义。直到大约 300000 行时,性能很快,然后从该行开始,它突然开始变得越来越慢。我现在正在利用新线索进一步调查此事。

最佳答案

问题出在 count(';') 中。

string.printable 包含 ';',而 string.ascii_characters 则不包含。

然后,随着 linesT 长度的增加,执行时间也会增加:

0.000236988067627
0.0460968017578
0.145275115967
0.271568059921
0.435608148575
0.575787067413
0.750104904175
0.899538993835
1.08505797386
1.24447107315
1.34459710121
1.45430088043
1.63317894936
1.90502595901
1.92841100693
2.07722711563
2.16924905777
2.30753016472

Faulty runtime behaviour 特别是此代码对于 string.printable 存在问题:

 numberOfCols = 38
 if count == numberOfCols:
        lines2.append(linesT)
        linesT = ''

由于在刷新 linesT 之前,';' 可能会在第 37 行多次包含,因此 38 将是跳过并且 linesT 无限期增长。

您可以通过将初始设置保留为 string.ascii_characters 并将代码更改为 count('a') 来观察此行为。

要解决可打印的问题,您可以像这样修改代码:

if count > numberOfCols:

然后我们回到预期的运行时行为:

0.000234842300415
0.00233697891235
0.00247097015381
0.00217199325562
0.00262403488159
0.00262403488159
0.0023078918457
0.0024049282074
0.00231409072876
0.00233006477356
0.00214791297913
0.0028760433197
0.00241804122925
0.00250506401062
0.00254893302917
0.00266218185425
0.00236296653748
0.00201988220215
0.00245118141174
0.00206398963928
0.00219988822937
0.00230193138123
0.00205302238464
0.00230097770691
0.00248003005981
0.00204801559448

关于python - 当读取具有某些字符的某些数据时, string.count() 会大幅减慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40504611/

相关文章:

performance - 如何使用 shell 脚本进行性能测试 - 工具和技术?

java - Isabelle:Isabelle2013-2 版本的性能问题

java - 将字符串转换为数学对象

string - URL 分类的模式匹配

python - 如何循环遍历城市列表以计算它们之间的距离

python - 如何在 docker 中安装 lxml

java - 获取精确子串的数量

.net - 查询远程数据库时性能下降 - 这正常吗?

python - 将两个 DataFrame 合并为 block

python - Matplotlib 上的共享分类 Y 轴