Подтвердить что ты не робот

Обработать очень большой (> 20 ГБ) текстовый файл по строкам

У меня есть несколько очень больших текстовых файлов, которые мне нужно обрабатывать, причем самое большое - около 60 ГБ.

Каждая строка имеет 54 символа в семи полях, и я хочу удалить последние три символа из каждого из трех первых полей, что должно уменьшить размер файла примерно на 20%.

Я новичок в Python и имею код, который будет делать то, что я хочу делать примерно в 3,4 ГБ в час, но для того, чтобы быть полезным упражнением, мне действительно нужно получать не менее 10 ГБ/ч - есть ли какие-либо способ ускорить это? Этот код не близок к вызову моего процессора, поэтому я делаю необразованное предположение, что он ограничен скоростью чтения и записи на внутреннем жестком диске?

ProcessLargeTextFile():
    r = open("filepath", "r")
    w = open("filepath", "w")
    l = r.readline()
    while l:
        x = l.split(' ')[0]
        y = l.split(' ')[1]
        z = l.split(' ')[2]
        w.write(l.replace(x,x[:-3]).replace(y,y[:-3]).replace(z,z[:-3]))
        l = r.readline()
r.close()
w.close()

Любая помощь будет действительно оценена. Я использую IDLE Python GUI в Windows 7 и имею 16 ГБ памяти - возможно, другая ОС была бы более эффективной?

Изменить: Вот фрагмент файла для обработки.

70700.642014 31207.277115 -0.054123 -1585 255 255 255
70512.301468 31227.990799 -0.255600 -1655 155 158 158
70515.727097 31223.828659 -0.066727 -1734 191 187 180
70566.756699 31217.065598 -0.205673 -1727 254 255 255
70566.695938 31218.030807 -0.047928 -1689 249 251 249
70536.117874 31227.837662 -0.033096 -1548 251 252 252
70536.773270 31212.970322 -0.115891 -1434 155 158 163
70533.530777 31215.270828 -0.154770 -1550 148 152 156
70533.555923 31215.341599 -0.138809 -1480 150 154 158
4b9b3361

Ответ 1

Это более идиоматично писать свой код, подобный этому

def ProcessLargeTextFile():
    with open("filepath", "r") as r, open("outfilepath", "w") as w:
        for line in r:
            x, y, z = line.split(' ')[:3]
            w.write(line.replace(x,x[:-3]).replace(y,y[:-3]).replace(z,z[:-3]))

Основное сохранение здесь - это просто сделать split один раз, но если ЦП не облагается налогом, это, вероятно, будет иметь очень малое значение.

Это может помочь сэкономить несколько тысяч строк за один раз и записать их одним ударом, чтобы уменьшить износ вашего жесткого диска. Миллион строк - всего 54 МБ ОЗУ!

def ProcessLargeTextFile():
    bunchsize = 1000000     # Experiment with different sizes
    bunch = []
    with open("filepath", "r") as r, open("outfilepath", "w") as w:
        for line in r:
            x, y, z = line.split(' ')[:3]
            bunch.append(line.replace(x,x[:-3]).replace(y,y[:-3]).replace(z,z[:-3]))
            if len(bunch) == bunchsize:
                w.writelines(bunch)
                bunch = []
        w.writelines(bunch)

предложенный @Janne, альтернативный способ генерации строк

def ProcessLargeTextFile():
    bunchsize = 1000000     # Experiment with different sizes
    bunch = []
    with open("filepath", "r") as r, open("outfilepath", "w") as w:
        for line in r:
            x, y, z, rest = line.split(' ', 3)
            bunch.append(' '.join((x[:-3], y[:-3], z[:-3], rest)))
            if len(bunch) == bunchsize:
                w.writelines(bunch)
                bunch = []
        w.writelines(bunch)

Ответ 2

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

  • Удалите любую обработку из вашего кода. Просто прочитайте и напишите данные и измерьте скорость. Если чтение и запись файлов происходит слишком медленно, это не проблема вашего кода.
  • Если чтение и запись уже медленны, попробуйте использовать несколько дисков. Вы одновременно читаете и пишете. На том же диске? Если да, попробуйте использовать разные диски и повторите попытку.
  • Может также помочь некоторая библиотека async io (Twisted?).

Если вы выяснили точную проблему, попросите снова об оптимизации этой проблемы.

Ответ 3

Как вы, кажется, не ограничены процессором, а скорее I/O, попробовали ли вы с некоторыми изменениями третьего параметра open?

В самом деле, этот третий параметр может использоваться, чтобы предоставить размер буфера, который будет использоваться для операций с файлами!

Простое запись open( "filepath", "r", 16777216 ) будет использовать буферы 16 МБ при чтении из файла. Это должно помочь.

Используйте то же самое для выходного файла и измерьте/сравните с идентичным файлом для остальных.

Примечание. Это та же самая оптимизация, предложенная другими, но вы можете получить ее здесь бесплатно, без изменения кода, без необходимости буферизации.

Ответ 4

Я добавлю этот ответ, чтобы объяснить, почему буферизация имеет смысл, а также предлагает еще одно решение

Вы получаете потрясающе плохую производительность. Эта статья Возможно ли ускорение ввода-вывода python? показывает, что чтение 10 ГБ должно занимать около 3 минут. Последовательная запись имеет одинаковую скорость. Таким образом, вам не хватает фактора в 30 раз, и ваша целевая производительность по-прежнему в 10 раз медленнее, чем то, что должно быть возможным.

Почти наверняка такое несоответствие заключается в количестве голов, которое ищет диск. Головной поиск занимает миллисекунды. Один поиск соответствует нескольким мегабайтам последовательного чтения-записи. Огромно дорого. Операции копирования на одном диске требуют поиска между входом и выходом. Как уже было сказано, одним из способов сокращения запросов является буферизация таким образом, что многие мегабайты считываются перед записью на диск и наоборот. Если вы можете убедить систему python io сделать это, отлично. В противном случае вы можете читать и обрабатывать строки в массиве строк, а затем писать после того, как будут готовы 50 мб вывода. Этот размер означает, что поиск приведет к снижению производительности на 10% по отношению к самой передаче данных.

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

Ответ 5

Те, кажется, очень большие файлы... Почему они такие большие? Какую обработку вы делаете в каждой строке? Почему бы вам не использовать базу данных с некоторыми сокращениями на карте (если это необходимо) или простыми операциями данных? Точка базы данных - это абстрагирование обработки и управления большими объемами данных, которые не могут вписаться в память.

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

Создать базу данных

 conn = sqlite3.connect('pts.db')
 c = conn.cursor()

Создает таблицу

c.execute('''CREATE TABLE ptsdata (filename, line, x, y, z''')

Затем используйте один из приведенных выше алгоритмов, чтобы вставить все строки и точки в базе данных, вызывая

c.execute("INSERT INTO ptsdata VALUES (filename, lineNumber, x, y, z)")

Теперь, как вы его используете, зависит от того, что вы хотите сделать. Например, чтобы работать со всеми точками в файле, выполнив запрос

c.execute("SELECT lineNumber, x, y, z FROM ptsdata WHERE filename=file.txt ORDER BY lineNumber ASC")

И получите n строки за раз из этого запроса с помощью

c.fetchmany(size=n)

Я уверен, что где-то в sql-операторе есть лучшая оболочка, но вы получаете идею.

Ответ 6

Ваш код довольно не идиоматичен и делает гораздо больше вызовов функций, чем нужно. Простейшая версия:

ProcessLargeTextFile():
    with open("filepath") as r, open("output") as w:
        for line in r:
            fields = line.split(' ')
            fields[0:2] = [fields[0][:-3], 
                           fields[1][:-3],
                           fields[2][:-3]]
            w.write(' '.join(fields))

и я не знаю о современной файловой системе, которая медленнее Windows. Поскольку, по-видимому, вы используете эти огромные файлы данных в качестве баз данных, считаете ли вы использование реальной базы данных?

Наконец, если вы просто заинтересованы в уменьшении размера файла, рассмотрели ли вы сжатие/архивирование файлов?

Ответ 7

ProcessLargeTextFile():
    r = open("filepath", "r")
    w = open("filepath", "w")
    l = r.readline()
    while l:

Как уже было предложено, вы можете использовать цикл for, чтобы сделать это более оптимальным.

    x = l.split(' ')[0]
    y = l.split(' ')[1]
    z = l.split(' ')[2]

Вы выполняете разделенную операцию 3 раза здесь, в зависимости от размера каждой строки это будет иметь влияние на производительность. Вы должны разделить один раз и назначить x, y, z для записей в возвращаемом массиве.

    w.write(l.replace(x,x[:-3]).replace(y,y[:-3]).replace(z,z[:-3]))

Каждая строка, которую вы читаете, записывается непосредственно в файл, что очень интенсивно. Вы должны рассмотреть возможность буферизации вывода в память и периодического нажатия на диск. Что-то вроде этого:

BUFFER_SIZE_LINES = 1024 # Maximum number of lines to buffer in memory

def ProcessLargeTextFile():
    r = open("filepath", "r")
    w = open("filepath", "w")
    buf = ""
    bufLines = 0
    for lineIn in r:

        x, y, z = lineIn.split(' ')[:3]
        lineOut = lineIn.replace(x,x[:-3]).replace(y,y[:-3]).replace(z,z[:-3])
        bufLines+=1

        if bufLines >= BUFFER_SIZE:
            # Flush buffer to disk
            w.write(buf)
            buf = ""
            bufLines=1

        buf += lineOut + "\n"

    # Flush remaining buffer to disk
    w.write(buf)
    buf.close()
    r.close()
    w.close()

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

Ответ 8

Поскольку вы упоминаете только экономию места в качестве преимущества, есть ли причина, по которой вы не можете просто сохранить файлы gzipped? Это должно сэкономить 70% и выше по этим данным. Или подумайте о том, чтобы заставить NTFS сжимать файлы, если по-прежнему важен случайный доступ. Вы получите гораздо более значительную экономию времени ввода-вывода после любого из них.

Что еще более важно, где ваши данные, что вы получаете всего 3,4 ГБ/час? Это ниже скорости USBv1.

Ответ 9

Прочитайте файл с помощью for l in r:, чтобы воспользоваться буферизацией.

Ответ 10

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

вы также можете попробовать не запускать его в gui. Запустите его в cmd.