【问题标题】:Most memory efficient way to read chunk in a buffer in Python [duplicate]在 Python 中读取缓冲区中块的最高效内存方法 [重复]
【发布时间】:2013-03-14 05:45:32
【问题描述】:

我有一个行的文本文件(几 GB 和约 1200 万行),其中每行是一个点 x、y、z 和附件信息。我希望逐块读取文件,处理点并拆分(遵循基于点位置的空间索引尊重 0.25 m 的方形网格)结果在临时文件夹中的几个文本文件中。

449319.34;6242700.23;0.38;1;1;1;0;0;42;25;3;17;482375.326087;20224;23808;23808
449310.72;6242700.22;0.35;3;1;1;0;0;42;23;3;17;482375.334291;20480;24576;24576
449313.81;6242700.66;0.39;1;1;1;0;0;42;24;3;17;482375.342666;20224;24576;24576
449298.37;6242700.27;0.39;1;1;1;0;0;42;21;3;17;482375.350762;18176;22784;23552
449287.47;6242700.06;0.39;11;1;1;0;0;42;20;3;17;482375.358921;20736;24832;24832
449290.11;6242700.21;0.35;1;1;1;0;0;42;20;3;17;482375.358962;19968;24064;23808
449280.48;6242700.08;0.33;1;1;1;0;0;42;18;3;17;482375.367142;22528;25856;26624
449286.97;6242700.44;0.36;3;1;1;0;0;42;19;3;17;482375.367246;19712;23552;23296
449293.03;6242700.78;0.37;1;1;1;0;0;42;21;3;17;482375.367342;19456;23296;23808
449313.36;6242701.92;0.38;6;1;1;0;0;42;24;3;17;482375.367654;19968;24576;24576
449277.48;6242700.17;0.34;8;1;1;0;0;42;18;3;17;482375.375420;20224;23808;25088
449289.46;6242700.85;0.31;3;1;1;0;0;42;20;3;17;482375.375611;18944;23040;23040

";" 是分隔符,first two columns the x and y 可用于提供ID position

输出结果是另一个文本文件,其中每个 ID 仅随机提取一个点

例如:

    20;10;449319.34;6242700.23;0.38;1;1;1;0;0;42;25;3;17;482375.326087;20224;23808;23808
    20;10;449310.72;6242700.22;0.35;3;1;1;0;0;42;23;3;17;482375.334291;20480;24576;24576
    20;10;449313.81;6242700.66;0.39;1;1;1;0;0;42;24;3;17;482375.342666;20224;24576;24576
    20;10;449298.37;6242700.27;0.39;1;1;1;0;0;42;21;3;17;482375.350762;18176;22784;23552
    20;11;449287.47;6242700.06;0.39;11;1;1;0;0;42;20;3;17;482375.358921;20736;24832;24832
    20;11;449290.11;6242700.21;0.35;1;1;1;0;0;42;20;3;17;482375.358962;19968;24064;23808

前两列是 ID

最终输出将是(示例)没有 ID 值

         20;10;449313.81;6242700.66;0.39;1;1;1;0;0;42;24;3;17;482375.342666;20224;24576;24576
         20;11;449287.47;6242700.06;0.39;11;1;1;0;0;42;20;3;17;482375.358921;20736;24832;24832

我正在使用来自 blog 的解决方案

# File: readline-example-3.py

file = open("sample.txt")

while 1:
    lines = file.readlines(100000)
    if not lines:
        break
    for line in lines:
        pass # do something

我的代码如下:

from __future__ import division
import os
import glob
import tempfile
import sys

def print_flulsh(n, maxvalue = None):
    sys.stdout.write("\r")
    if maxvalue is None:
        sys.stdout.write("Laser points processed: %d" % n)
    else:
        sys.stdout.write("%d of %d laser points processed" % (n, maxvalue))
    sys.stdout.flush()


def point_grid_id(x, y, minx, maxy, size):
    """give id (row,col)"""
    col = int((x - minx) / size)
    row = int((maxy - y) / size)
    return row, col


def tempfile_tile_name(line, temp_dir, minx, maxy, size, parse):
    x, y = line.split(parse)[:2]
    row, col = point_grid_id(float(x), float(y), minx, maxy, size)
    return os.path.normpath(os.path.join(temp_dir + os.sep,"tempfile_%s_%s.tmp" % (row, col)))

# split the text file in small text files following the ID value given by tempfile_tile_name
# where:
# filename : name+path of text file
# temp_dir: temporary folder
# minx, maxy: origin of the grid (left-up corner)
# size: size of the grid
# parse: delimeter of the text file
# num: number of lines (~ 12 millions)

def tempfile_split(filename, temp_dir, minx, maxy, size, parse, num):
    index = 1
    with open(filename) as file:
        while True:
            lines = file.readlines(100000)
            if not lines:
                break
            for line in lines:
                print_flulsh(index, num)
                index += 1
                name = tempfile_tile_name(line, temp_dir, minx, maxy, size, parse)
                with open(name, 'a') as outfile:
                    outfile.write(line)

我的代码的主要问题是当大约 200 万个拆分文本文件保存在临时文件夹中时,速度会降低。我想知道effbot.org的解决方案是否有优化的方法来创建缓冲区?

【问题讨论】:

  • 保存两百万个单独文件的原因是什么?我知道我知道 - 过早的优化是万恶之源,您应该始终(如果可能)使用纯文本文件 - 但我会寻找另一种存储这些数据的方法。
  • 对于您阅读的每一行,您打开和关闭一个输出文件。这就是瓶颈。考虑改为写入数据库。
  • 为什么使用file.readlines()?文件对象应该已经使用缓冲 IO,并且无论如何您都在逐行迭代,所以它不像您要摆脱循环开销,并且一遍又一遍地分配和释放大量内存。当您并不真正关心这些块中的内容时,以块读取数据更有意义,例如当您尝试将二进制文件下载到磁盘时。似乎只是做for line in file: 就可以了。 (内存开销应该最小。)
  • @millimoose 猜测是因为它读取了这么多字节,然后一直到下一行的末尾......所以,我想 100000 应该是 512mb 左右......但是是的 - 查看代码,无论如何都没有真正发生任何事情,所以很好
  • @Gianni 我会看看我能不能找到一个例子......或者给你写一个快速的例子 - 但我可以看到这需要一段时间;)

标签: python performance optimization buffer chunking


【解决方案1】:

代码中的瓶颈不在于读取,而在于为读取的每一行打开和关闭输出文件。在 cmets 中,您提到了您的最终目标:拆分后,我需要再次打开每个文件并随机选择一行。

theodox 提到了一种可能的方法,获取每个 ID 的第一个条目,然后在内存中随机覆盖它。请注意,覆盖必须以概率 1/n 发生,其中 n 是到目前为止看到的具有相同 ID 的行数,以避免对后面的样本产生偏差。

编辑。您可以通过对文件执行两次传递来节省内存。第一遍构建一组被随机选择排除的行号,第二遍处理未排除的行。

from random import random

def random_selection(filename, temp_dir, minx, maxy, size, parse, num):
    selection = {}
    excluded = set()
    with open(filename) as file:
        for i, line in enumerate(file):
            x, y, _ = line.split(parse, 2)
            row_col = point_grid_id(float(x), float(y), minx, maxy, size)
            try:
                n, selected_i = selection[row_col]
            except KeyError:
                selection[row_col] = 1, i
            else:
                n += 1
                if random() < 1.0 / n:
                    excluded.add(selected_i)
                    selected_i = i
                selection[row_col] = n, selected_i

    with open(filename) as file:
        for i, line in enumerate(file):
            if i not in excluded:
                #process the line

【讨论】:

  • 谢谢 Janne,我明白了。存储 12 GB 数据时存在内存问题。当您在一个小的方形网格(例如:0.25 m)内选择时,通常只有一个或两个点落在方形网格内。使用 ~12 GB,最终输出约为 ~10 GB
  • @Gianni 好的,我编辑了我的代码以保留行号而不是整行。也许它现在符合你的记忆?
猜你喜欢
  • 2014-07-26
  • 1970-01-01
  • 1970-01-01
  • 2016-06-17
  • 2021-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多