Pandas, Анализ данных

Обработка больших файлов. Часть 2.

Время прочтения: 3 мин.

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

counter = 0
buffer_df = pandas.DataFrame()
current_line = []
buffer_line_list = []
result_df = pandas.DataFrame()
with open('example.csv', 'r', encoding = 'utf-8-sig' ) as fl:
    header = fl.readline() #подразумеваем что в первой строке файла содержится заголовок
    header = header.split(',')
    for line in fl:
        current_line = line.split(',') #делим строку по разделительному символу
        buffer_line_list += [current_line] #добавляем строку в накопительный список
        counter +=1
        if(counter ==  100000): #достигаем требуемого порогового значения
            buffer_df = pandas.DataFrame(buffer_line_list)
            buffer_df.columns = header
            # (...) в этой части кода можно выполнять требуемые группировки
            result_df = result_df.append(buffer_df)
            counter = 0
            buffer_line_list = []
            counter = 0
    #важно помнить что если выставленное нами условие на размер не делит файл 
    #на равное количество частей, то у нас останется часть необработанных данных
    buffer_df = pandas.DataFrame(buffer_line_list)
    buffer_df.columns = header
    # (...) в этой части кода можно выполнять требуемые группировки
    result_df = result_df.append(buffer_df)

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

 Полученный промежуточный результат потребуется обработать ещё один раз, и выполнить требуемые группировки над result_df, чтобы получить итоговые значения.


Рассмотрим другой случай, когда нам требуется объединить между собой два больших файла, например, список операций и список клиентов. Одним из лучших вариантов для этого будет использование функции mergeв функционала pandas. Однако подобное объединение требует ещё большего объёма ресурсов — соответственно приходится читать оба файла частями.             Разумеется, чтение двух файлов по частям (при этом, один из файлов потребуется читать неоднократно) занимает много времени, поэтому по возможности следует уменьшать размер файла заранее, чтобы оба файла было возможно вносить в оперативную память целиком.  Если это сделать невозможно, то можно подготовить один из файлов заранее, сформировав из него dataframe объекты и сохранить из на диск с использованием модуля pickle:

import pickle
file_counter = 0
...
            buffer_df = pandas.DataFrame(buffer_line_list)
            buffer_df.columns = header
            f = open('file1' + str(file_counter), 'wb') #открываем файл для записи
            pickle.dump(buffer_df, f) #сохраняем готовый датафрейм
            f.close()
...
    f = open('file1' + str(file_counter), 'wb') #повторяем процедуру для оставшихся строк
    pickle.dump(buffer_df, f)
    f.close()

Таким образом мы сокращаем время на обработку файла, так как нам не требуется каждый раз «по новой» читать файл и обрабатывать строки.
Обработка, связанная с объединением двух файлов, в таком случае, выглядит так:

import pandas 
import pickle 
import glob
...
file_list =glob.glob('file1*') # читаем список файлов загруженных ранее
with open('example.csv', 'r', encoding = 'utf-8-sig' ) as fl:
    ...
        if(counter >=  100000): #достигаем требуемого порогового значения
            buffer_df = pandas.DataFrame(buffer_line_list)
            buffer_df.columns = header
            for file in file_list: #для каждого файла в списке
                second_file = open(file, 'rb') #открваем поток к файлу из ранее прогруженых
                second_df = pickle.load(second_file) # загружаем файл в оперативную память
                second_file.close() # закрываем поток
                result_df.append(buffer_df.merge(second_df)) # объёдиняем две части и записывем результат
    #важно помнить что если выставленное нами условие на размер не делит файл 
    #на равное количество частей, то у нас останется часть необработанных данных
    ...

Подобные практики позволяют обрабатывать файлы любого размера, объединять их между собой, выполнять группировки и другие аналитические функции, зачастую без каких-либо калибровок в алгоритме, не упираясь в ограничения сред Excel, Access или в технические ограничения, вызванные объёмом оперативной памяти. Это, в свою очередь, позволяет большему числу сотрудников заниматься работой с массивными выборками.

Советуем почитать