Извлечь определенные текстовые строки?

У меня есть большие несколько hudred тысяч текстового файла строк. Я должен извлечь 30 000 определенных строк, которые являются всеми в текстовом файле в случайных местах. Это - программа, я должен извлечь одну строку за один раз:

big_file = open('C:\\gbigfile.txt', 'r')
small_file3 = open('C:\\small_file3.txt', 'w')
for line in big_file:
   if 'S0414' in line:
      small_file3.write(line)
gbigfile.close()
small_file3.close()

Как я могу ускорить это для 30 000 строк, которые я должен искать>?

5
задан Simon Nickerson 14 July 2010 в 16:44
поделиться

10 ответов

Ага! Значит, ваша реальная проблема в том, как проверить множество условий в строке и, если одно из них выполнено, вывести эту строку. Проще всего будет использовать регулярное выражение, я думаю:

import re
keywords = ['S0414', 'GT213', 'AT3423', 'PR342'] # etc - you probably get those from some source
pattern = re.compile('|'.join(keywords))

for line in inf:
    if pattern.search(ln):
        outf.write(line)
5
ответ дан 13 December 2019 в 22:00
поделиться

Согласно документации Python по файловым объектам, итерация, которую вы делаете, не должна быть особенно медленной, и поиск подстрок также должен быть в порядке с точки зрения скорости.

Я не вижу причин, по которым ваш код должен быть медленным, так что если вам нужно, чтобы он работал быстрее, возможно, вам придется переписать его на C и использовать mmap() для быстрого доступа к исходному файлу.

1
ответ дан 13 December 2019 в 22:00
поделиться

Можно попробовать читать большими блоками, избегая накладных расходов на разбивку строк, за исключением отдельных интересующих вас строк. Например, если предположить, что ни одна из ваших строк не длиннее мегабайта:

BLOCKSIZE = 1024 * 1024

def byblock_fullines(f):
    tail = ''
    while True:
        block = f.read(BLOCKSIZE)
        if not block: break
        linend = block.rindex('\n')
        newtail = block[linend + 1:]
        block = tail + block[:linend + 1]
        tail = newtail
        yield block
    if tail: yield tail + '\n'

эта программа принимает аргумент открытого файла и выдает блоки размером около 1 МБ, гарантированно заканчивающиеся новой строкой. Чтобы определить (по итератору) все вхождения строки-иголки в строку стога сена:

def haystack_in_needle(haystack, needle):
    start = 0
    while True:
        where = haystack.find(needle, start)
        if where == -1: return
        yield where
        start = where + 1

Чтобы определить все соответствующие строки внутри такого блока:

def wantlines_inblock(s, block):
    last_yielded = None
    for where in haystack_in_needle(block, s):
        prevend = block.rfind('\n', where)  # could be -1, that's OK
        if prevend == last_yielded: continue  # no double-yields
        linend = block.find('\n', where)
        if linend == -1: linend = len(block)
        yield block[prevend + 1: linend]
        last_yielded = prevend

Как все это сочетается:

def main():
    with open('bigfile.txt') as f:
        with open('smallfile.txt', 'w') as g:
            for block in byblock_fulllines(f):
                for line in wantlines_inblock('S0414', block)
                    f.write(line)

В 2.7 вы можете сложить оба оператора с в один, просто чтобы немного уменьшить вложенность.

Примечание: этот код не тестировался, поэтому в нем могут быть (надеюсь, небольшие;-) ошибки, такие как off-by-one. Производительность требует настройки размера блока и должна быть выверена путем измерения на вашей конкретной машине и данных. Ваш пробег может варьироваться. Недействительно, если запрещено законом.

1
ответ дан 13 December 2019 в 22:00
поделиться

Лучший вариант для ускорения - это если конкретная строка S0414 всегда появляется в одной и той же позиции, так что вместо того, чтобы делать несколько неудачных сравнений в строке (вы сказали, что они начинаются с разных имен), она может просто сделать одно и готово.

например, если в вашем файле есть строки типа

GLY S0414 GCT
ASP S0435 AGG
LEU S0432 CCT

сделайте if line[4:9] == 'S0414': small.write(line).

0
ответ дан 13 December 2019 в 22:00
поделиться

1. Попробуйте читать файл целиком

Одно из ускорений, которое вы можете сделать, это читать весь файл в памяти, если это возможно, в противном случае читайте кусками. Вы сказали "несколько сотен тысяч строк", допустим, 1 миллион строк, каждая строка 100 символов, т.е. около 100 МБ, если у вас есть столько свободной памяти (я предполагаю, что есть), просто сделайте вот так

big_file = open('C:\\gbigfile.txt', 'r')
big_file_lines = big_file.read_lines()
big_file.close()
small_file3 = open('C:\\small_file3.txt', 'w')
for line in big_file_lines:
   if 'S0414' in line:
      small_file3.write(line)
small_file3.close()

Задайте время для оригинальной версии и посмотрите, есть ли разница, я думаю, будет.

Но если ваш файл действительно большой в ГБ, то вы можете читать его кусками, например, кусками по 100 МБ, разделить его на строки и искать, но не забывайте соединять строки через каждые 100 МБ (я могу рассказать подробнее, если это так)

file.readlines возвращает список, содержащий все строки данных в файле. Если дан необязательный параметр sizehint, он считывает из файла столько байт, сколько нужно для завершения строки, и возвращает строки из этого списка. Это часто используется для эффективного чтения большого файла по строкам, но без необходимости загружать весь файл в память. Возвращаются только полные строки.

Также смотрите следующую ссылку о разнице в скорости между чтением по строкам и всего файла. http://handyfloss.wordpress.com/2008/02/15/python-speed-vs-memory-tradeoff-reading-files/

2. Попробуйте записать файл целиком

Вы также можете сохранять строки и записывать их сразу в конце, хотя я не уверен, что это сильно поможет

big_file = open('C:\\gbigfile.txt', 'r')
big_file_lines = big_file.read_lines()
small_file_lines = []
for line in big_file_lines:
   if 'S0414' in line:
      small_file_lines.append(line)
small_file3 = open('C:\\small_file3.txt', 'w')
small_file3.write("".join(small_file_lines))
small_file3.close()

3. Попробуйте использовать фильтр

Вы также можете попробовать использовать фильтр вместо цикла, посмотрим, есть ли разница

small_file_lines= filter(lambda line:line.find('S0414') >= 0, big_file_lines)
1
ответ дан 13 December 2019 в 22:00
поделиться

Если строка начинается с S0414, вы можете использовать метод .startswith :

if line.startswith('S0414'): small_file3.write(line)

Вы также можете удалить левый пробел, если он есть:

line.lstrip().startswith('S0414')

If 'S0414' всегда появляется после определенной точки, например, это всегда не менее 10 символов, а не последние 5 символов, вы можете сделать:

'S0414' in line[10:-5]

В противном случае вам придется искать по каждой строке, как и вы .

0
ответ дан 13 December 2019 в 22:00
поделиться

Тестирование многих условий на строку обычно происходит медленно при использовании наивного алгоритма. Существуют различные превосходные алгоритмы (например, с использованием Tries), которые могут быть намного лучше. Я предлагаю вам дать шанс алгоритму сопоставления строк Ахо-Корасика. См. здесь для реализации python. Это должно быть значительно быстрее, чем наивный подход использования вложенного цикла и тестирования каждой строки по отдельности.

2
ответ дан 13 December 2019 в 22:00
поделиться

Какие критерии определяют 30000 строк, которые вы хотите извлечь? Чем больше информации вы дадите, тем больше у вас шансов получить полезный ответ.

Если вы хотите, чтобы все строки содержали определенную строку или, в более общем смысле, содержали любой из заданного набора строк или вхождение регулярного выражения, используйте grep . Вероятно, это будет значительно быстрее для больших наборов данных.

0
ответ дан 13 December 2019 в 22:00
поделиться

Это напоминает мне проблему, описанную Тимом Бреем , который пытался извлечь данные из файлов журнала веб-сервера с помощью многоядерных машин. Результаты описаны в Проект Wide Finder и Wide Finder 2 . Итак, если последовательная оптимизация идет недостаточно быстро для вас, возможно, вам стоит начать с этого. Есть примеры такого рода проблем во многих языках, включая python . Ключевая цитата из последней ссылки:

Резюме

В этой статье мы взяли относительно быструю реализацию Python и оптимизировали ее, используя количество приемов:

  • Предварительно скомпилированные шаблоны RE
  • Быстрая фильтрация строк-кандидатов
  • Чтение по фрагментам
  • Несколько процессов
  • Отображение памяти в сочетании с поддержкой операций RE в отображенных буферах

Это уменьшило время, необходимое для синтаксического анализа 200 мегабайт данных журнала, с 6,7 секунды до 0,8 секунд на тестовой машине. Или, другими словами, финальная версия более чем в 8 раз быстрее чем исходная версия Python, и (потенциально) в 600 раз быстрее, чем исходная версия Тима Версия Erlang.

Сказав это, 30 000 строк - это не то много, поэтому вы можете хотя бы начать с исследования производительности чтения / записи вашего диска.Помогает ли, если вы записываете вывод не на диск, с которого вы читаете ввод, или читаете весь файл за один раз перед обработкой?

0
ответ дан 13 December 2019 в 22:00
поделиться

Этот метод предполагает специальные значения появляются в той же позиции в строке в gbigfile

def mydict(iterable):
    d = {}
    for k, v in iterable:
        if k in d:
            d[k].append(v)
        else:
            d[k] = [v]
    return d

with open("C:\\to_find.txt", "r") as t:
    tofind = mydict([(x[0], x) for x in t.readlines()])

with open("C:\\gbigfile.txt", "r") as bigfile:
    with open("C:\\outfile.txt", "w") as outfile:
        for line in bigfile:
            seq = line[4:9]
            if seq in tofind[seq[0]]:
                outfile.write(line)

. В зависимости от того, какое распределение начальной буквы в этих целевых объектах, вы можете значительно сократить количество сравнений. Если вы не знаете, где появятся значения, вы говорите о ДЛИННОЙ операции, потому что вам придется сравнивать сотни тысяч - скажем, 300 000 - 30 000 раз. Это 9 миллионов сравнений, которые займут долгое время.

0
ответ дан 13 December 2019 в 22:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: