如何加快读取多个文件并将数据放入数据帧?

blu*_*e13 39 python regex performance parsing pandas

我有许多文本文件,比如50,我需要读入一个庞大的数据帧.目前,我正在使用以下步骤.

  1. 阅读每个文件并检查标签是什么.我需要的信息通常包含在前几行中.对于文件的其余部分,只重复相同的标签,每次都会列出不同类型的数据.
  2. 使用这些标签创建数据框.
  3. 再次读取文件并使用值填充数据框.
  4. 将该数据帧与主数据帧连接起来.

这适用于100 KB大小的文件 - 几分钟,但在50 MB时,它只需要几个小时,并且不实用.

如何优化我的代码?特别是 -

  1. 如何识别哪些功能占用的时间最多,哪些需要优化?是文件的读物吗?是写入数据帧吗?我的节目在哪里花费时间?
  2. 我应该考虑多线程还是多处理?
  3. 我可以改进算法吗?
    • 或许可以将整个文件一次性读入列表,而不是逐行读取,
    • 以块/整个文件解析数据,而不是逐行解析,
    • 以块/一次为单位将数据分配给数据帧,而不是逐行分配.
  4. 还有什么我可以做的让我的代码执行得更快吗?

这是一个示例代码.我自己的代码有点复杂,因为文本文件更复杂,因此我必须使用大约10个正则表达式和多个while循环来读取数据并将其分配到正确数组中的正确位置.为了保持MWE的简单,我没有在MWE的输入文件中使用重复标签,所以我希望我无缘无故地读取文件两次.我希望这是有道理的!

import re
import pandas as pd

df = pd.DataFrame()
paths = ["../gitignore/test1.txt", "../gitignore/test2.txt"]
reg_ex = re.compile('^(.+) (.+)\n')
# read all files to determine what indices are available
for path in paths:
    file_obj = open(path, 'r')
    print file_obj.readlines()

['a 1\n', 'b 2\n', 'end']
['c 3\n', 'd 4\n', 'end']

indices = []
for path in paths:
    index = []
    with open(path, 'r') as file_obj:
        line = True
        while line:
            try:
                line = file_obj.readline()
                match = reg_ex.match(line)
                index += match.group(1)
            except AttributeError:
                pass
    indices.append(index)
# read files again and put data into a master dataframe
for path, index in zip(paths, indices):
    subset_df = pd.DataFrame(index=index, columns=["Number"])
    with open(path, 'r') as file_obj:
        line = True
        while line:
            try:
                line = file_obj.readline()
                match = reg_ex.match(line)
                subset_df.loc[[match.group(1)]] = match.group(2)
            except AttributeError:
                pass
    df = pd.concat([df, subset_df]).sort_index()
print df

  Number
a      1
b      2
c      3
d      4
Run Code Online (Sandbox Code Playgroud)

我的输入文件:

test1.txt的

a 1
b 2
end
Run Code Online (Sandbox Code Playgroud)

的test2.txt

c 3
d 4
end
Run Code Online (Sandbox Code Playgroud)

clo*_*ker 16

在拔出多处理锤之前,您的第一步应该是进行一些分析.使用cProfile快速查看以确定哪些功能需要很长时间.不幸的是,如果你的行都在一个函数调用中,它们将显示为库调用.line_profiler更好,但需要更多的设置时间.

注意.如果使用ipython,您可以使用%timeit(timeit模块的magic命令)和%prun(profile模块的magic命令)来为语句和函数计时.谷歌搜索将显示一些指南.

熊猫是一个很棒的图书馆,但是我偶尔也会因为结果糟糕而使用它.特别要注意append()/ concat()操作.这可能是你的瓶颈,但你应该确定一下.通常,如果您不需要执行索引/列对齐,则numpy.vstack()和numpy.hstack()操作会更快.在你的情况下,看起来你可以使用系列或1-D numpy ndarray,这可以节省时间.

顺便说一句,trypython中的一个块通常比检查无效条件慢10倍或更多,所以当你将它粘贴到每一行的循环中时,确保你绝对需要它.这可能是时间的另一个障碍; 我想你在try.group(1)失败的情况下卡住了try块来检查AttributeError.我会先检查一下有效的比赛.

即使是这些小的修改也应该足以让你的程序在尝试像多处理这样繁琐的事情之前运行得更快.那些Python库非常棒,但却带来了一系列新的挑战.


Нек*_*кто 15

我已经多次使用它,因为它是多处理的一个特别简单的实现.

import pandas as pd
from multiprocessing import Pool

def reader(filename):
    return pd.read_excel(filename)

def main():
    pool = Pool(4) # number of cores you want to use
    file_list = [file1.xlsx, file2.xlsx, file3.xlsx, ...]
    df_list = pool.map(reader, file_list) #creates a list of the loaded df's
    df = pd.concat(df_list) # concatenates all the df's into a single df

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

使用它你应该能够大大提高你的程序的速度,而不需要太多的工作.如果您不知道有多少处理器,可以通过拉起shell并键入来检查

echo %NUMBER_OF_PROCESSORS%
Run Code Online (Sandbox Code Playgroud)

编辑:为了使这个运行更快,考虑将文件更改为csvs并使用pandas函数pandas.read_csv


blu*_*e13 3

事实证明,首先创建一个空白的 DataFrame,搜索索引以找到一行数据的正确位置,然后仅更新 DataFrame 的一行是一个非常耗时的过程。

一种更快的方法是将输入文件的内容读取到原始数据结构(例如列表列表或字典列表)中,然后将其转换为 DataFrame。

当您读取的所有数据都位于同一列中时,请使用列表。否则,使用字典明确说明每一位数据应该进入哪一列。

1 月 18 日更新:这链接到如何使用 Python 解析复杂的文本文件?我还写了一篇博客文章,向初学者解释如何解析复杂文件