【问题标题】:Why is file processing in python taking more time for chunks which come later in the file?为什么 python 中的文件处理需要更多时间来处理文件后面的块?
【发布时间】:2017-12-04 11:08:09
【问题描述】:

我有一个解析 JSON 文件的非常简单的代码。该文件包含作为 JSON 对象的每一行。出于某种原因,每行的处理时间会随着我运行代码而增加。

有人可以向我解释为什么会发生这种情况以及如何阻止这种情况吗?

这里是sn-p的代码:

from ast import literal_eval as le
import re
import string
from pandas import DataFrame
import pandas
import time
f = open('file.json')
df = DataFrame(columns=(column_names))
row_num = 0
while True:
    t = time.time()
    for line in f:
        line = line.strip()
        d = le(line)
        df.loc[row_num] = [d[column_name1], d[column_name2]]
        row_num+=1
        if(row_num%5000 == 0):
            print row_num, 'done', time.time() - t
            break
df.to_csv('MetaAnalysis', encoding='utf-8')

部分输出如下:

5000 done 11.4549999237     
10000 done 16.5380001068    
15000 done 24.2339999676    
20000 done 36.3680000305    
25000 done 50.0610001087    
30000 done 57.0130000114    
35000 done 65.9800000191    
40000 done 74.4649999142 

可见,每一行的时间都在增加。

【问题讨论】:

  • 看起来df.locO(n) WRT df 中的项目数?
  • 一种方法可以是为每个 5000 个块创建 df 并 pd.concat 该块,这可能不如 pd.Series 和 append 快,但可能比当前的更好。

标签: python


【解决方案1】:

Pandas 在追加行方面是出了名的慢 - 它维护数据的分层索引,每次追加一行时,它都必须更新所有索引。

这意味着添加一千行(然后更新)比添加一行(然后更新)一千次要快得多。

要遵循的示例代码;我还在下载the mozilla.tar.gz file (453 MB)

编辑: 显然,我下载并提取的文件(/dump/mozilla/mozall.bson,890 MB)是带有 TenGen 扩展的 bson 中的 MongoDB 转储,包含 769k 行。出于测试目的,我将前 50k 行重新保存为 json(结果为 54 MB - 平均行约为 1200 个字符),然后使用 Notepad++ 将其分解为每行一条记录。

这里的大部分复杂性在于以块的形式读取文件:

from itertools import islice
import pandas as pd
from time import time

LINES_PER_BLOCK = 5000

# read object chunks from json file
with open("mozilla.json") as inf:
    chunks = []
    while True:
        start = time()
        block = list(islice(inf, LINES_PER_BLOCK))
        if not block:
            # reached EOF
            break
        json  = "[" + ",".join(block) + "]"
        chunk = pd.read_json(json, "records")
        chunks.append(chunk)
        done = time()
        print(LINES_PER_BLOCK * len(chunks), "done", done - start)
        start = done

# now combine chunks
start = time()
df = pd.concat(chunks)
done = time()
print("Concat done", done - start)

给了

5000 done 0.12293195724487305
10000 done 0.12034845352172852
15000 done 0.12239885330200195
20000 done 0.11942410469055176
25000 done 0.12282919883728027
30000 done 0.11931681632995605
35000 done 0.1278700828552246
40000 done 0.12238287925720215
45000 done 0.12096738815307617
50000 done 0.20111417770385742
Concat done 0.04361534118652344

总时间为 1.355 秒;如果您不需要对文件进行分块,则可以简化为

import pandas as pd
from time import time

start = time()

with open("mozilla.json") as inf:
    json = "[" + ",".join(inf) + "]"
df = pd.read_json(json, "records")

done = time()
print("Total time", done - start)

给了

Total time 1.247551441192627

【讨论】:

  • 我的又快了 8 倍 ;-)
【解决方案2】:

因此,根据 mayercn 的回答和 Hugh Bowell 的评论,我能够确定代码的问题。 我将代码修改如下,以将时间减少 1/12(平均)。 TL;DR:我将行附加到列表中,然后将其附加到最终数据帧中。

from ast import literal_eval as le
import re
import string
from pandas import DataFrame
import pandas
import time
f = open('Filename')
df = DataFrame(columns=cols)
row_num = 0
while True:
    t = time.time()
    l = []
    for line in f:
        line = line.strip()
        bug = le(line)
        l.append([values])
        row_num+=1
        if(row_num%5000 == 0):
            print row_num, 'done', time.time() - t
            df = df.append(pandas.DataFrame(l),ignore_index=True)
            break
df.to_csv('File', index='id', encoding='utf-8')

输出时间:

5000 done 0.998000144958
10000 done 1.01800012589
15000 done 1.01699995995
20000 done 0.999000072479
25000 done 1.04600000381
30000 done 1.09200000763
35000 done 1.06200003624
40000 done 1.14300012589
45000 done 1.00900006294
50000 done 1.06600022316

【讨论】:

  • 您有机会分享 MozillaBugs.json 的链接吗?使用 df.from_records 可能会大大加快加载速度。
【解决方案3】:

您通过在该行中插入新元素来单调增加数据结构 df.loc

df.loc[row_num] = [d[column_name1], d[column_name2]].

变量 df.loc 似乎是一个字典(例如here)。插入 python 字典的速度越慢,它已经包含的元素越多。这已经在this stackoverflow response 中讨论过了。因此,字典大小的增加最终会减慢循环的内部代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-14
    • 2016-09-07
    • 1970-01-01
    • 1970-01-01
    • 2019-03-16
    • 2017-04-24
    • 1970-01-01
    相关资源
    最近更新 更多