From 4ea480ea1e0139088dbf733effa99baa0d2891d6 Mon Sep 17 00:00:00 2001 From: Mattijn van Hoek Date: Tue, 9 May 2023 16:59:19 +0200 Subject: [PATCH 01/12] MAINT: bump version to 5.1.0dev --- altair/__init__.py | 2 +- doc/conf.py | 4 ++-- doc/releases/changes.rst | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/altair/__init__.py b/altair/__init__.py index af21e7afa..ca4ee1af1 100644 --- a/altair/__init__.py +++ b/altair/__init__.py @@ -1,5 +1,5 @@ # ruff: noqa -__version__ = "5.0.0" +__version__ = "5.1.0dev" from typing import Any diff --git a/doc/conf.py b/doc/conf.py index a82a8c5ff..e6155c5e1 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -76,9 +76,9 @@ # built documents. # # The short X.Y version. -version = "5.0.0" +version = "5.1.0" # The full version, including alpha/beta/rc tags. -release = f"{version}" +release = f"{version}dev" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/releases/changes.rst b/doc/releases/changes.rst index d30b28110..db8885b06 100644 --- a/doc/releases/changes.rst +++ b/doc/releases/changes.rst @@ -3,8 +3,20 @@ Release Notes ============= -Version 5.0.0 -------------- +Version 5.1.0 (unreleased) +-------------------------- + +Enhancements +~~~~~~~~~~~~ + +Bug Fixes +~~~~~~~~~ + +Backward-Incompatible Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Version 5.0.0 (released May 9, 2023) +------------------------------------ - Update Vega-Lite from version 4.17.0 to version 5.8.0; see `Vega-Lite Release Notes `_. From f3ced05897cb1e17f7299086436c0a08348c7028 Mon Sep 17 00:00:00 2001 From: Joel Ostblom Date: Thu, 11 May 2023 15:15:14 -0700 Subject: [PATCH 02/12] Be clearer about how vegafusion works (#3052) --- doc/user_guide/large_datasets.rst | 33 ++++++++++++++++++------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/doc/user_guide/large_datasets.rst b/doc/user_guide/large_datasets.rst index 0269be6a0..b5ee0a232 100644 --- a/doc/user_guide/large_datasets.rst +++ b/doc/user_guide/large_datasets.rst @@ -12,7 +12,7 @@ If you try to create a plot that will directly embed a dataset with more than import pandas as pd data = pd.DataFrame({"x": range(10000)}) - alt.Chart(data).mark_line() + alt.Chart(data).mark_point() .. code-block:: none @@ -336,15 +336,15 @@ summary statistics to Altair instead of the full dataset. VegaFusion ~~~~~~~~~~ `VegaFusion`_ is a third-party package that re-implements most Vega-Lite transforms for evaluation -in the Python kernel. This makes it possible to scale many Altair charts to millions of rows as long as -they include some form of aggregation. - -VegaFusion 1.0 provides two rendering modes that are useful in different situations. +in the Python kernel, which makes it possible to scale Altair charts to millions of rows of data. +VegaFusion provides two rendering modes that are useful in different situations. Mime Renderer ^^^^^^^^^^^^^ -The `VegaFusion mime renderer`_ is a good choice for charts that do not re-aggregate or re-filter data in response -to selections. It is enabled with: +The `VegaFusion mime renderer`_ is a good choice for charts that contain aggregations +and that do not re-aggregate or re-filter data in response to selections +(so it offers similar but more advanced functionality as the ``altair-transform`` package). +It is enabled with: .. code-block:: python @@ -357,23 +357,28 @@ Python kernel. It then removes any unused columns and inlines the transformed da for rendering. Charts rendered this way are self-contained and do not require the Python kernel or a custom -notebook extension to display. They are rendered with the same frontend functionality that +notebook extension to display. They are rendered with the same frontend functionality that is already used to display regular Altair charts. Widget Renderer ^^^^^^^^^^^^^^^ -The `VegaFusion widget renderer`_ is a good choice for charts that re-aggregate or re-filter data in response -to selections. It is enabled with: +The `VegaFusion widget renderer`_ is a good choice for displaying unaggregated data +and for aggregated charts that re-aggregate or re-filter data in response to selections +(so it offers similar but more advanced functionality as the ``altair-data-server`` package). +It is enabled with: .. code-block:: python import vegafusion as vf vf.enable_widget() -The widget renderer uses a Jupyter Widget extension to maintain a live connection between the displayed -chart and the Python kernel. This makes it possible for transforms to be evaluated interactively in response to -changes in selections. Charts rendered this way require a running Python kernel and Jupyter Widget extension to -display. +The widget renderer uses a Jupyter Widget extension to maintain a live connection between the displayed chart and the Python kernel. +This makes it possible for transforms to be evaluated interactively in response to changes in selections, +and to send the datasets to the client in arrow format separately instead of inlining them in the chart json spec. + +Charts rendered this way require a running Python kernel and Jupyter Widget extension to +display, which works in many frontends including locally in the classic notebook, JupyterLab, and VSCode, +as well as remotely in Colab and Binder. .. _VegaFusion: https://vegafusion.io .. _VegaFusion mime renderer: https://vegafusion.io/mime_renderer.html From 5571f39904cba4acac7c4d248788cd95e8a44a8b Mon Sep 17 00:00:00 2001 From: Stefan Binder Date: Sat, 13 May 2023 21:04:12 +0200 Subject: [PATCH 03/12] Use altairplot Sphinx directive of sphinxext_altair package (#3056) * Use altairplot Sphinx directive of sphinxext_altair package * Use sphinxext_altair package from pypi * Add note about sphinxext-altair package in release 5 --- doc/conf.py | 4 +- doc/releases/changes.rst | 1 + pyproject.toml | 1 + sphinxext/altairplot.py | 370 --------------------------------------- 4 files changed, 4 insertions(+), 372 deletions(-) delete mode 100644 sphinxext/altairplot.py diff --git a/doc/conf.py b/doc/conf.py index e6155c5e1..ff468aa88 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -12,8 +12,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys import os +import sys from datetime import datetime # If extensions (or modules to document with autodoc) are in another directory, @@ -36,7 +36,7 @@ "sphinx.ext.coverage", "sphinx.ext.githubpages", "numpydoc.numpydoc", - "sphinxext.altairplot", + "sphinxext_altair.altairplot", "sphinxext.altairgallery", "sphinxext.schematable", "sphinx_copybutton", diff --git a/doc/releases/changes.rst b/doc/releases/changes.rst index db8885b06..0c5176836 100644 --- a/doc/releases/changes.rst +++ b/doc/releases/changes.rst @@ -64,6 +64,7 @@ Backward-Incompatible Changes - Removed the Vega-Lite 3 and 4 wrappers (#2847). - Removed the deprecated datasets.py (#3010). - In regards to the grammar changes listed above, the old terminology will still work in many basic cases. On the other hand, if that old terminology gets used at a lower level, then it most likely will not work. For example, in the current version of :ref:`gallery_scatter_with_minimap`, two instances of the key ``param`` are used in dictionaries to specify axis domains. Those used to be ``selection``, but that usage is not compatible with the current Vega-Lite schema. +- Removed the ``altair.sphinxext`` module (#2792). The ``altair-plot`` Sphinx directive is now part of the `sphinxext-altair `_ package. Maintenance ~~~~~~~~~~~ diff --git a/pyproject.toml b/pyproject.toml index 571d63a59..180683381 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,7 @@ dev = [ doc = [ "sphinx", "docutils", + "sphinxext_altair", "jinja2", "numpydoc", "pillow", diff --git a/sphinxext/altairplot.py b/sphinxext/altairplot.py deleted file mode 100644 index 6c4faa359..000000000 --- a/sphinxext/altairplot.py +++ /dev/null @@ -1,370 +0,0 @@ -""" -Altair Plot Sphinx Extension -============================ - -This extension provides a means of inserting live-rendered Altair plots within -sphinx documentation. There are two directives defined: ``altair-setup`` and -``altair-plot``. ``altair-setup`` code is used to set-up various options -prior to running the plot code. For example:: - - .. altair-plot:: - :output: none - - from altair import * - import pandas as pd - data = pd.DataFrame({'a': list('CCCDDDEEE'), - 'b': [2, 7, 4, 1, 2, 6, 8, 4, 7]}) - - .. altair-plot:: - - Chart(data).mark_point().encode( - x='a', - y='b' - ) - -In the case of the ``altair-plot`` code, the *last statement* of the code-block -should contain the chart object you wish to be rendered. - -Options -------- -The directives have the following options:: - - .. altair-plot:: - :namespace: # specify a plotting namespace that is persistent within the doc - :hide-code: # if set, then hide the code and only show the plot - :remove-code: # if set, then remove the code and only show the plot - :code-below: # if set, then code is below rather than above the figure - :output: [plot|repr|stdout|none] - :alt: text # Alternate text when plot cannot be rendered - :links: editor source export # specify one or more of these options - :chart-var-name: chart # name of variable in namespace containing output - :strict: # if set, then code with errors will raise instead of being skipped - - -Additionally, this extension introduces a global configuration -``altairplot_links``, set in your ``conf.py`` which is a dictionary -of links that will appear below plots, unless the ``:links:`` option -again overrides it. It should look something like this:: - - # conf.py - # ... - altairplot_links = {'editor': True, 'source': True, 'export': True} - # ... - -If this configuration is not specified, all are set to True. -""" - -import contextlib -import io -import os -import json -import warnings - -import jinja2 - -from docutils import nodes -from docutils.parsers.rst import Directive -from docutils.parsers.rst.directives import flag, unchanged - -from sphinx.locale import _ - -import altair as alt -from altair.utils.execeval import eval_block - -# These default URLs can be changed in conf.py; see setup() below. -VEGA_JS_URL_DEFAULT = "https://cdn.jsdelivr.net/npm/vega@{}".format(alt.VEGA_VERSION) -VEGALITE_JS_URL_DEFAULT = "https://cdn.jsdelivr.net/npm/vega-lite@{}".format( - alt.VEGALITE_VERSION -) -VEGAEMBED_JS_URL_DEFAULT = "https://cdn.jsdelivr.net/npm/vega-embed@{}".format( - alt.VEGAEMBED_VERSION -) - - -VGL_TEMPLATE = jinja2.Template( - """ -
- -
-""" -) - - -class altair_plot(nodes.General, nodes.Element): - pass - - -def purge_altair_namespaces(app, env, docname): - if not hasattr(env, "_altair_namespaces"): - return - env._altair_namespaces.pop(docname, {}) - - -DEFAULT_ALTAIRPLOT_LINKS = {"editor": True, "source": True, "export": True} - - -def validate_links(links): - if links.strip().lower() == "none": - return False - - links = links.strip().split() - diff = set(links) - set(DEFAULT_ALTAIRPLOT_LINKS.keys()) - if diff: - raise ValueError("Following links are invalid: {}".format(list(diff))) - return {link: link in links for link in DEFAULT_ALTAIRPLOT_LINKS} - - -def validate_output(output): - output = output.strip().lower() - if output not in ["plot", "repr", "stdout", "none"]: - raise ValueError(":output: flag must be one of [plot|repr|stdout|none]") - return output - - -def validate_div_class(output): - return output.strip().lower() - - -class AltairPlotDirective(Directive): - has_content = True - - option_spec = { - "hide-code": flag, - "remove-code": flag, - "code-below": flag, - "namespace": unchanged, - "output": validate_output, - "alt": unchanged, - "links": validate_links, - "chart-var-name": unchanged, - "strict": flag, - "div_class": validate_div_class, - } - - def run(self): - env = self.state.document.settings.env - app = env.app - - hide_code = "hide-code" in self.options - remove_code = "remove-code" in self.options - code_below = "code-below" in self.options - strict = "strict" in self.options - div_class = self.options.get("div_class", None) - - if not hasattr(env, "_altair_namespaces"): - env._altair_namespaces = {} - namespace_id = self.options.get("namespace", "default") - namespace = env._altair_namespaces.setdefault(env.docname, {}).setdefault( - namespace_id, {} - ) - - code = "\n".join(self.content) - - # Show code - source_literal = nodes.literal_block(code, code) - source_literal["language"] = "python" - - # get the name of the source file we are currently processing - rst_source = self.state_machine.document["source"] - rst_dir = os.path.dirname(rst_source) - rst_filename = os.path.basename(rst_source) - - # use the source file name to construct a friendly target_id - serialno = env.new_serialno("altair-plot") - rst_base = rst_filename.replace(".", "-") - div_id = "{}-altair-plot-{}".format(rst_base, serialno) - target_id = "{}-altair-source-{}".format(rst_base, serialno) - target_node = nodes.target("", "", ids=[target_id]) - - # create the node in which the plot will appear; - # this will be processed by html_visit_altair_plot - plot_node = altair_plot() - plot_node["target_id"] = target_id - plot_node["div_id"] = div_id - plot_node["div_class"] = div_class - plot_node["code"] = code - plot_node["namespace"] = namespace - plot_node["relpath"] = os.path.relpath(rst_dir, env.srcdir) - plot_node["rst_source"] = rst_source - plot_node["rst_lineno"] = self.lineno - plot_node["links"] = self.options.get( - "links", app.builder.config.altairplot_links - ) - plot_node["output"] = self.options.get("output", "plot") - plot_node["chart-var-name"] = self.options.get("chart-var-name", None) - plot_node["strict"] = strict - - if "alt" in self.options: - plot_node["alt"] = self.options["alt"] - - result = [target_node] - - if code_below: - result += [plot_node] - - if hide_code: - html = "
Click to show code" - raw_html = nodes.raw("", html, format="html") - result += [raw_html] - - if not remove_code: - result += [source_literal] - - if hide_code: - html = "
" - raw_html = nodes.raw("", html, format="html") - result += [raw_html] - - if not code_below: - result += [plot_node] - - return result - - -def html_visit_altair_plot(self, node): - # Execute the code, saving output and namespace - namespace = node["namespace"] - try: - f = io.StringIO() - with contextlib.redirect_stdout(f): - chart = eval_block(node["code"], namespace) - stdout = f.getvalue() - except Exception as err: - message = "altair-plot: {}:{} Code Execution failed:" "{}: {}".format( - node["rst_source"], node["rst_lineno"], err.__class__.__name__, str(err) - ) - if node["strict"]: - raise ValueError(message) from err - else: - warnings.warn(message, stacklevel=1) - raise nodes.SkipNode from err - - chart_name = node["chart-var-name"] - if chart_name is not None: - if chart_name not in namespace: - raise ValueError( - "chart-var-name='{}' not present in namespace" "".format(chart_name) - ) - chart = namespace[chart_name] - - output = node["output"] - - if output == "none": - raise nodes.SkipNode - elif output == "stdout": - if not stdout: - raise nodes.SkipNode - else: - output_literal = nodes.literal_block(stdout, stdout) - output_literal["language"] = "none" - node.extend([output_literal]) - elif output == "repr": - if chart is None: - raise nodes.SkipNode - else: - rep = " " + repr(chart).replace("\n", "\n ") - repr_literal = nodes.literal_block(rep, rep) - repr_literal["language"] = "none" - node.extend([repr_literal]) - elif output == "plot": - if isinstance(chart, alt.TopLevelMixin): - # Last line should be a chart; convert to spec dict - try: - spec = chart.to_dict() - except alt.utils.schemapi.SchemaValidationError as err: - raise ValueError("Invalid chart: {0}".format(node["code"])) from err - actions = node["links"] - - # TODO: add an option to save chart specs to file & load from there. - # TODO: add renderer option - - # Write spec to a *.vl.json file - # dest_dir = os.path.join(self.builder.outdir, node['relpath']) - # if not os.path.exists(dest_dir): - # os.makedirs(dest_dir) - # filename = "{0}.vl.json".format(node['target_id']) - # dest_path = os.path.join(dest_dir, filename) - # with open(dest_path, 'w') as f: - # json.dump(spec, f) - - # Pass relevant info into the template and append to the output - html = VGL_TEMPLATE.render( - div_id=node["div_id"], - div_class=node["div_class"], - spec=json.dumps(spec), - mode="vega-lite", - renderer="canvas", - actions=json.dumps(actions), - ) - self.body.append(html) - else: - warnings.warn( - "altair-plot: {}:{} Malformed block. Last line of " - "code block should define a valid altair Chart object." - "".format(node["rst_source"], node["rst_lineno"]), - stacklevel=1, - ) - raise nodes.SkipNode - - -def generic_visit_altair_plot(self, node): - # TODO: generate PNGs and insert them here - if "alt" in node.attributes: - self.body.append(_("[ graph: %s ]") % node["alt"]) - else: - self.body.append(_("[ graph ]")) - raise nodes.SkipNode - - -def depart_altair_plot(self, node): - return - - -def builder_inited(app): - app.add_js_file(app.config.altairplot_vega_js_url) - app.add_js_file(app.config.altairplot_vegalite_js_url) - app.add_js_file(app.config.altairplot_vegaembed_js_url) - - -def setup(app): - setup.app = app - setup.config = app.config - setup.confdir = app.confdir - - app.add_config_value("altairplot_links", DEFAULT_ALTAIRPLOT_LINKS, "env") - - app.add_config_value("altairplot_vega_js_url", VEGA_JS_URL_DEFAULT, "html") - app.add_config_value("altairplot_vegalite_js_url", VEGALITE_JS_URL_DEFAULT, "html") - app.add_config_value( - "altairplot_vegaembed_js_url", VEGAEMBED_JS_URL_DEFAULT, "html" - ) - - app.add_directive("altair-plot", AltairPlotDirective) - - app.add_css_file("altair-plot.css") - - app.add_node( - altair_plot, - html=(html_visit_altair_plot, depart_altair_plot), - latex=(generic_visit_altair_plot, depart_altair_plot), - texinfo=(generic_visit_altair_plot, depart_altair_plot), - text=(generic_visit_altair_plot, depart_altair_plot), - man=(generic_visit_altair_plot, depart_altair_plot), - ) - - app.connect("env-purge-doc", purge_altair_namespaces) - app.connect("builder-inited", builder_inited) - - return {"version": "0.1"} From 79de39e2de179d9c6395d9d050ec51be255f824e Mon Sep 17 00:00:00 2001 From: Stefan Binder Date: Sun, 14 May 2023 11:51:36 +0200 Subject: [PATCH 04/12] Fix test command in README (#3058) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a6a9ced53..0e3ab32d7 100644 --- a/README.md +++ b/README.md @@ -108,9 +108,9 @@ For bugs and feature requests, please open a [Github Issue](https://github.com/a ## Development You can find the instructions on how to install the package for development in [the documentation](https://altair-viz.github.io/getting_started/installation.html). -To run the tests, use +To run the tests and linters, use ``` -pytest --doctest-modules altair +hatch run test ``` For information on how to contribute your developments back to the Vega-Altair repository, see From 62ca5e37776f5cecb27e83c1fbd5d685a173095d Mon Sep 17 00:00:00 2001 From: Jan Tilly Date: Sun, 14 May 2023 15:37:18 +0200 Subject: [PATCH 05/12] Remove extra files in site-packages from wheel (#3057) --- pyproject.toml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 180683381..727690b22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -93,12 +93,7 @@ path = "altair/__init__.py" allow-direct-references = true [tool.hatch.build] -include = [ - "/altair", - "README.md", - "LICENSE", - "pyproject.toml" -] +include = ["/altair"] [tool.hatch.envs.default] features = ["dev"] From 5b1a5d0c9104311cc712fc4850d9c24824cbecc7 Mon Sep 17 00:00:00 2001 From: Stefan Binder Date: Thu, 18 May 2023 21:09:19 +0200 Subject: [PATCH 06/12] Add validation of Vega-Lite schema to build pipeline (#3061) * Add validation of Vega-Lite schema itself * Install check-jsonschema --- .github/workflows/build.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc3f700c4..37995c134 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,3 +65,12 @@ jobs: run: | # pip install vl-convert-python pytest -m save_engine --doctest-modules tests + - name: Validate Vega-Lite schema + run: | + # We install all 'format' dependencies of jsonschema as check-jsonschema + # only does the 'format' checks which are installed. + # We can always use the latest jsonschema version here. + # uri-reference check is disabled as the URIs in the Vega-Lite schema do + # not conform RFC 3986. + pip install 'jsonschema[format]' check-jsonschema --upgrade + check-jsonschema --check-metaschema altair/vegalite/v5/schema/vega-lite-schema.json --disable-formats uri-reference From 5d15325e2b406098a38ec8555697e530ee57a731 Mon Sep 17 00:00:00 2001 From: Mattijn van Hoek Date: Fri, 19 May 2023 13:43:12 +0200 Subject: [PATCH 07/12] Deprecate `.ref()` instead of removing it (#3063) * MAINT: bump version to 5.0.0 * MAINT: bump version to 5.0.0 * deprecate .ref() over removing * update changes.rst * Update doc/releases/changes.rst Co-authored-by: Stefan Binder --------- Co-authored-by: Stefan Binder --- altair/vegalite/v5/api.py | 7 +++++++ doc/conf.py | 2 +- doc/releases/changes.rst | 12 +++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index e31182cdc..e9aa439cb 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -191,6 +191,13 @@ def __init__(self, name): name = self._get_name() self.name = name + @utils.deprecation.deprecated( + message="'ref' is deprecated. No need to call '.ref()' anymore." + ) + def ref(self): + "'ref' is deprecated. No need to call '.ref()' anymore." + return self.to_dict() + def to_dict(self): if self.param_type == "variable": return {"expr": self.name} diff --git a/doc/conf.py b/doc/conf.py index ff468aa88..76776b448 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -78,7 +78,7 @@ # The short X.Y version. version = "5.1.0" # The full version, including alpha/beta/rc tags. -release = f"{version}dev" +release = f"{version}" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/releases/changes.rst b/doc/releases/changes.rst index 0c5176836..3b40db77a 100644 --- a/doc/releases/changes.rst +++ b/doc/releases/changes.rst @@ -3,17 +3,11 @@ Release Notes ============= -Version 5.1.0 (unreleased) +Version 5.0.1 (unreleased) -------------------------- -Enhancements -~~~~~~~~~~~~ - -Bug Fixes -~~~~~~~~~ - -Backward-Incompatible Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- Remove unwanted files during build to avoid littering site-packages folder (#3057) +- Deprecate the `.ref()` function for selections, instead of removing it (#3063). Version 5.0.0 (released May 9, 2023) ------------------------------------ From 76f75d253a121ca66135cec0134bfd6260dafd51 Mon Sep 17 00:00:00 2001 From: Mattijn van Hoek Date: Fri, 19 May 2023 13:43:26 +0200 Subject: [PATCH 08/12] Update area.rst (#3064) Fix https://github.com/altair-viz/altair/issues/3055. This PR changes the description so it is in line with the example connected to it. I think the specification is good as is, so the emphasis is on the `alt.Y2` and not on the `ci0` and `ci1`. --- doc/user_guide/marks/area.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/user_guide/marks/area.rst b/doc/user_guide/marks/area.rst index 5e6defc2b..78c77866b 100644 --- a/doc/user_guide/marks/area.rst +++ b/doc/user_guide/marks/area.rst @@ -174,8 +174,7 @@ Adding the ``interactive`` method allows for zooming and panning the x-scale. Ranged Area ^^^^^^^^^^^ -Specifying ``x2`` or ``y2`` for the quantitative axis of area marks produce ranged areas. For example, we can use ranged area with the ``ci0`` and ``ci0`` -:ref:`aggregation operators ` to highlight 95% confidence interval of a line chart that shows mean values over time. +Specifying ``X2`` or ``Y2`` for the quantitative axis of area marks produce ranged areas. For example, we can use ranged area to highlight the mininium and maximum measured temperatures over time, aggregated by ``monthdate``. .. altair-plot:: import altair as alt From d47f13b118534771c54fffa829a8458793b0448e Mon Sep 17 00:00:00 2001 From: Stefan Binder Date: Tue, 23 May 2023 16:25:25 +0200 Subject: [PATCH 09/12] Documentation: Improve homepage (#3060) * Make start page wider, example gallery on start page bigger, and show 7 examples per row * Remove raw-html role as unclear what it does and can't see an effect when removing it * Style first paragraph as a lead, similar to Vega-Lite docs * Add cards to start page. Move reference to Vega-Lite docs to Overview * Fix link to API section by renaming it to lowercase. Add back raw-html role as it is needed. It registers raw-html as being the raw role with html format for the rest of the documentation * Make 'Vega-Altair' bold. Improve card grid layout for smaller screens * Change api.rst file name also to lowercase in schema generation script * Increase space around lead. From code suggestion * Remove unused css * Extend gallery to whole screen. Make text narrower * Make gallery fill full width of header. Replace and reorder some gallery images so it looks better in this configuration. Replaced the heatmp with text as it looks too 'zomed in' with the text compared to the others --- doc/_static/altair-gallery.css | 22 ++++++++---- doc/_static/custom.css | 23 ++++++++++--- doc/_static/theme_overrides.css | 5 +++ doc/case_studies/exploring-weather.rst | 2 +- doc/getting_started/overview.rst | 2 +- doc/index.rst | 47 ++++++++++++++++++++++---- doc/user_guide/{API.rst => api.rst} | 2 +- tools/generate_api_docs.py | 8 ++--- 8 files changed, 86 insertions(+), 25 deletions(-) rename doc/user_guide/{API.rst => api.rst} (99%) diff --git a/doc/_static/altair-gallery.css b/doc/_static/altair-gallery.css index e8097f9e4..2dfe9e8a4 100644 --- a/doc/_static/altair-gallery.css +++ b/doc/_static/altair-gallery.css @@ -44,11 +44,13 @@ div.bottomnav { /* Front-page Example Showcase */ #showcase { width: 100%; - height: 240px; + height: 300px; overflow: hidden; margin: 0; padding: 0; - position: relative; + position: absolute; + left: 0; + right: 0; margin-bottom: 10px; } #showcase:after, @@ -71,13 +73,21 @@ div.bottomnav { } #showcase .examples { margin: 0 auto; - height: 240px; - width: 800px; + height: 300px; line-height: 0; + /* Width and padding settings give the example showcase the same width + as the page header on a big screen. On smaller screens, they both will + have the same width anyway as the title header is responsive and gets narrower + and the example showcase gets clipped by the overflow: hidden setting. */ + width: 88rem; + padding-left: 1rem; + padding-right: 1rem; } #showcase .preview { - width: 144px; - height: 80px; + /* Value is chosen through trial and error so that the examples fill out the + whole width which is defined in #showcase .examples */ + width: 172px; + height: 100px; padding: 0; outline: 1px solid #ddd; background-position: left top; diff --git a/doc/_static/custom.css b/doc/_static/custom.css index a4e6a7914..f5c32b728 100644 --- a/doc/_static/custom.css +++ b/doc/_static/custom.css @@ -75,8 +75,21 @@ properly displayed on mobile devices and not restricted to 20% */ width: 100%; } -/* Use full page width without sidebars */ -/* .bd-content { */ -/* max-width: 100%; */ -/* flex-grow: 1; */ -/* } */ +/* Configurations for the start page +------------------------------------ */ +.lead { + font-size: 1.3em; + font-weight: 300; + margin-top: 22px; + margin-bottom: 22px; + /* This pushes down the lead so that it is not rendered on top of + the gallery (showcase) which has an absolute position. The value is calculated + as height (showcase) + margin-bottom (showcase) + margin-top (lead) */ + padding-top: 332px; +} + +.lead strong { + /* Default is bolder which is less */ + font-weight: bold; +} +/* ---------------------------------- */ \ No newline at end of file diff --git a/doc/_static/theme_overrides.css b/doc/_static/theme_overrides.css index f1ccfb9be..33d3af378 100644 --- a/doc/_static/theme_overrides.css +++ b/doc/_static/theme_overrides.css @@ -23,3 +23,8 @@ img.logo { width: 120px !important; } + +/* Increas max-width of the content area slightly to accomodate larger screens */ +.bd-main .bd-content .bd-article-container { + max-width: 1000px; +} diff --git a/doc/case_studies/exploring-weather.rst b/doc/case_studies/exploring-weather.rst index 9670f95c4..4b16bef2a 100644 --- a/doc/case_studies/exploring-weather.rst +++ b/doc/case_studies/exploring-weather.rst @@ -263,7 +263,7 @@ This is the end of this tutorial where you have seen various ways to bin and aggregate data, derive new fields, and customize your charts. You can find more visualizations in the :ref:`example-gallery`. If you want to further customize your charts, you can refer to Altair's -:ref:`API`. +:ref:`api`. .. _Pandas: http://pandas.pydata.org/ diff --git a/doc/getting_started/overview.rst b/doc/getting_started/overview.rst index 07610b86e..929fd8651 100644 --- a/doc/getting_started/overview.rst +++ b/doc/getting_started/overview.rst @@ -36,7 +36,7 @@ concise grammar. The project is named after the `brightest star `_ in the constellation Aquila. From Earth's sky Altair appears close to Vega, the star from which our parent project drew its name. -This documentation serves as the main reference for learning about Altair. Additional learning material and tutorials can be found in the :ref:`learning-resources` section. +This documentation serves as the main reference for learning about Altair. Additional learning material and tutorials can be found in the :ref:`learning-resources` section. It can also be helpful to browse the `Vega-Lite documentation `_. .. _Vega: http://vega.github.io/vega .. _Vega-Lite: http://vega.github.io/vega-lite diff --git a/doc/index.rst b/doc/index.rst index f5d8d1088..f87d66e13 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -5,16 +5,49 @@ Vega-Altair: Declarative Visualization in Python .. role:: raw-html(raw) :format: html - .. altair-minigallery:: - :names: one_dot_per_zipcode, horizon_graph, world_projections, candlestick_chart, falkensee, scatter_linked_brush, layered_heatmap_text, natural_disasters, streamgraph, multiline_tooltip, select_detail, choropleth, interactive_cross_highlight, seattle_weather_interactive, london_tube - :size: 15 + :names: one_dot_per_zipcode, horizon_graph, world_projections, candlestick_chart, falkensee, errorbars_with_ci, scatter_linked_brush, line_with_ci, natural_disasters, bar_rounded, streamgraph, multiline_tooltip, choropleth, select_detail, interactive_cross_highlight, seattle_weather_interactive, london_tube, ridgeline_plot, violin_plot, strip_plot, table_bubble_plot_github, radial_chart, boxplot, mosaic_with_labels + :size: 24 + + +.. rst-class:: lead + + **Vega-Altair** is a declarative visualization library for Python. Its simple, friendly and consistent API, built on top of the powerful Vega-Lite_ grammar, empowers you to spend less time writing code and more time exploring your data. + + +.. grid:: 1 1 2 2 + :padding: 0 2 3 5 + :gutter: 2 2 3 3 + :class-container: startpage-grid + + .. grid-item-card:: Getting Started + :link: overview + :link-type: ref + :link-alt: Getting started + + In the Getting Started section you can find installation instructions and a high-level overview of the main concepts. + + .. grid-item-card:: User Guide + :link: user-guide-data + :link-type: ref + :link-alt: User guide + + Check out the User Guides for in-depth information on the key concepts of Vega-Altair. + + .. grid-item-card:: Examples + :link: example-gallery + :link-type: ref + :link-alt: Examples + + The Examples gallery contains a selection of different visualizations which you can create with Vega-Altair. -Vega-Altair is a declarative statistical visualization library for Python. + .. grid-item-card:: API + :link: api + :link-type: ref + :link-alt: api -Its simple, friendly and consistent API, built on top of the powerful Vega-Lite_ grammar, empowers you to spend less time writing code and more time exploring your data. + The API reference guide contains detailed information on all of Vega-Altair's methods and classes. -You can browse this site via the links in the top navigation panel. It can also be helpful to browse the `Vega-Lite documentation `_. *The Vega-Altair open-source project is not affiliated with Altair Engineering, Inc.* @@ -25,7 +58,7 @@ You can browse this site via the links in the top navigation panel. It can also Getting Started User Guide Examples - API + API user_guide/ecosystem releases/changes diff --git a/doc/user_guide/API.rst b/doc/user_guide/api.rst similarity index 99% rename from doc/user_guide/API.rst rename to doc/user_guide/api.rst index 24497b5fd..5ed9fbe18 100644 --- a/doc/user_guide/API.rst +++ b/doc/user_guide/api.rst @@ -1,4 +1,4 @@ -.. _API: +.. _api: API Reference ============= diff --git a/tools/generate_api_docs.py b/tools/generate_api_docs.py index 843019a37..20335c116 100644 --- a/tools/generate_api_docs.py +++ b/tools/generate_api_docs.py @@ -1,20 +1,20 @@ """ -This script fills the contents of doc/user_guide/API.rst +This script fills the contents of doc/user_guide/api.rst based on the updated Altair schema. """ -from os.path import abspath, dirname, join import sys import types +from os.path import abspath, dirname, join # Import Altair from head ROOT_DIR = abspath(join(dirname(__file__), "..")) sys.path.insert(0, ROOT_DIR) import altair as alt # noqa: E402 -API_FILENAME = join(ROOT_DIR, "doc", "user_guide", "API.rst") +API_FILENAME = join(ROOT_DIR, "doc", "user_guide", "api.rst") API_TEMPLATE = """\ -.. _API: +.. _api: API Reference ============= From e2e2a5b86b0190fd8b4d1eb1b56fcd6d5b7ccd7d Mon Sep 17 00:00:00 2001 From: Joel Ostblom Date: Thu, 25 May 2023 10:42:15 -0700 Subject: [PATCH 10/12] TitleParam to Title in example gallery and sync scatterplot table (#3066) * Change titleparams to title in examples * Sync scatter plot table examples --- .../natural_disasters.py | 2 +- .../scatter_linked_table.py | 28 +++++++++++-------- .../natural_disasters.py | 2 +- .../scatter_linked_table.py | 8 +++--- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/tests/examples_arguments_syntax/natural_disasters.py b/tests/examples_arguments_syntax/natural_disasters.py index 031c1e987..890580a6a 100644 --- a/tests/examples_arguments_syntax/natural_disasters.py +++ b/tests/examples_arguments_syntax/natural_disasters.py @@ -36,7 +36,7 @@ ).properties( width=450, height=320, - title=alt.TitleParams( + title=alt.Title( text="Global Deaths from Natural Disasters (1900-2017)", subtitle="The size of the bubble represents the total death count per year, by type of disaster", anchor='start' diff --git a/tests/examples_arguments_syntax/scatter_linked_table.py b/tests/examples_arguments_syntax/scatter_linked_table.py index c31c4a9c9..93777b751 100644 --- a/tests/examples_arguments_syntax/scatter_linked_table.py +++ b/tests/examples_arguments_syntax/scatter_linked_table.py @@ -1,7 +1,7 @@ """ Brushing Scatter Plot to Show Data on a Table --------------------------------------------- -A scatter plot of the cars dataset, with data tables for horsepower, MPG, and origin. +A scatter plot of the cars dataset, with data tables for horsepower, MPG, and origin. The tables update to reflect the selection on the scatter plot. """ # category: scatter plots @@ -18,26 +18,30 @@ points = alt.Chart(source).mark_point().encode( x='Horsepower:Q', y='Miles_per_Gallon:Q', - color=alt.condition(brush, 'Cylinders:O', alt.value('grey')) + color=alt.condition(brush, alt.value('steelblue'), alt.value('grey')) ).add_params(brush) # Base chart for data tables -ranked_text = alt.Chart(source).mark_text().encode( - y=alt.Y('row_number:O',axis=None) -).transform_window( - row_number='row_number()' +ranked_text = alt.Chart(source).mark_text(align='right').encode( + y=alt.Y('row_number:O', axis=None) ).transform_filter( brush ).transform_window( - rank='rank(row_number)' + row_number='row_number()' ).transform_filter( - alt.datum.rank<20 + alt.datum.row_number < 15 ) # Data Tables -horsepower = ranked_text.encode(text='Horsepower:N').properties(title='Horsepower') -mpg = ranked_text.encode(text='Miles_per_Gallon:N').properties(title='MPG') -origin = ranked_text.encode(text='Origin:N').properties(title='Origin') +horsepower = ranked_text.encode(text='Horsepower:N').properties( + title=alt.Title(text='Horsepower', align='right') +) +mpg = ranked_text.encode(text='Miles_per_Gallon:N').properties( + title=alt.Title(text='MPG', align='right') +) +origin = ranked_text.encode(text='Origin:N').properties( + title=alt.Title(text='Origin', align='right') +) text = alt.hconcat(horsepower, mpg, origin) # Combine data tables # Build chart @@ -46,4 +50,6 @@ text ).resolve_legend( color="independent" +).configure_view( + stroke=None ) diff --git a/tests/examples_methods_syntax/natural_disasters.py b/tests/examples_methods_syntax/natural_disasters.py index 31b50a151..cbc5a0d68 100644 --- a/tests/examples_methods_syntax/natural_disasters.py +++ b/tests/examples_methods_syntax/natural_disasters.py @@ -36,7 +36,7 @@ ).properties( width=450, height=320, - title=alt.TitleParams( + title=alt.Title( text="Global Deaths from Natural Disasters (1900-2017)", subtitle="The size of the bubble represents the total death count per year, by type of disaster", anchor='start' diff --git a/tests/examples_methods_syntax/scatter_linked_table.py b/tests/examples_methods_syntax/scatter_linked_table.py index d2295e273..99979e074 100644 --- a/tests/examples_methods_syntax/scatter_linked_table.py +++ b/tests/examples_methods_syntax/scatter_linked_table.py @@ -29,18 +29,18 @@ ).transform_window( row_number='row_number()' ).transform_filter( - 'datum.row_number < 15' + alt.datum.row_number < 15 ) # Data Tables horsepower = ranked_text.encode(text='Horsepower:N').properties( - title=alt.TitleParams(text='Horsepower', align='right') + title=alt.Title(text='Horsepower', align='right') ) mpg = ranked_text.encode(text='Miles_per_Gallon:N').properties( - title=alt.TitleParams(text='MPG', align='right') + title=alt.Title(text='MPG', align='right') ) origin = ranked_text.encode(text='Origin:N').properties( - title=alt.TitleParams(text='Origin', align='right') + title=alt.Title(text='Origin', align='right') ) text = alt.hconcat(horsepower, mpg, origin) # Combine data tables From 958301de8571742e17834d1dda6c69ae639e239e Mon Sep 17 00:00:00 2001 From: Stefan Binder Date: Fri, 26 May 2023 23:03:25 +0200 Subject: [PATCH 11/12] Fix bug in reconstructing layered charts with from_json/from_dict (#3068) * Fix for when subcharts do not have layer_props * Test the to_json and from_json methods using all examples in the gallery --- altair/vegalite/v5/api.py | 9 ++++++--- tests/test_examples.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index e9aa439cb..ed449bcab 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -3395,9 +3395,12 @@ def remove_prop(subchart, prop): else: raise ValueError(f"There are inconsistent values {values} for {prop}") else: - # Top level has this prop; subchart props must be either - # Undefined or identical to proceed. - if all(c[prop] is Undefined or c[prop] == chart[prop] for c in subcharts): + # Top level has this prop; subchart must either not have the prop + # or it must be Undefined or identical to proceed. + if all( + getattr(c, prop, Undefined) is Undefined or c[prop] == chart[prop] + for c in subcharts + ): output_dict[prop] = chart[prop] else: raise ValueError(f"There are inconsistent values {values} for {prop}") diff --git a/tests/test_examples.py b/tests/test_examples.py index 578f38303..da5808067 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -3,6 +3,7 @@ import pytest +import altair as alt from altair.utils.execeval import eval_block from tests import examples_arguments_syntax from tests import examples_methods_syntax @@ -48,6 +49,39 @@ def test_render_examples_to_chart(syntax_module): ) from err +@pytest.mark.parametrize( + "syntax_module", [examples_arguments_syntax, examples_methods_syntax] +) +def test_from_and_to_json_roundtrip(syntax_module): + """Tests if the to_json and from_json (and by extension to_dict and from_dict) + work for all examples in the Example Gallery. + """ + for filename in iter_examples_filenames(syntax_module): + source = pkgutil.get_data(syntax_module.__name__, filename) + chart = eval_block(source) + + if chart is None: + raise ValueError( + f"Example file {filename} should define chart in its final " + "statement." + ) + + try: + first_json = chart.to_json() + reconstructed_chart = alt.Chart.from_json(first_json) + # As the chart objects are not + # necessarily the same - they could use different objects to encode the same + # information - we do not test for equality of the chart objects, but rather + # for equality of the json strings. + second_json = reconstructed_chart.to_json() + assert first_json == second_json + except Exception as err: + raise AssertionError( + f"Example file {filename} raised an exception when " + f"doing a json conversion roundtrip: {err}" + ) from err + + # We do not apply the save_engine mark to this test. This mark is used in # the build GitHub Action workflow to select the tests which should be rerun # with some of the saving engines uninstalled. This would not make sense for this test From ed1d42dd5d9c689803e172d36a39d6db76aadeb9 Mon Sep 17 00:00:00 2001 From: Mattijn van Hoek Date: Fri, 26 May 2023 23:27:53 +0200 Subject: [PATCH 12/12] maint: bump version to 5.0.1 --- altair/__init__.py | 2 +- doc/conf.py | 2 +- doc/releases/changes.rst | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/altair/__init__.py b/altair/__init__.py index ca4ee1af1..ad57eb9d3 100644 --- a/altair/__init__.py +++ b/altair/__init__.py @@ -1,5 +1,5 @@ # ruff: noqa -__version__ = "5.1.0dev" +__version__ = "5.0.1" from typing import Any diff --git a/doc/conf.py b/doc/conf.py index 76776b448..1ebc12852 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -76,7 +76,7 @@ # built documents. # # The short X.Y version. -version = "5.1.0" +version = "5.0.1" # The full version, including alpha/beta/rc tags. release = f"{version}" diff --git a/doc/releases/changes.rst b/doc/releases/changes.rst index 3b40db77a..7c624b4a6 100644 --- a/doc/releases/changes.rst +++ b/doc/releases/changes.rst @@ -3,11 +3,12 @@ Release Notes ============= -Version 5.0.1 (unreleased) --------------------------- +Version 5.0.1 (released May 26, 2023) +------------------------------------- -- Remove unwanted files during build to avoid littering site-packages folder (#3057) -- Deprecate the `.ref()` function for selections, instead of removing it (#3063). +- Remove unwanted files during build to avoid littering site-packages folder (#3057). +- Deprecate the ``.ref()`` function for selections, instead of removing it (#3063). +- Fix bug in reconstructing layered charts with ``Chart.from_json()``/``Chart.from_dict()`` (#3068). Version 5.0.0 (released May 9, 2023) ------------------------------------