Преобразователи данных

Перед передачей спецификации Vega-Lite или Vega средству отображения обычно нужна подготовка данных:

  • DataFrame pandas нужно привести к допустимому виду и сериализовать в JSON.
  • Может потребоваться выборка строк или ограничение их максимального числа.
  • Для повышения производительности DataFrame может записываться в отдельный файл .csv или .json.

Этими операциями управляет API преобразователей данных Altair.

Преобразователь данных — функция Python, принимающая словарь данных Vega-Lite dict или DataFrame pandas и возвращающая преобразованное значение одного из этих типов:

from typing import Union
Data = Union[dict, pd.DataFrame]

def data_transformer(data: Data) -> Data:
    # Transform and return the data
    return transformed_data

Объединение одинаковых наборов данных

DataFrame pandas может быть представлен в диаграмме двумя способами:

  • Непосредственными значениями в атрибуте data на любом уровне спецификации.
  • Именованным набором в атрибуте datasets верхнего уровня.

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

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

Поведение можно изменить атрибутом consolidate_datasets преобразователя данных. Рассмотрим простую диаграмму со слоями:

import altair as alt
import pandas as pd

df = pd.DataFrame({'x': range(5),
                   'y': [1, 3, 4, 3, 5]})

line = alt.Chart(df).mark_line().encode(x='x', y='y')
points = alt.Chart(df).mark_point().encode(x='x', y='y')
chart = line + points

Несмотря на двукратное указание набора, итоговая спецификация содержит одну его копию:

from pprint import pprint
pprint(chart.to_dict())

Объединение одинаковых наборов — дополнительная обработка, по умолчанию включённая во всех средствах отображения.

Если её нужно отключить, задайте alt.data_transformers.consolidate_datasets = False или временно используйте контекстный менеджер enable():

with alt.data_transformers.enable(consolidate_datasets=False):
    pprint(chart.to_dict())

Теперь данные находятся не в datasets верхнего уровня, а в data каждого слоя. Именно из-за такого дублирования объединение по умолчанию включено — True.

Встроенные преобразователи данных

Altair предоставляет стандартный набор преобразователей со следующими сигнатурами.

Вызвать MaxRowsError, если DataFrame содержит больше max_rows строк:

limit_rows(data, max_rows=5000)

Получить случайную выборку из DataFrame без возвращения перед визуализацией:

sample(data, n=None, frac=None)

Преобразовать DataFrame в отдельный .json-файл:

to_json(data, prefix='altair-data'):

Преобразовать DataFrame в отдельный .csv-файл:

to_csv(data, prefix='altair-data'):

Преобразовать DataFrame в значения JSON, встроенные в спецификацию:

to_values(data):

Объединение в цепочку

Несколько преобразователей можно объединять через pipe:

from altair import limit_rows, to_values
from toolz.curried import pipe
pipe(data, limit_rows(10000), to_values)

Управление преобразователями

Altair хранит реестр преобразователей данных, включая стандартный, который автоматически применяется ко всем DataFrame перед отрисовкой.

Посмотреть зарегистрированные преобразователи:

>>> import altair as alt
>>> alt.data_transformers.names()
['default', 'json', 'csv']

Стандартный преобразователь устроен так:

def default_data_transformer(data):
    return pipe(data, limit_rows, to_values)

Преобразователи json и csv перед отрисовкой сохраняют DataFrame во временный файл .json или .csv. Их преимущества:

  • Полный набор не сохраняется в документе notebook.
  • Vega-Lite/Vega в JavaScript, по наблюдениям авторов, быстрее работает с отдельными JSON/CSV-файлами, чем со встроенными значениями.

Недостатки:

  • DataFrame экспортируется во временный файл рядом с notebook.
  • Без этого файла или повторного выполнения ячейки notebook не сможет заново отрисовать диаграмму.

По опыту авторов, ускорение достаточно существенно, чтобы рекомендовать json для больших наборов:

alt.data_transformers.enable('json')

Авторы надеются на появление дополнительных преобразователей. Например, преобразователь, сохраняющий JSON в S3, можно зарегистрировать и включить так:

alt.data_transformers.register('s3', lambda data: pipe(data, to_s3('mybucket')))
alt.data_transformers.enable('s3')

Хранение JSON в отдельном каталоге

При создании множества диаграмм с alt.data_transformers.enable('json') рабочий каталог может переполниться файлами. Чтобы избежать этого, создадим простой преобразователь, сохраняющий JSON в отдельный каталог:

import os
import altair as alt
from toolz.curried import pipe


def json_dir(data, data_dir='altairdata'):
    os.makedirs(data_dir, exist_ok=True)
    return pipe(data, alt.to_json(filename=data_dir + '/{prefix}-{hash}.{extension}') )


alt.data_transformers.register('json_dir', json_dir)
alt.data_transformers.enable('json_dir', data_dir='mydata')

После включения файлы записываются в каталог, указанный через data_dir, либо в altairdata по умолчанию. Достаточно добавить нужный каталог перед именем файла в аргументе filename функции alt.to_json и убедиться, что каталог существует.