"Работа с большими данными" в pandas [закрыто]
Проблема с обработкой больших объемов данных с использованием Pandas
Я изучаю библиотеку Pandas в Python, и озадачен тем, как эффективно обрабатывать большие наборы данных, которые не могут быть полностью загружены в память. В моей повседневной работе я использую SAS, который отлично справляется с поддержкой внешних данных. Однако сам по себе SAS несовершенен по многим другим причинам, и я надеюсь в будущем заменить его на Python и Pandas.
Мой текущий вызов заключается в том, что мне необходимо установить рабочий процесс, способный обрабатывать большие файлы, которые слишком велики, чтобы поместиться в оперативной памяти, но достаточно малы, чтобы храниться на жестком диске. Я не имею в виду «большие данные», требующие распределенной обработки, а просто файлы, которые могут быть слишком объемными.
В качестве первого шага я подумал о использовании HDFStore
для хранения больших наборов данных на диске и выборочного импорта необходимых кусочков данных для анализа. Некоторые коллеги упомянули MongoDB как более удобную альтернативу. Я хотел бы узнать, каковы рекомендации по организации рабочего процесса для следующих задач:
- Загрузка плоских файлов в постоянную структуру базы данных на диске.
- Запрос данных из этой базы, чтобы передать их в структуру данных Pandas.
- Обновление базы данных после манипуляции данными в Pandas.
Буду признателен за любые примеры из реальной практики, особенно от тех, кто работает с «большими данными» в Pandas.
Дополнительные детали:
- Я хочу поэтапно импортировать большие плоские файлы и сохранять их в постоянной структуре базы данных на диске. Эти файлы зачастую слишком велики, чтобы помещаться в память.
- Я хочу загружать подмножества данных (чаще всего всего несколько столбцов) для работы с Pandas.
- На основе выбранных столбцов я буду создавать новые колонки, выполняя различные операции.
- Затем мне нужно будет добавить созданные новые колонки обратно в структуру базы данных.
Ищу наилучшие практики для выполнения этих шагов. Судя по информации о pandas и pytables, добавление нового столбца может представлять собой проблему.
Ответы на вопросы о контексте:
Я занимаюсь моделированием рисков потребительского кредитования с использованием данных, таких как характеристики телефонов, номера социального страхования, адреса, значения недвижимости и негативная информация, включая уголовные дела и банкротства. Каждый набор данных, с которым я работаю, содержит в среднем от 1000 до 2000 полей, включая непрерывные, номинальные и порядковые переменные. Я почти никогда не добавляю строки, но часто создаю новые столбцы.
Обычные операции включают в себя комбинирование нескольких столбцов с помощью условной логики в новый составной столбец.
После выполнения операций мне необходимо добавлять новые столбцы в структуру данных на диске. Я периодически исследую данные и ищу интересные взаимосвязи, которые можно моделировать.
Типичный проект требует обработки файлов размером около 1 ГБ. Файлы организованы так, чтобы каждая строка представляла собой запись данных потребителя.
Я редко выбираю строки при создании нового столбца, но часто делаю это при создании отчетов или генерации описательных статистик.
Мне нужна помощь в поиске оптимальных подходов для реализации этих шагов в Python с использованием библиотеки Pandas.
5 ответ(ов)
Я думаю, что в ответах выше не хватает простого подхода, который оказался для меня очень полезным.
Когда у меня есть файл, который слишком велик, чтобы загрузить его в память, я разбиваю его на несколько меньших файлов (либо по строкам, либо по колонкам).
Например, если у меня есть данные о торгах за 30 дней размером около 30 ГБ, я разбиваю их на файл за каждый день размером около 1 ГБ. Затем я обрабатываю каждый файл отдельно и агрегирую результаты в конце.
Одно из основных преимуществ такого подхода заключается в том, что это позволяет параллельную обработку файлов (либо с помощью нескольких потоков, либо с использованием процессов).
Также стоит отметить, что манипуляции с файлами (например, добавление или удаление дат в приведенном примере) можно выполнять с помощью обычных команд оболочки, что может быть невозможно при работе с более сложными форматами файлов.
Хотя этот подход не охватывает все сценарии, он очень полезен во многих из них.
Один полезный прием, который я нашел для работы с большими объемами данных, заключается в снижении объема данных за счет уменьшения точности чисел с плавающей запятой до 32 бит. Это не всегда применимо, но во многих случаях 64-битная точность избыточна, и экономия в памяти в 2 раза того стоит. Чтобы сделать эту очевидную идею еще более наглядной:
>>> df = pd.DataFrame(np.random.randn(int(1e8), 5))
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float64(5)
memory usage: 3.7 GB
>>> df.astype(np.float32).info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float32(5)
memory usage: 1.9 GB
Как видно из приведенных данных, использование float32
позволяет существенно сократить использование памяти, что может быть особенно полезно при работе с большими наборами данных.
Я заметил этот вопрос немного поздно, но работаю над похожей задачей (модели предоплаты по ипотечным кредитам). Моё решение заключалось в том, чтобы пропустить уровень HDFStore в pandas и использовать только pytables. Я сохраняю каждый столбец как отдельный массив HDF5 в финальном файле.
Мой основной рабочий процесс заключается в том, чтобы сначала получить CSV-файл из базы данных. Я сжимаю его с помощью gzip, чтобы он не занимал слишком много места. Затем я конвертирую его в файл HDF5 ориентации по строкам, итерируя его в Python, преобразуя каждую строку в реальный тип данных и записывая его в HDF5 файл. Это занимает несколько десятков минут, но не использует память, так как работает построчно. Затем я "транспонирую" файл HDF5 ориентации по строкам в файл HDF5 ориентации по столбцам.
Вот как выглядит операция транспонирования:
def transpose_table(h_in, table_path, h_out, group_name="data", group_path="/"):
# Получаем ссылку на входные данные.
tb = h_in.getNode(table_path)
# Создаем выходную группу для хранения столбцов.
grp = h_out.createGroup(group_path, group_name, filters=tables.Filters(complevel=1))
for col_name in tb.colnames:
logger.debug("Обрабатываю %s", col_name)
# Получаем данные.
col_data = tb.col(col_name)
# Создаем выходной массив.
arr = h_out.createCArray(grp,
col_name,
tables.Atom.from_dtype(col_data.dtype),
col_data.shape)
# Сохраняем данные.
arr[:] = col_data
h_out.flush()
Чтение данных обратно выглядит следующим образом:
def read_hdf5(hdf5_path, group_path="/data", columns=None):
"""Читать транспонированный набор данных из файла HDF5."""
if isinstance(hdf5_path, tables.file.File):
hf = hdf5_path
else:
hf = tables.openFile(hdf5_path)
grp = hf.getNode(group_path)
if columns is None:
data = [(child.name, child[:]) for child in grp]
else:
data = [(child.name, child[:]) for child in grp if child.name in columns]
# Преобразуем любые столбцы float32 в float64 для обработки.
for i in range(len(data)):
name, vec = data[i]
if vec.dtype == np.float32:
data[i] = (name, vec.astype(np.float64))
if not isinstance(hdf5_path, tables.file.File):
hf.close()
return pd.DataFrame.from_items(data)
Как правило, я выполняю это на машине с большим объемом памяти, поэтому я могу быть недостаточно осторожным с использованием памяти. Например, по умолчанию операция загрузки считывает весь набор данных.
В целом, это работает для меня, но выглядит несколько громоздко, и я не могу использовать всю магию pytables.
Правка: Реальное преимущество данного подхода, по сравнению со стандартным массивом записей pytables, заключается в том, что я могу загружать данные в R с помощью h5r, который не может обрабатывать таблицы. По крайней мере, мне не удалось заставить его загрузить гетерогенные таблицы.
Еще одна вариация
Многие операции, выполняемые в pandas, также могут быть реализованы с помощью запросов к базе данных (SQL, MongoDB).
Использование реляционной базы данных или MongoDB позволяет выполнять некоторые агрегации непосредственно в запросах к базе данных (которые оптимизированы для работы с большими объемами данных и эффективно используют кэш и индексы).
Позже вы можете выполнить постобработку с использованием pandas.
Преимущество такого подхода заключается в том, что вы получаете оптимизации базы данных для работы с большими данными, одновременно определяя логику на высоком уровне с помощью декларативного синтаксиса — при этом вам не нужно заботиться о деталях, связанных с тем, что выполнять в памяти, а что — вне оперативной памяти.
Хотя языки запросов и pandas различаются, обычно не представляет сложности перевести часть логики с одного языка на другой.
Я хотел бы обратить внимание на пакет Vaex.
Vaex — это библиотека Python для "ленивых" Out-of-Core DataFrame (аналогично Pandas), которая позволяет визуализировать и исследовать большие табличные наборы данных. Она может вычислять статистики, такие как среднее, сумма, количество, стандартное отклонение и т.д. на N-мерной сетке с производительностью до миллиарда (10^9) объектов/строк в секунду. Визуализация осуществляется с помощью гистограмм, плотностей и 3D-объемного рендеринга, что позволяет интерактивно исследовать большие данные. Vaex использует отображение памяти, политику нулевого копирования и ленивые вычисления для достижения наилучшей производительности (без desperdicio памяти).
Посмотрите документацию: https://vaex.readthedocs.io/en/latest/ API очень похож на API Pandas.
Переименование названий столбцов в Pandas
"Красивая печать всей Series / DataFrame в Pandas"
Запись DataFrame pandas в CSV файл
Преобразование списка словарей в DataFrame pandas
Объединение двух столбцов текста в DataFrame pandas