【问题标题】:Efficient way to read a lot of text files using python使用python读取大量文本文件的有效方法
【发布时间】:2020-09-10 21:19:16
【问题描述】:

我在子目录中有大约 20000 个文档。我想全部阅读它们并将它们附加为一个列表列表。到目前为止,这是我的代码,

topics =os.listdir(my_directory)
df =[]
for topic in topics:
    files = os.listdir (my_directory+ '/'+ topic)
    print(files)

    for file in files: 
        print(file)
        f = open(my_directory+ '/'+ topic+ '/'+file, 'r', encoding ='latin1')
        data = f.read().replace('\n', ' ')
        print(data)
        f.close()
    df = np.append(df, data)

但是这样效率低下,并且需要很长时间才能读取它们并将它们附加到 df 列表中。我的预期输出是,

 df= [[doc1], [doc2], [doc3], [doc4],......,[doc20000]]

上面的代码我跑了6个多小时还是没写完(大概做了一半的文档)。如何改代码让它更快?

【问题讨论】:

  • 我注意到您已将此标记为“机器学习”,因此我不会准确回答您的问题,但会给出一些建议。将所有数据同时加载到内存中通常是不好的做法,尤其是因为您可以在进行其他计算时执行读取。您应该使用多处理模块来利用另一个核心来收集下 N 个批次,而您的模型正在计算梯度(或其他任何操作)。否则,您的代码看起来不错(可以使用上下文管理器进行改进),但需要多线程。
  • 顺便说一句,由于df = np.append(df, data) 在循环之外,所以除了最后一个data 之外,您将把所有东西都扔掉。
  • 打开 20 000 个文本文件本身就需要很多时间。也许您可以编写一个单独的代码,将它们转换为 100 个 csv 文件,这样读取速度会快很多?
  • 删除循环中的print(data) 调用。打印内容会花费令人惊讶的长时间滚动,如果您在 IDE 或终端以外的其他工具中运行脚本,则速度可能会更慢。
  • 这些文件有多大,您是否有足够的 RAM 来保存它们?读取足够的数据以淹没您的 RAM 应该不需要几个小时。在某些时候,您可能会开始破坏交换文件,但最终它会全部崩溃。

标签: python file


【解决方案1】:

生成器函数允许您声明一个行为类似于 一个迭代器,即它可以在 for 循环中使用。

generators

lazy function generator

def read_in_chunks(file, chunk_size=1024):
    """Lazy function (generator) to read a file piece by piece.
    Default chunk size: 1k."""
    while True:
        data = file.read(chunk_size)
        if not data:
            break
        yield data


with open('big_file.dat') as f:
    for piece in read_in_chunks(f):
        process_data(piece)

class Reader(object):
    def __init__(self, g):
        self.g = g
    def read(self, n=0):
        try:
            return next(self.g)
        except StopIteration:
            return ''

df = pd.concat(list(pd.read_csv(Reader(read_in_chunks()),chunksize=10000)),axis=1)
df.to_csv("output.csv", index=False)

【讨论】:

  • 这不是一个大文件。该数据集在子目录中包含 20000 个文件夹。我的目标是逐一阅读它们并将它们附加到列表列表中。
  • 好的,有一个 Reader() 将数据放入 datframe 并写入 csv。
【解决方案2】:

要加快磁盘访问速度,您能做的只有这么多。您可以使用线程将某些文件读取操作与latin1 解码和换行符替换重叠。但实际上,它不会有很大的不同。

import multiprocessing.pool

MEG = 2**20
filelist = []

topics =os.listdir(my_directory)
for topic in topics:
    files = os.listdir (my_directory+ '/'+ topic)
    print(files)

    for file in files: 
        print(file)
        filelist.append(my_directory+ '/'+ topic+ '/'+file)

def worker(filename):
    with open(filename, encoding ='latin1',  bufsize=1*MEG) as f:
        data = f.read().replace('\n', ' ')
        #print(data)
        return data

with multiprocessing.pool.ThreadPool() as pool:
    datalist = pool.map(worker, filelist, chunksize=1)
df = np.array(datalist)

【讨论】:

  • 也可以使用 MPI 完成吗?你能推荐一个基于 MPI 的解决方案吗?
  • 当然,但我认为 MPI 会更慢,因为您必须在进程之间复制数据。这个任务可能是 I/O 绑定的,让一个线程等待读取,而另一个线程正在转换,这几乎是你能得到的最好的。
【解决方案3】:

注意

我误读了 df = np.append(df, data) 行,我假设您要附加到 DataFrame,而不是 numpy 数组。所以我的评论有点无关紧要,但我会把它留给其他人,因为我像我一样误读或对 pandas 的 DataFrame 附加有类似问题。


实际问题

看起来您的问题可能无法真正解决您的实际问题。 您是否测量了两个最重要的调用的性能?

  • files = os.listdir (my_directory+ '/'+ topic)
  • df = np.append(df, data)

你格式化代码的方式让我觉得有一个错误:df = np.append(df, data) 超出了文件的 for 循环范围,所以我认为只有你的最后一个 data 被附加到你的数据框。如果这只是帖子中代码格式的问题,而您确实将 20k 文件附加到数据框中,那么这可能是问题所在 - 附加到 DataFrame 很慢。

潜在解决方案

像往常一样,缓慢的性能可以通过在问题上投入更多内存来解决。如果您有足够的内存来预先加载 all 的文件,然后再将它们插入到 DataFrame 中,这可能会更快。

关键是在加载所有数据之前不要处理任何 pandas 操作。只有这样,您才能使用DataFramefrom_records 或其其他工厂方法之一。

我发现了一个很好的 SO 问题,其中包含更多讨论: Improve Row Append Performance On Pandas DataFrames

TL;DR

  1. 测量读取所有文件的时间而不用处理 pandas
  2. 如果它被证明要快得多并且你有足够的内存来一次加载所有文件的内容使用另一种方式来构造你的DataFrame,比如说DataFrame.from_records

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多