| """ |
| Magic functions for rendering vega-lite specifications |
| """ |
| __all__ = ["vegalite"] |
|
|
| import json |
| import warnings |
|
|
| import IPython |
| from IPython.core import magic_arguments |
| import pandas as pd |
| from toolz import curried |
|
|
| from altair.vegalite import v5 as vegalite_v5 |
|
|
| try: |
| import yaml |
|
|
| YAML_AVAILABLE = True |
| except ImportError: |
| YAML_AVAILABLE = False |
|
|
|
|
| RENDERERS = { |
| "vega-lite": { |
| "5": vegalite_v5.VegaLite, |
| }, |
| } |
|
|
|
|
| TRANSFORMERS = { |
| "vega-lite": { |
| "5": vegalite_v5.data_transformers, |
| }, |
| } |
|
|
|
|
| def _prepare_data(data, data_transformers): |
| """Convert input data to data for use within schema""" |
| if data is None or isinstance(data, dict): |
| return data |
| elif isinstance(data, pd.DataFrame): |
| return curried.pipe(data, data_transformers.get()) |
| elif isinstance(data, str): |
| return {"url": data} |
| else: |
| warnings.warn("data of type {} not recognized".format(type(data)), stacklevel=1) |
| return data |
|
|
|
|
| def _get_variable(name): |
| """Get a variable from the notebook namespace.""" |
| ip = IPython.get_ipython() |
| if ip is None: |
| raise ValueError( |
| "Magic command must be run within an IPython " |
| "environment, in which get_ipython() is defined." |
| ) |
| if name not in ip.user_ns: |
| raise NameError( |
| "argument '{}' does not match the name of any defined variable".format(name) |
| ) |
| return ip.user_ns[name] |
|
|
|
|
| @magic_arguments.magic_arguments() |
| @magic_arguments.argument( |
| "data", |
| nargs="?", |
| help="local variablename of a pandas DataFrame to be used as the dataset", |
| ) |
| @magic_arguments.argument("-v", "--version", dest="version", default="v5") |
| @magic_arguments.argument("-j", "--json", dest="json", action="store_true") |
| def vegalite(line, cell): |
| """Cell magic for displaying vega-lite visualizations in CoLab. |
| |
| %%vegalite [dataframe] [--json] [--version='v5'] |
| |
| Visualize the contents of the cell using Vega-Lite, optionally |
| specifying a pandas DataFrame object to be used as the dataset. |
| |
| if --json is passed, then input is parsed as json rather than yaml. |
| """ |
| args = magic_arguments.parse_argstring(vegalite, line) |
| existing_versions = {"v5": "5"} |
| version = existing_versions[args.version] |
| assert version in RENDERERS["vega-lite"] |
| VegaLite = RENDERERS["vega-lite"][version] |
| data_transformers = TRANSFORMERS["vega-lite"][version] |
|
|
| if args.json: |
| spec = json.loads(cell) |
| elif not YAML_AVAILABLE: |
| try: |
| spec = json.loads(cell) |
| except json.JSONDecodeError as err: |
| raise ValueError( |
| "%%vegalite: spec is not valid JSON. " |
| "Install pyyaml to parse spec as yaml" |
| ) from err |
| else: |
| spec = yaml.load(cell, Loader=yaml.SafeLoader) |
|
|
| if args.data is not None: |
| data = _get_variable(args.data) |
| spec["data"] = _prepare_data(data, data_transformers) |
|
|
| return VegaLite(spec) |
|
|