2013-03-24 56 views
5

我有一行(幾GB和~12百萬行)的文本文件,其中每行是一個點x,y,z,+附件信息。我希望逐塊讀取該文件,處理該點並分割(在基於點的位置的空間索引方面,遵循0.25米的正方形網格),將結果放入臨時文件夾中的多個文本文件中。在Python中用緩衝區讀取塊的大多數內存有效方法

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) 

我的代碼的主要問題是當大約2百萬分割文本文件保存在臨時文件夾中時速度下降。我想知道effbot.org的解決方案是否存在創建緩衝區的優化方法?

+1

保存200萬個文件的原因是什麼?我知道我知道 - 不成熟的優化是萬惡之源,你應該總是(如果可能的話)使用純文本文件 - 但我會尋找另一種存儲這些數據的方式。 – Anders 2013-03-24 19:48:26

+1

對於你閱讀的每一行,你打開並關閉一個輸出文件。這是瓶頸。考慮寫入數據庫。 – 2013-03-24 19:49:16

+0

@Anders,「不成熟的優化是萬惡之源」是如此的真實!拆分後,我需要再次打開每個文件,並隨機選擇一行。 – 2013-03-24 19:53:42

回答

1

代碼中的瓶頸不在讀取,而是在打開和關閉每行讀取的輸出文件。在評論你提到你的最終目標:分裂後,我需要再次打開每個文件,並隨機選擇只有一行。

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 
+0

謝謝Janne,我明白了。有一個內存問題來存儲12 GB的數據。當你選擇一個小的方形網格(例如:0.25米)時,通常只有一個或兩個點落在方形網格內。大約12 GB的最終輸出約爲10 GB – 2013-03-25 11:06:52

+0

@Gianni OK,我編輯了我的代碼以保留行號而不是全行。也許它現在適合你的記憶? – 2013-03-25 11:28:10