парсинг больших объемов данных
От: sinmaster  
Дата: 08.09.10 12:47
Оценка:
привет!
прошу подсказать как можно улучшить следующий код для обработки здоровенного 30GB текстового файла со строками.
требуется:
a) удалить в файле символ "|"
б) удалить все до первой открытой скобки "<"
в) удалить в конце файла всё что начинается на символ "-"
г) удалить в кажд строке пробелы в начале и в конце
д) удалить пустые строки
е) присоединить в конец кажд строки символ '."

как я это пока себе представляю:
def process(lines):
    dataStr = "".join(lines)

    # remove | globally
    p = re.compile(r'\|')
    dataStr = p.sub("", dataStr)

    # fix header
    p = re.compile(r'^[^<]*')
    dataStr = p.sub("", dataStr)

    #fix footer
    p = re.compile('-*$')
    dataStr = p.sub("", dataStr)

    # delete BOTH leading and trailing whitespace from each line
    #TODO: do better
    data  =dataStr.split('\n')

    #remove empty lines
    data = list(filter(lambda x: len(x) > 0, data))

    #append ending . to each line
    data = [l.strip() + '.' for l in data]

    return data

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print "usage: %s %s" % (sys.argv[0], "dump.txt")
        exit(-1)
    else:
        fname = sys.argv[1]
        with open(fname, "r+") as file:

            data = list()
            i = 0
            while True:
                if i >= 100000:
                    result = process(data)
                    #TODO: append result to output file
                    i = 0
                    data = list()
                else:
                    line = file.readline()
                    if not line:
                        if data:
                            result = process(data)
                            #TODO: append result to output file
                        break
                    data.append(line)
                    i += 1

т.е. читаю в список по сто тыщ строк, и обрабатываю их, записываю в выходной файл. и так пока не кончатся данные все из исходного файла.
смущает явные излишние "кувыркания" данных в ф-ции "process" и назад: list_of_strings <--> string. как сделать лаконичней, а самое главное, быстрее по времени исполнения? спасибо!
Re: парсинг больших объемов данных
От: Mr.Cat  
Дата: 08.09.10 12:52
Оценка:
Здравствуйте, sinmaster, Вы писали:
S>прошу подсказать как можно улучшить следующий код для обработки здоровенного 30GB текстового файла со строками.
Внезапно, гугл занимается грепом на кластере и использует MapReduce (http://labs.google.com/papers/mapreduce.html). Может, стоит заюзать какой-нибудь фреймворк для MR?
Re[2]: парсинг больших объемов данных
От: sinmaster  
Дата: 08.09.10 13:01
Оценка:
может и стОит заюзать. но я исхожу из того что это простая далеко НЕ ежедневная обработка данных у меня, т.ч. особо заморачиваться не планировал
Re: парсинг больших объемов данных
От: Temoto  
Дата: 08.09.10 13:13
Оценка:
S>привет!
S>прошу подсказать как можно улучшить следующий код для обработки здоровенного 30GB текстового файла со строками.
S>требуется:
S>a) удалить в файле символ "|"
S>б) удалить все до первой открытой скобки "<"
S>в) удалить в конце файла всё что начинается на символ "-"
S>г) удалить в кажд строке пробелы в начале и в конце
S>д) удалить пустые строки
S>е) присоединить в конец кажд строки символ '."

S>т.е. читаю в список по сто тыщ строк, и обрабатываю их, записываю в выходной файл. и так пока не кончатся данные все из исходного файла.

S>смущает явные излишние "кувыркания" данных в ф-ции "process" и назад: list_of_strings <--> string. как сделать лаконичней, а самое главное, быстрее по времени исполнения? спасибо!

Во-первых, быстрее и питон.. мухи отдельно, котлеты отдельно.

Чтобы сделать это быстрее, нужно написать примерно такой пайп на шеллскрипте:
cat input | sed \
  # a) удалить в файле символ "|"
  -e 's/|//' \
  -e 's/.*\<\(.*\)/\\1/' \ # здесь может быть синтаксически неправильно, но смысл думаю понятен
... т.д. остальные правила
  > output


Чтобы сделать это на питоне... В двух словах: нужно убрать магическое число сто тыщ (с буферизацией OS справится лучше, чем вы) и не использовать list <--> string, а обрабатывать каждую строку отдельно.

def process_file(f):
  for s in f:
    # пропускаем пустые строки
    if s:
      yield process_line(s)

def process_line(s):
  # Внимание: в конце этой строки есть символ перевода строки.

  # a) удалить в файле символ "|"
  s = s.replace('|', '')

  ... остальные правила

  # г) удалить в кажд строке пробелы в начале и в конце
  s = s.strip(' ') # уберите ' ' чтобы удалить любые пробельные символы (\t\v\r\n...), но тогда добавить символ перевода строки в конец надо будет самому

  # е) присоединить в конец кажд строки символ '."
  s = s + '.'

  return s

fin = open("/path", 'r')
fout = open("/other", 'w')

for s in process_file(fin):
  fout.write(s)

fin.close()
fout.close()
Re[2]: парсинг больших объемов данных
От: Temoto  
Дата: 08.09.10 13:14
Оценка:
S>>прошу подсказать как можно улучшить следующий код для обработки здоровенного 30GB текстового файла со строками.
MC>Внезапно, гугл занимается грепом на кластере и использует MapReduce (http://labs.google.com/papers/mapreduce.html). Может, стоит заюзать какой-нибудь фреймворк для MR?

Казалось бы, причём тут гугл.
Re[3]: парсинг больших объемов данных
От: sinmaster  
Дата: 08.09.10 13:21
Оценка:
Здравствуйте, Temoto, Вы писали:

на sed у меня уже есть накатанный рабочий вариант, но т.к. я изучаю счас Питон, захотел и его попробовать. просто для самообучения, благо задача подвернулась хорошая. спасибо за пояснения, вдумываюсь в питоновский пример ваш.
Re[4]: парсинг больших объемов данных
От: Temoto  
Дата: 08.09.10 13:30
Оценка:
S>на sed у меня уже есть накатанный рабочий вариант, но т.к. я изучаю счас Питон, захотел и его попробовать. просто для самообучения, благо задача подвернулась хорошая. спасибо за пояснения, вдумываюсь в питоновский пример ваш.

Если нужно на питоне и быстро, то есть смысл посмотреть в сторону Cython.

А если для самообучения то вроде быстро не надо же, хотя тут конечно, каждый по-своему считает.
Re: парсинг больших объемов данных
От: Temoto  
Дата: 08.09.10 13:34
Оценка:
Ещё замечание

[ccode]
+RE_VLINE = re.compile(r'\|')
+
def process(lines):
dataStr = "".join(lines)

# remove | globally
— p = re.compile(r'\|')
— dataStr = p.sub("", dataStr)
+ dataStr = RE_VLINE.sub("", dataStr)
[/code]

регекспы нужно компилить один раз, а не в каждом вызове функции.
Re[3]: парсинг больших объемов данных
От: Mr.Cat  
Дата: 08.09.10 13:56
Оценка:
Здравствуйте, Temoto, Вы писали:
T>Казалось бы, причём тут гугл.
Про кого сам читал — про того и написал. Ну и хороший пример конторы, у которой обрабатываются большие объемы данных.
Re[2]: парсинг больших объемов данных
От: sinmaster  
Дата: 08.09.10 14:55
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Ещё замечание


да, это я знаю и уже сам учёл.
Re[3]: парсинг больших объемов данных
От: sinmaster  
Дата: 09.09.10 05:52
Оценка:
если парсить построчно, то неслишком красиво получится реализовать пункт бэ)
Re[4]: парсинг больших объемов данных
От: Temoto  
Дата: 09.09.10 10:54
Оценка:
S>если парсить построчно, то неслишком красиво получится реализовать пункт бэ)

Удалить всё до первой открытой скобки? А нужно удалить в т.ч. все предыдущие строки?

lalala
zar<h1>

И нужно lalala тоже удалить? Тут два решения — простое, если '<' точно встречается в файле и сложное, если неизвестно — будет скобка или нет.
Re[5]: парсинг больших объемов данных
От: sinmaster  
Дата: 09.09.10 12:52
Оценка:
Здравствуйте, Temoto, Вы писали:

S>>если парсить построчно, то неслишком красиво получится реализовать пункт бэ)


T>Удалить всё до первой открытой скобки? А нужно удалить в т.ч. все предыдущие строки?


T>lalala

T>zar<h1>

T>И нужно lalala тоже удалить? Тут два решения — простое, если '<' точно встречается в файле и сложное, если неизвестно — будет скобка или нет.


нужно удалить "lalala\nzar", т.е. чтобы в итоге осталось только "<h1>foo\nbarbar\n\n\n\bazfred...etc".
в моем случае "<" точно встречается в файле, в строчке где-то 5-10 от начала файла.
если не прибегать к регекспам (ведь мы обрабатываем построчно), то придется вводить if и проверять булев флажок, чтобы блок в if выполнился 1 раз.
Re[6]: парсинг больших объемов данных
От: Temoto  
Дата: 09.09.10 13:21
Оценка:
S>>>если парсить построчно, то неслишком красиво получится реализовать пункт бэ)

T>>Удалить всё до первой открытой скобки? А нужно удалить в т.ч. все предыдущие строки?


T>>lalala

T>>zar<h1>

T>>И нужно lalala тоже удалить? Тут два решения — простое, если '<' точно встречается в файле и сложное, если неизвестно — будет скобка или нет.


S>нужно удалить "lalala\nzar", т.е. чтобы в итоге осталось только "<h1>foo\nbarbar\n\n\n\bazfred...etc".

S>в моем случае "<" точно встречается в файле, в строчке где-то 5-10 от начала файла.
S>если не прибегать к регекспам (ведь мы обрабатываем построчно), то придется вводить if и проверять булев флажок, чтобы блок в if выполнился 1 раз.

Не надо флажки. Флажки делает умный компилятор, когда компилирует конечный автомат. Питонячий компилятор подобные конечные автоматы оптимизировать не умеет (и никогда не будет уметь), а человек — плохой умный компилятор.

def process_file(f):
  # пропускаем всё до символа '<'
  # Внимание! Если в файле не было символа '<', то на выходе будет пустота.
  for s in f:
    if '<' in s:
      _, after = s.split('<', 1)
      yield process_line('<' + after)
      break

  for s in f:
    # пропускаем пустые строки
    if s:
      yield process_line(s)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.