Вложенные массивы в dask и pandas - PullRequest
10 голосов
/ 23 марта 2019

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

Например, в моем конкретном случае использования распознавания речи, наборы данных почти всегда состоят из записей со свойствами, такими как:

  • Идентификатор динамика (строка)
  • Стенограмма (строка)
  • Данные испытаний (бул)
  • WAV-данные (массив numpy)
  • Имя набора данных (строка)
  • ...

Каков рекомендуемый способ представления такого набора данных в pandas и / или dask - акцент на wav-данных (в наборе данных изображения это будут сами данные изображения)?

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

Можно также сериализовать / десериализовать эти массивы в двоичный формат (Petastorm Uber делает что-то вроде этого ), но это, кажется, упускает точку библиотек, таких как dask и pandas, где автоматическая сериализация является одним из основных выгоды.

Любые практические комментарии или предложения по различным методологиям приветствуются.

Ответы [ 2 ]

1 голос
/ 17 апреля 2019

Ваша организация данных действительно очень похожа на xarray : многомерные данные с регулярными координатами по каждому из измерений и свойствами переменных.xarray позволяет вам работать с вашим массивом в стиле панды (документы очень подробные, поэтому я не буду вдаваться в подробности).Следует отметить, что xarray взаимодействует напрямую с Dask, так что, работая с высокоуровневой структурой данных, вы фактически манипулируете массивами dask, расположенными снизу, и поэтому можете вычислять вне ядра и / или распределяться.

ХотяВдохновленный иерархическим представлением данных netCDF (обычно хранящимся в виде файлов HDF5), вы можете использовать ряд возможных вариантов хранения, включая zarr , что особенно полезно в качестве облачного формата для параллельного доступа в форме Dask.хотел бы использовать.

0 голосов
/ 16 апреля 2019

Один (возможно, некрасивый) способ - это исправление API-интерфейса pandas и dask parquet для поддержки многомерных массивов:

# these monkey-patches into the pandas and dask I/O API allow us to save multi-dimensional numpy
# arrays# in parquet format by serializing them into byte arrays

from dask import dataframe as dd
import pandas as pd
from io import BytesIO

def _patched_pd_read_parquet(*args, **kwargs):
    return _orig_pd_read_parquet(*args, **kwargs).applymap(
        lambda val: np.load(BytesIO(val)) if isinstance(val, bytes) else val)
_orig_pd_read_parquet = pd.io.parquet.PyArrowImpl.read
pd.io.parquet.PyArrowImpl.read = _patched_pd_read_parquet

def _serialize_ndarray(arr: np.ndarray) -> bytes:
    if isinstance(arr, np.ndarray):
        with BytesIO() as buf:
            np.save(buf, arr)
            return buf.getvalue()
    return arr

def _deserialize_ndarray(val: bytes) -> np.ndarray:
    return np.load(BytesIO(val)) if isinstance(val, bytes) else val

def _patched_pd_write_parquet(self, df: pd.DataFrame, *args, **kwargs):
    return _orig_pd_write_parquet(self, df.applymap(_serialize_ndarray), *args, **kwargs)
_orig_pd_write_parquet = pd.io.parquet.PyArrowImpl.write
pd.io.parquet.PyArrowImpl.write = _patched_pd_write_parquet

def _patched_dask_read_pyarrow_parquet_piece(*args, **kwargs):
    return _orig_dask_read_pyarrow_parquet_piece(*args, **kwargs).applymap(_deserialize_ndarray)
_orig_dask_read_pyarrow_parquet_piece = dd.io.parquet._read_pyarrow_parquet_piece
dd.io.parquet._read_pyarrow_parquet_piece = _patched_dask_read_pyarrow_parquet_piece

def _patched_dd_write_partition_pyarrow(df: pd.DataFrame, *args, **kwargs):
    return _orig_dd_write_partition_pyarrow(df.applymap(_serialize_ndarray), *args, **kwargs)
_orig_dd_write_partition_pyarrow = dd.io.parquet._write_partition_pyarrow
dd.io.parquet._write_partition_pyarrow = _patched_dd_write_partition_pyarrow

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

...