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

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

Моя первая мысль - использовать HDFStore для хранения больших наборов данных на диске и извлекать только те фрагменты, которые мне нужны, в рамки данных для анализа. Другие упоминали MongoDB как более простую альтернативу. У меня вопрос такой:

Каковы лучшие рабочие процессы для достижения следующих целей:

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

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

Редактировать - пример того, как я хотел бы, чтобы это работало:

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

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

Изменить - Отвечая на вопросы Джеффа конкретно:

  1. Я строю модели потребительского кредитного риска. Типы данных включают телефон, SSN и адресные характеристики; ценности имущества; уничижительная информация, такая как судимости, банкротства и т. д. Наборы данных, которые я использую каждый день, содержат в среднем от 1000 до 2000 полей смешанных типов данных: непрерывные, номинальные и порядковые переменные числовых и символьных данных. Я редко добавляю строки, но я выполняю много операций, которые создают новые столбцы.
  2. Типичные операции включают объединение нескольких столбцов с использованием условной логики в новый составной столбец. Например, if var1 > 2 then newvar = 'A' elif var2 = 4 then newvar = 'B'. Результатом этих операций является новый столбец для каждой записи в моем наборе данных.
  3. Наконец, я хотел бы добавить эти новые столбцы в структуру данных на диске. Я бы повторил шаг 2, изучая данные с помощью кросс-таблиц и описательной статистики, пытаясь найти интересные, интуитивно понятные связи для моделирования.
  4. Типичный файл проекта обычно составляет около 1 ГБ. Файлы организованы таким образом, что строка состоит из записи данных потребителя. Каждая строка имеет одинаковое количество столбцов для каждой записи. Это всегда будет так.
  5. Довольно редко, когда я создаю новый столбец, я добавляю его по строкам. Тем не менее, для меня довольно распространено подмножество строк при создании отчетов или создании описательной статистики. Например, я мог бы хотеть создать простую частоту для определенной сферы бизнеса, скажем, Розничные кредитные карты. Для этого я бы выбрал только те записи, где бизнес = розничная торговля, в дополнение к тем столбцам, о которых я хочу сообщить. Однако при создании новых столбцов я извлекаю все строки данных и только те столбцы, которые мне нужны для операций.
  6. Процесс моделирования требует, чтобы я анализировал каждый столбец, искал интересные отношения с некоторой переменной результата и создавал новые составные столбцы, которые описывают эти отношения. Столбцы, которые я исследую, обычно делаются небольшими наборами. Например, я сконцентрируюсь на наборе, скажем, 20 столбцов, которые имеют дело только со значениями собственности, и посмотрю, как они относятся к дефолту по кредиту. После их изучения и создания новых столбцов я перехожу к другой группе столбцов, например, «Образование в колледже», и повторяю процесс. Я занимаюсь созданием переменных-кандидатов, которые объясняют связь между моими данными и некоторым результатом. В самом конце этого процесса я применяю некоторые методы обучения, которые создают уравнение из этих составных столбцов.

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

948
Zelazny7 10 Янв 2013 в 20:20

13 ответов

Подумайте, Ruffus, если вы идете простым путем создания конвейера данных, который разбит на несколько небольших файлов.

10
Golf Monkey 9 Окт 2014 в 19:07

Я хотел бы указать на пакет Vaex.

Vaex - это библиотека Python для ленивых внешних фреймов данных (похожих на Pandas), для визуализации и изучения больших табличных наборов данных. Он может вычислять статистику, такую как среднее значение, сумма, число, стандартное отклонение и т. Д., В N-мерной сетке до миллиарда (10 9 ) объектов / строк в секунду. Визуализация выполняется с использованием гистограмм, графиков плотности и трехмерного рендеринга, что позволяет интерактивно исследовать большие данные. Vaex использует отображение памяти, нулевую политику копирования памяти и ленивые вычисления для лучшей производительности (без потери памяти).

Ознакомьтесь с документацией: https://vaex.readthedocs.io/en/latest/ API очень близок к API панд.

6
Rob 3 Июн 2019 в 09:40

Еще один вариант

Многие из операций, выполняемых в пандах, также могут выполняться в виде запросов к базе данных (sql, mongo).

Использование СУБД или mongodb позволяет выполнять некоторые агрегации в запросе к БД (который оптимизирован для больших данных и эффективно использует кэш и индексы)

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

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

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

15
Ophir Yoktan 28 Апр 2015 в 05:22

Как отмечали другие, через несколько лет появился «не имеющий ядра» эквивалент панд: dask . Несмотря на то, что dask не является заменой панд и их функциональности, он выделяется по нескольким причинам:

Dask - это гибкая библиотека параллельных вычислений для аналитических вычислений, оптимизированная для динамического планирования задач для интерактивных вычислительных рабочих нагрузок коллекций «больших данных», таких как параллельные массивы, фреймы данных и списки, которые расширяют общие интерфейсы, такие как NumPy, Pandas или Python, итераторами для увеличения чем память или распределенные среды и масштабируется от ноутбуков до кластеров.

Dask подчеркивает следующие достоинства:

  • Знакомый: Предоставляет распараллеленный массив NumPy и объекты Pandas DataFrame.
  • Гибкость: предоставляет интерфейс планирования задач для большего количества пользовательских рабочих нагрузок и интеграции с другими проектами.
  • Собственный: Включает распределенные вычисления в Pure Python с доступом к стеку PyData.
  • Быстро: работает с низкими издержками, низкой задержкой и минимальной сериализацией, необходимой для быстрых численных алгоритмов
  • Масштабируется: гибко работает на кластерах с тысячами ядер. Масштабируется: тривиально настроить и запустить на ноутбуке за один процесс
  • Отзывчивый: разработанный с учетом интерактивных вычислений, он обеспечивает быструю обратную связь и диагностику, чтобы помочь людям

И добавить простой пример кода:

import dask.dataframe as dd
df = dd.read_csv('2015-*-*.csv')
df.groupby(df.user_id).value.mean().compute()

Заменяет некоторый код панд следующим образом:

import pandas as pd
df = pd.read_csv('2015-01-01.csv')
df.groupby(df.user_id).value.mean()

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

from dask.distributed import Client
client = Client('scheduler:port')

futures = []
for fn in filenames:
    future = client.submit(load, fn)
    futures.append(future)

summary = client.submit(summarize, futures)
summary.result()
24
wp78de 8 Янв 2020 в 17:07

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

Просто замените импорт панд, и код должен работать как есть:

# import pandas as pd
import ray.dataframe as pd

#use pd as usual

Можете прочитать подробнее здесь:

https://rise.cs.berkeley.edu/blog/pandas-on-ray/

14
lev 18 Мар 2018 в 09:30

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

Из документов .

Blaze расширяет возможности использования NumPy и Pandas для распределенных и неосновных вычислений. Blaze предоставляет интерфейс, похожий на интерфейс Num-Array NumPy или DataFrame Pandas, но отображает эти знакомые интерфейсы на множество других вычислительных движков, таких как Postgres или Spark.

Редактировать . Кстати, его поддерживают ContinuumIO и Тревис Олифант, автор NumPy.

54
chishaku 3 Дек 2014 в 22:09

Недавно я столкнулся с похожей проблемой. Я обнаружил, что простое чтение данных кусками и добавление их при записи в один и тот же CSV-файл работает хорошо. Моя проблема заключалась в добавлении столбца даты на основе информации в другой таблице, используя значения определенных столбцов следующим образом. Это может помочь тем, кто смущен dask и hdf5, но лучше знаком с такими пандами, как я.

def addDateColumn():
"""Adds time to the daily rainfall data. Reads the csv as chunks of 100k 
   rows at a time and outputs them, appending as needed, to a single csv. 
   Uses the column of the raster names to get the date.
"""
    df = pd.read_csv(pathlist[1]+"CHIRPS_tanz.csv", iterator=True, 
                     chunksize=100000) #read csv file as 100k chunks

    '''Do some stuff'''

    count = 1 #for indexing item in time list 
    for chunk in df: #for each 100k rows
        newtime = [] #empty list to append repeating times for different rows
        toiterate = chunk[chunk.columns[2]] #ID of raster nums to base time
        while count <= toiterate.max():
            for i in toiterate: 
                if i ==count:
                    newtime.append(newyears[count])
            count+=1
        print "Finished", str(chunknum), "chunks"
        chunk["time"] = newtime #create new column in dataframe based on time
        outname = "CHIRPS_tanz_time2.csv"
        #append each output to same csv, using no header
        chunk.to_csv(pathlist[2]+outname, mode='a', header=None, index=None)
8
timpjohns 4 Окт 2016 в 15:32

Один прием, который я нашел полезным для случаев использования больших данных , - это уменьшить объем данных за счет снижения точности с плавающей запятой до 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
33
AkshayNevrekar 3 Ноя 2018 в 06:51

Если ваши наборы данных имеют размер от 1 до 20 ГБ, вы должны получить рабочую станцию с 48 ГБ ОЗУ. Тогда Pandas может хранить весь набор данных в оперативной памяти. Я знаю, что это не тот ответ, который вы ищете здесь, но делать научные вычисления на ноутбуке с 4 ГБ ОЗУ нецелесообразно.

61
rjurney 2 Ноя 2013 в 07:14

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

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

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

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

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

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

132
user1827356 23 Дек 2013 в 15:21

В данный момент я работаю «как вы», просто по более низкой шкале, поэтому у меня нет PoC для моего предложения.

Тем не менее, я, кажется, добился успеха в использовании pickle в качестве системы кеширования и аутсорсинга выполнения различных функций в файлы - выполнение этих файлов из моего файла commando / main; Например, я использую prepare_use.py для преобразования типов объектов, разделения набора данных на набор данных тестирования, проверки и прогнозирования.

Как работает твое кеширование с рассолом? Я использую строки для доступа к файлам pickle, которые создаются динамически, в зависимости от того, какие параметры и наборы данных были переданы (с этим я пытаюсь захватить и определить, была ли программа уже запущена, используя .shape для набора данных, dict для переданного параметры) . Соблюдая эти меры, я получаю строку, чтобы попытаться найти и прочитать файл .pickle, и могу, если найден, пропустить время обработки, чтобы перейти к выполнению, над которым я сейчас работаю.

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

Я надеюсь, что мои 2 цента помогут вам в некотором роде.

Приветствую.

-1
TiRoX 8 Янв 2020 в 14:05

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

Мой основной рабочий процесс - сначала получить файл CSV из базы данных. Я gzip это, так что это не так огромно. Затем я преобразую это в файл HDF5 с ориентацией на строки, перебирая его в python, преобразуя каждую строку в реальный тип данных и записывая его в файл HDF5. Это занимает несколько десятков минут, но не использует никакой памяти, поскольку работает только построчно. Затем я «транспонирую» ориентированный на строки файл HDF5 в ориентированный на столбцы файл HDF5.

Таблица транспонирования выглядит так:

def transpose_table(h_in, table_path, h_out, group_name="data", group_path="/"):
    # Get a reference to the input data.
    tb = h_in.getNode(table_path)
    # Create the output group to hold the columns.
    grp = h_out.createGroup(group_path, group_name, filters=tables.Filters(complevel=1))
    for col_name in tb.colnames:
        logger.debug("Processing %s", col_name)
        # Get the data.
        col_data = tb.col(col_name)
        # Create the output array.
        arr = h_out.createCArray(grp,
                                 col_name,
                                 tables.Atom.from_dtype(col_data.dtype),
                                 col_data.shape)
        # Store the data.
        arr[:] = col_data
    h_out.flush()

Чтение обратно тогда выглядит так:

def read_hdf5(hdf5_path, group_path="/data", columns=None):
    """Read a transposed data set from a HDF5 file."""
    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]

    # Convert any float32 columns to float64 for processing.
    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.

Edit: реальное преимущество этого подхода, по сравнению с Pytables по умолчанию для массива записей, заключается в том, что я могу затем загрузить данные в R, используя h5r, который не может обрабатывать таблицы. Или, по крайней мере, я не смог заставить его загружать разнородные таблицы.

40
Johann Hibschman 22 Мар 2013 в 15:38
14262433