11

"Работа с большими данными" в pandas [закрыто]

15

Проблема с обработкой больших объемов данных с использованием Pandas

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

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

В качестве первого шага я подумал о использовании HDFStore для хранения больших наборов данных на диске и выборочного импорта необходимых кусочков данных для анализа. Некоторые коллеги упомянули MongoDB как более удобную альтернативу. Я хотел бы узнать, каковы рекомендации по организации рабочего процесса для следующих задач:

  1. Загрузка плоских файлов в постоянную структуру базы данных на диске.
  2. Запрос данных из этой базы, чтобы передать их в структуру данных Pandas.
  3. Обновление базы данных после манипуляции данными в Pandas.

Буду признателен за любые примеры из реальной практики, особенно от тех, кто работает с «большими данными» в Pandas.

Дополнительные детали:

  1. Я хочу поэтапно импортировать большие плоские файлы и сохранять их в постоянной структуре базы данных на диске. Эти файлы зачастую слишком велики, чтобы помещаться в память.
  2. Я хочу загружать подмножества данных (чаще всего всего несколько столбцов) для работы с Pandas.
  3. На основе выбранных столбцов я буду создавать новые колонки, выполняя различные операции.
  4. Затем мне нужно будет добавить созданные новые колонки обратно в структуру базы данных.

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

Ответы на вопросы о контексте:

  1. Я занимаюсь моделированием рисков потребительского кредитования с использованием данных, таких как характеристики телефонов, номера социального страхования, адреса, значения недвижимости и негативная информация, включая уголовные дела и банкротства. Каждый набор данных, с которым я работаю, содержит в среднем от 1000 до 2000 полей, включая непрерывные, номинальные и порядковые переменные. Я почти никогда не добавляю строки, но часто создаю новые столбцы.

  2. Обычные операции включают в себя комбинирование нескольких столбцов с помощью условной логики в новый составной столбец.

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

  4. Типичный проект требует обработки файлов размером около 1 ГБ. Файлы организованы так, чтобы каждая строка представляла собой запись данных потребителя.

  5. Я редко выбираю строки при создании нового столбца, но часто делаю это при создании отчетов или генерации описательных статистик.

Мне нужна помощь в поиске оптимальных подходов для реализации этих шагов в Python с использованием библиотеки Pandas.

5 ответ(ов)

1

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

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

Например, если у меня есть данные о торгах за 30 дней размером около 30 ГБ, я разбиваю их на файл за каждый день размером около 1 ГБ. Затем я обрабатываю каждый файл отдельно и агрегирую результаты в конце.

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

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

Хотя этот подход не охватывает все сценарии, он очень полезен во многих из них.

0

Один полезный прием, который я нашел для работы с большими объемами данных, заключается в снижении объема данных за счет уменьшения точности чисел с плавающей запятой до 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 позволяет существенно сократить использование памяти, что может быть особенно полезно при работе с большими наборами данных.

0

Я заметил этот вопрос немного поздно, но работаю над похожей задачей (модели предоплаты по ипотечным кредитам). Моё решение заключалось в том, чтобы пропустить уровень 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, который не может обрабатывать таблицы. По крайней мере, мне не удалось заставить его загрузить гетерогенные таблицы.

0

Еще одна вариация

Многие операции, выполняемые в pandas, также могут быть реализованы с помощью запросов к базе данных (SQL, MongoDB).

Использование реляционной базы данных или MongoDB позволяет выполнять некоторые агрегации непосредственно в запросах к базе данных (которые оптимизированы для работы с большими объемами данных и эффективно используют кэш и индексы).

Позже вы можете выполнить постобработку с использованием pandas.

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

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

0

Я хотел бы обратить внимание на пакет Vaex.

Vaex — это библиотека Python для "ленивых" Out-of-Core DataFrame (аналогично Pandas), которая позволяет визуализировать и исследовать большие табличные наборы данных. Она может вычислять статистики, такие как среднее, сумма, количество, стандартное отклонение и т.д. на N-мерной сетке с производительностью до миллиарда (10^9) объектов/строк в секунду. Визуализация осуществляется с помощью гистограмм, плотностей и 3D-объемного рендеринга, что позволяет интерактивно исследовать большие данные. Vaex использует отображение памяти, политику нулевого копирования и ленивые вычисления для достижения наилучшей производительности (без desperdicio памяти).

Посмотрите документацию: https://vaex.readthedocs.io/en/latest/ API очень похож на API Pandas.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь