2013-03-24 9 views
5

Mam plik tekstowy z liniami (o kilku GB i ~ 12 milionach linii), gdzie każda linia jest punktem x, y, z, + informacja o akcesoriach. Chciałbym czytać fragmenty po kawałku, przetwarzając punkt i dzieląc (według indeksu przestrzennego na podstawie położenia punktów względem kwadratu o boku 0,25 m), co skutkuje kilkoma plikami tekstowymi w folderze tymczasowym.Najbardziej efektywny pod względem pamięci sposób odczytywania fragmentów w buforze w Pythonie

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 

gdzie ";" jest separator i first two columns the x and y wszelkie przydatne dla dają ID position

wynik wyjściowy jest inny pliki tekstowe, gdzie dla każdego identyfikatora tylko jeden punkt jest losowo wydobyte

ex:

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 

gdzie dwie pierwsze kolumny to ID

ostateczny wynik będzie (przykład) bez identyfikator wartości

  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 

Używam rozwiązanie od tego 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 

mojego kodu jest następujący:

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) 

Głównym problemem mojego kodu jest zmniejszenie prędkości, gdy ~ 2 miliony podzielonych plików tekstowych są zapisywane w folderze tymczasowym. Chciałbym poznać z szacunkiem rozwiązanie effbot.org, jeśli istnieje zoptymalizowana metoda tworzenia bufora?

+1

Jaki jest powód oszczędności dwóch milionów pojedynczych plików? Wiem, że wiem - przedwczesna optymalizacja jest źródłem wszelkiego zła i powinieneś zawsze (jeśli to możliwe) korzystać z czystych plików tekstowych - ale chciałbym znaleźć inny sposób przechowywania tych danych. – Anders

+1

Dla każdej przeczytanej linii otwierasz i zamykasz plik ouput. To jest wąskie gardło. Zamiast tego rozważ pisanie do bazy danych. –

+0

@Anders, "przedwczesna optymalizacja jest źródłem wszelkiego zła", tak prawdziwe !!!. Po podziale muszę ponownie otworzyć każdy plik i wybrać tylko jedną linię losową. –

Odpowiedz

1

Węzłem w kodzie nie jest czytanie, ale otwieranie i zamykanie pliku wyjściowego dla każdej przeczytanej linii. W komentarzach wspominasz o swoim ostatecznym celu: Po podziale muszę ponownie otworzyć każdy plik i wybrać tylko jedną linię losową.

theodox wspomina o możliwym podejściu, biorąc pierwszy wpis dla każdego ID, a następnie losowo zastępując go w pamięci. Zauważ, że nadpisanie musi mieć miejsce z prawdopodobieństwem 1/n, gdzie n jest liczbą linii dotychczas widzianych z tym samym ID, aby uniknąć tendencji do późniejszych próbek.

EDYCJA. Możesz zaoszczędzić pamięć, wykonując dwa przejścia przez plik. Pierwsze przejście buduje zestaw numerów linii wykluczonych przez losowy wybór, drugie przejście przetwarza linie, które nie są wykluczone.

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

Dzięki Janne, zrozumiałem. Wystąpił problem z pamięcią do przechowywania 12 GB danych. Po wybraniu wewnątrz małej kwadratowej siatki (np. 0,25 m) często tylko jeden lub dwa punkty spadają do kwadratowej siatki. Przy ~ 12 GB wynik końcowy wynosi około ~ 10 GB. –

+0

@Gianni OK, edytowałem swój kod, aby zachować numery linii zamiast pełnych linii. Może pasuje teraz do twojej pamięci? –

Powiązane problemy