Как получить время, необходимое для распаковки больших файлов bz2?

Мне нужно обрабатывать большие файлы bz2 (~ 6G) с помощью Python, распаковывая их построчно, используя BZ2File.readline(). Проблема в том, что я хочу знать, сколько времени нужно для обработки всего файла.

Я сделал много поисков, попытался получить фактический размер распакованного файла, чтобы я мог знать процент, обработанный на лету, и, следовательно, оставшееся время, в то время как вывод состоит в том, что невозможно узнать размер распакованного файла. без предварительной распаковки (https://stackoverflow.com/a/12647847/7876675).

Помимо того, что распаковка файла занимает много памяти, сама распаковка занимает много времени. Итак, может ли кто-нибудь помочь мне получить оставшееся время обработки на лету?

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

quamrana 08.02.2019 17:55

Используя модуль bz2, вы сможете открыть его в python как поток и прочитать каждую строку. это сэкономит вам память. Что касается того, сколько времени это займет, это во многом зависит от того, что для вас значит «процесс».

owenrumney 08.02.2019 17:56

@quamrana Боюсь, нет, как указано в ссылке, которую я дал выше.

NeuralNotworks 08.02.2019 18:04

@owen79 Давайте возьмем самый простой случай: я могу записать время для всех строк, уже прочитанных readline(), но меня больше волнует, сколько времени осталось, чтобы закончить этот файл.

NeuralNotworks 08.02.2019 18:07

Возможно, вы сможете вызвать tell() для файлового объекта, переданного BZ2File , и сравнить текущую позицию с размером всего файла, что даст вам оценку того, сколько было обработано. Этой информации плюс знание того, сколько времени уже прошло, будет достаточно, чтобы предсказать, сколько времени это займет.

martineau 08.02.2019 18:09

@martineau tell() дает вам позицию в файле декомпрессия, но опять же, вам нужно знать исходный размер файла, чтобы оценить процент обработки.

NeuralNotworks 08.02.2019 19:23

Вызов tell() для базового (сжатого) файла, который BZ2File читает (пока он находится в процессе чтения), даст вам представление о том, какая часть файла была обработана.

martineau 08.02.2019 19:28

@martineau Спасибо за объяснение. Вы правы насчет tell(), но, не зная оставшегося размера файла несжатый, оценить оставшееся время все равно невозможно.

NeuralNotworks 08.02.2019 20:18

Из вашего последнего комментария я боюсь, что вы все еще не понимаете. Не обязательно знать оставшийся размер, достаточно знать общий размер. Позвонив в tell(), вы узнаете, какая часть этой суммы была обработана BZ2File в любой момент времени. Например, предположим, что 25%, если общий размер файла был прочитан до сих пор, и это заняло 5 секунд. Это позволит вам оценить, что чтение оставшихся 75% займет в 4 раза больше времени: т.е. (4 * 5) = 20 секунд.

martineau 08.02.2019 20:33

Как я упоминал в описании вопроса, невозможно узнать общий размер (или обработанный процент) несжатого файла без предварительной распаковки всего файла. @martineau спасибо за попытку помочь.

NeuralNotworks 09.02.2019 14:37

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

martineau 09.02.2019 15:22

Хорошо, здесь много недоразумений. Две вещи я могу получить точно: размер файла сжатый и положение в файле несжатый с помощью tell(). Если вы, @martineau, предлагаете использовать tell() для оценки обработанного процента файла сжатый, я должен с уважением не согласиться, потому что сжатый файл обычно в несколько раз меньше несжатого файла, а это означает, что процент скоро превысит 100%. Спасибо за ваши советы, но они не решили мою проблему.

NeuralNotworks 09.02.2019 15:57

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

martineau 09.02.2019 16:14
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
13
1 380
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

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

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

Вот простой пример использования объекта BZ2Decompress для обработки входных данных по частям за раз, показывающий ход чтения (Python 3, получение имени файла из командной строки):

# Decompress a bzip2 file, showing progress based on consumed input.

import sys
import os
import bz2
import time

def proc(input):
    """Decompress and process a piece of a compressed stream"""
    dat = dec.decompress(input)
    got = len(dat)
    if got != 0:    # 0 is common -- waiting for a bzip2 block
        # process dat here
        pass
    return got

# Get the size of the compressed bzip2 file.
path = sys.argv[1]
size = os.path.getsize(path)

# Decompress CHUNK bytes at a time.
CHUNK = 16384
totin = 0
totout = 0
prev = -1
dec = bz2.BZ2Decompressor()
start = time.time()
with open(path, 'rb') as f:
    for chunk in iter(lambda: f.read(CHUNK), b''):
        # feed chunk to decompressor
        got = proc(chunk)

        # handle case of concatenated bz2 streams
        if dec.eof:
            rem = dec.unused_data
            dec = bz2.BZ2Decompressor()
            got += proc(rem)

        # show progress
        totin += len(chunk)
        totout += got
        if got != 0:    # only if a bzip2 block emitted
            frac = round(1000 * totin / size)
            if frac != prev:
                left = (size / totin - 1) * (time.time() - start)
                print(f'\r{frac / 10:.1f}% (~{left:.1f}s left) ', end='')
                prev = frac

# Show the resulting size.
print(end='\r')
print(totout, 'uncompressed bytes')

Да, я могу получить размер всего сжатого файла, но есть идеи, как получить потребляемый размер?

NeuralNotworks 09.02.2019 14:42

Используйте BZ2Decompressor и загружайте его только порциями. Тогда потребляемый размер - это то, что вы кормили до сих пор.

Mark Adler 09.02.2019 16:40

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

  1. прочитать сжатый файл как байтовый объект в память: byte_data, что довольно быстро
  2. рассчитать размер byte_data, используя total_size = len(byte_data)
  3. обернуть byte_data как byte_f = io.BytesIO(byte_data)
  4. обернуть byte_f как bz2f = bz2.BZ2File(byte_f)
  5. во время обработки используйте pos = byte_f.tell() для получения текущей позиции в файле сжатый
  6. рассчитать точный процент обработанного percent = pos/total_size
  7. записать использованное время и рассчитать оставшееся время

Через несколько секунд оценка может стать довольно точной:

0.01% processed, 2.00s elapsed, 17514.27s remaining...
0.02% processed, 4.00s elapsed, 20167.48s remaining...
0.03% processed, 6.00s elapsed, 21239.60s remaining...
0.04% processed, 8.00s elapsed, 21818.91s remaining...
0.05% processed, 10.00s elapsed, 22180.76s remaining...
0.05% processed, 12.00s elapsed, 22427.78s remaining...
0.06% processed, 14.00s elapsed, 22661.80s remaining...
0.07% processed, 16.00s elapsed, 22840.45s remaining...
0.08% processed, 18.00s elapsed, 22937.07s remaining...
....
99.97% processed, 22704.28s elapsed, 6.27s remaining...
99.98% processed, 22706.28s elapsed, 4.40s remaining...
99.99% processed, 22708.28s elapsed, 2.45s remaining...
100.00% processed, 22710.28s elapsed, 0.54s remaining...

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

Mark Adler 09.02.2019 16:37

@MarkAdler Думаю, ты прав. Но в моей ситуации чтение всего файла в памяти ускорит работу программы, а использование BZ2File.readline() может значительно упростить обработку.

NeuralNotworks 14.02.2019 17:44

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

import bz2
import datetime
import time

with bz2.open(input_filename, 'rt', encoding='utf8') as input_file:
    underlying_file = input_file.buffer._buffer.raw._fp
    underlying_file.seek(0, io.SEEK_END)
    underlying_file_size = underlying_file.tell()
    underlying_file.seek(0, io.SEEK_SET)
    lines_count = 0
    start_time = time.perf_counter()
    progress = f'{0:.2f}%'

    while True:
        line = input_file.readline().strip()
        if not line:
            break

        process_line(line)

        lines_count += 1
        current_position = underlying_file.tell()
        new_progress = f'{current_position / underlying_file_size * 100:.2f}%'
        if progress != new_progress:
            progress = new_progress
            current_time = time.perf_counter()
            elapsed_time = current_time - start_time
            elapsed = datetime.timedelta(seconds=elapsed_time)
            remaining = datetime.timedelta(seconds=(underlying_file_size / current_position - 1) * elapsed_time)
            print(f"{lines_count} lines processed, {progress}, {elapsed} elapsed, {remaining} remaining")

Если вы читаете не текстовые файлы, а двоичные файлы, вам нужно использовать:

with bz2.open(input_filename, 'rb') as input_file:
    underlying_file = input_file._buffer.raw._fp
    ...

Другие вопросы по теме