Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: update to Vega-Lite 5.2 #2528

Merged
merged 27 commits into from
Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
14bbb8d
Initial creation of vegalite/v5 folder
ChristopherDavisUCI Nov 27, 2021
65399d3
Initial VariableParameter definitions
ChristopherDavisUCI Nov 27, 2021
50c4732
Updates to parameter definitions
ChristopherDavisUCI Nov 27, 2021
c2a58b2
Move height/width/view from layer to parent
ChristopherDavisUCI Nov 27, 2021
c143c04
Increase compatibility with old selection syntax
ChristopherDavisUCI Nov 28, 2021
5bba402
Fix formatting
ChristopherDavisUCI Nov 28, 2021
4d0ae32
Updated use of PredicateComposition
ChristopherDavisUCI Dec 2, 2021
d4915ce
Update scatter_with_minimap.py
ChristopherDavisUCI Dec 2, 2021
870e30c
Update api.py
ChristopherDavisUCI Dec 3, 2021
566a779
First draft of parameters documentation
ChristopherDavisUCI Dec 13, 2021
015b036
Merge remote-tracking branch 'upstream/master' into samplev52
ChristopherDavisUCI Dec 13, 2021
ea9ae53
Merge remote-tracking branch 'upstream/master' into samplev52
ChristopherDavisUCI Dec 30, 2021
846a842
Define SelectionPredicateComposition
ChristopherDavisUCI Jan 8, 2022
b391eec
Update test_add_selection
ChristopherDavisUCI Jan 8, 2022
3b41f22
Update test_selection_expression
ChristopherDavisUCI Jan 8, 2022
b8fd70e
Update tests related to selections
ChristopherDavisUCI Jan 11, 2022
55e88fb
point temporary to master of altair_viewer
mattijn Jan 17, 2022
c1236a6
alt.VEGALITE_VERSION uses this schema
mattijn Jan 18, 2022
96634c8
Update expected vega_spec
ChristopherDavisUCI Jan 18, 2022
454bbfe
Merge branch 'samplev52' of https://github.com/ChristopherDavisUCI/al…
ChristopherDavisUCI Jan 18, 2022
62ec051
Update release notes
ChristopherDavisUCI Feb 4, 2022
d0022f3
Update test_plugin_registry.py
ChristopherDavisUCI Feb 4, 2022
6e62f53
Corrected add_parameter definition on multi-view charts
ChristopherDavisUCI Feb 9, 2022
a941cc1
Remove remove obsolete schema classes from the user guide
ChristopherDavisUCI Mar 2, 2022
5d83434
Update parameters documentation
ChristopherDavisUCI Mar 3, 2022
2168430
Include new classes in API.rst
ChristopherDavisUCI Mar 4, 2022
af0c3b4
Examples involving offsets
ChristopherDavisUCI Mar 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
python -m pip install --upgrade pip
pip install .[dev]
pip install altair_saver
pip install git+git://github.com/altair-viz/altair_viewer.git
- name: Test with pytest
run: |
pytest --doctest-modules altair
1 change: 1 addition & 0 deletions .github/workflows/docbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
pip install .[dev]
pip install altair_saver
pip install -r doc/requirements.txt
pip install git+git://github.com/altair-viz/altair_viewer.git
- name: Run docbuild
run: |
cd doc && make ${{ matrix.build-type }}
Expand Down
20 changes: 20 additions & 0 deletions altair/examples/grouped_bar_chart2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
Grouped Bar Chart with xOffset
------------------------------
Like :ref:`gallery_grouped_bar_chart`, this example shows a grouped bar chart. Whereas :ref:`gallery_grouped_bar_chart` used the ``column`` encoding channel, this example uses the ``xOffset`` encoding channel. This is adapted from a corresponding Vega-Lite Example:
`Grouped Bar Chart <https://vega.github.io/vega-lite/examples/bar_grouped.html>`_.
"""
# category: bar charts
import altair as alt
import pandas as pd

source = pd.DataFrame({"Category":list("AAABBBCCC"),
"Group":list("xyzxyzxyz"),
"Value":[0.1, 0.6, 0.9, 0.7, 0.2, 1.1, 0.6, 0.1, 0.2]})

alt.Chart(source).mark_bar().encode(
x="Category:N",
y="Value:Q",
xOffset="Group:N",
color="Group:N"
)
21 changes: 21 additions & 0 deletions altair/examples/jitter_chart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
Jitter Chart
------------
In this chart, we encode the ``Cylinders`` column from the ``cars`` dataset in the ``y``-channel. Because most cars (all but seven) in this dataset have 4, 6, or 8 cylinders, the default presentation of this data would show most of the data concentrated on three horizontal lines. Furthermore, in that default presentation, it would be difficult to gauge the relative frequencies with which different values occur (because there would be so much overlap). To compensate for this, we use the ``yOffset`` channel to incorporate a random offset (jittering). This is adapted from a corresponding Vega-Lite Example:
`Dot Plot with Jittering <https://vega.github.io/vega-lite/examples/point_offset_random.html>`_.
"""
# category: scatter plots
import altair as alt
from vega_datasets import data

source = data.cars()

alt.Chart(source).mark_point().encode(
x='Horsepower:Q',
y='Cylinders:O',
yOffset='randomCalc:Q'
).transform_calculate(
randomCalc='random()'
).properties(
height=alt.Step(50)
)
4 changes: 2 additions & 2 deletions altair/examples/scatter_with_minimap.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@
.mark_point()
.encode(
x=alt.X(
"date:T", scale=alt.Scale(domain={"selection": zoom.name, "encoding": "x"})
"date:T", scale=alt.Scale(domain={"param": zoom.name, "encoding": "x"})
),
y=alt.Y(
"temp_max:Q",
scale=alt.Scale(domain={"selection": zoom.name, "encoding": "y"}),
scale=alt.Scale(domain={"param": zoom.name, "encoding": "y"}),
),
color="weather",
)
Expand Down
30 changes: 30 additions & 0 deletions altair/examples/slider_cutoff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""
Slider Cutoff
=============
This example shows how to bind a variable parameter to a slider, and how to use the corresponding bound value to color data points. This example is based on an example from the Altair 4 documentation for Interactions, in which the interactivity was accomplished using a selection. The version below has been simplified significantly through the use of a variable parameter. Variable parameters were added in Altair 5.
"""
# category: interactive charts
import altair as alt
import pandas as pd
import numpy as np

rand = np.random.RandomState(42)

df = pd.DataFrame({
'xval': range(100),
'yval': rand.randn(100).cumsum()
})

slider = alt.binding_range(min=0, max=100, step=1)
cutoff = alt.parameter(bind=slider, value=50)

alt.Chart(df).mark_point().encode(
x='xval',
y='yval',
color=alt.condition(
alt.datum.xval < cutoff,
alt.value('red'), alt.value('blue')
)
).add_parameter(
cutoff
)
117 changes: 77 additions & 40 deletions altair/expr/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,114 +31,151 @@ def _js_repr(val):
return "false"
elif val is None:
return "null"
elif isinstance(val, OperatorMixin):
return val._to_expr()
else:
return repr(val)


class Expression(SchemaBase):
"""Expression

Base object for enabling build-up of Javascript expressions using
a Python syntax. Calling ``repr(obj)`` will return a Javascript
representation of the object and the operations it encodes.
"""

_schema = {"type": "string"}

def to_dict(self, *args, **kwargs):
# Designed to work with Expression and VariableParameter
class OperatorMixin(object):
def _to_expr(self):
return repr(self)

def __setattr__(self, attr, val):
# We don't need the setattr magic defined in SchemaBase
return object.__setattr__(self, attr, val)
def _from_expr(self, expr):
return expr

def __add__(self, other):
return BinaryExpression("+", self, other)
comp_value = BinaryExpression("+", self, other)
return self._from_expr(comp_value)

def __radd__(self, other):
return BinaryExpression("+", other, self)
comp_value = BinaryExpression("+", other, self)
return self._from_expr(comp_value)

def __sub__(self, other):
return BinaryExpression("-", self, other)
comp_value = BinaryExpression("-", self, other)
return self._from_expr(comp_value)

def __rsub__(self, other):
return BinaryExpression("-", other, self)
comp_value = BinaryExpression("-", other, self)
return self._from_expr(comp_value)

def __mul__(self, other):
return BinaryExpression("*", self, other)
comp_value = BinaryExpression("*", self, other)
return self._from_expr(comp_value)

def __rmul__(self, other):
return BinaryExpression("*", other, self)
comp_value = BinaryExpression("*", other, self)
return self._from_expr(comp_value)

def __truediv__(self, other):
return BinaryExpression("/", self, other)
comp_value = BinaryExpression("/", self, other)
return self._from_expr(comp_value)

def __rtruediv__(self, other):
return BinaryExpression("/", other, self)
comp_value = BinaryExpression("/", other, self)
return self._from_expr(comp_value)

__div__ = __truediv__

__rdiv__ = __rtruediv__

def __mod__(self, other):
return BinaryExpression("%", self, other)
comp_value = BinaryExpression("%", self, other)
return self._from_expr(comp_value)

def __rmod__(self, other):
return BinaryExpression("%", other, self)
comp_value = BinaryExpression("%", other, self)
return self._from_expr(comp_value)

def __pow__(self, other):
# "**" Javascript operator is not supported in all browsers
return FunctionExpression("pow", (self, other))
comp_value = FunctionExpression("pow", (self, other))
return self._from_expr(comp_value)

def __rpow__(self, other):
# "**" Javascript operator is not supported in all browsers
return FunctionExpression("pow", (other, self))
comp_value = FunctionExpression("pow", (other, self))
return self._from_expr(comp_value)

def __neg__(self):
return UnaryExpression("-", self)
comp_value = UnaryExpression("-", self)
return self._from_expr(comp_value)

def __pos__(self):
return UnaryExpression("+", self)
comp_value = UnaryExpression("+", self)
return self._from_expr(comp_value)

# comparison operators

def __eq__(self, other):
return BinaryExpression("===", self, other)
comp_value = BinaryExpression("===", self, other)
return self._from_expr(comp_value)

def __ne__(self, other):
return BinaryExpression("!==", self, other)
comp_value = BinaryExpression("!==", self, other)
return self._from_expr(comp_value)

def __gt__(self, other):
return BinaryExpression(">", self, other)
comp_value = BinaryExpression(">", self, other)
return self._from_expr(comp_value)

def __lt__(self, other):
return BinaryExpression("<", self, other)
comp_value = BinaryExpression("<", self, other)
return self._from_expr(comp_value)

def __ge__(self, other):
return BinaryExpression(">=", self, other)
comp_value = BinaryExpression(">=", self, other)
return self._from_expr(comp_value)

def __le__(self, other):
return BinaryExpression("<=", self, other)
comp_value = BinaryExpression("<=", self, other)
return self._from_expr(comp_value)

def __abs__(self):
return FunctionExpression("abs", (self,))
comp_value = FunctionExpression("abs", (self,))
return self._from_expr(comp_value)

# logical operators

def __and__(self, other):
return BinaryExpression("&&", self, other)
comp_value = BinaryExpression("&&", self, other)
return self._from_expr(comp_value)

def __rand__(self, other):
return BinaryExpression("&&", other, self)
comp_value = BinaryExpression("&&", other, self)
return self._from_expr(comp_value)

def __or__(self, other):
return BinaryExpression("||", self, other)
comp_value = BinaryExpression("||", self, other)
return self._from_expr(comp_value)

def __ror__(self, other):
return BinaryExpression("||", other, self)
comp_value = BinaryExpression("||", other, self)
return self._from_expr(comp_value)

def __invert__(self):
return UnaryExpression("!", self)
comp_value = UnaryExpression("!", self)
return self._from_expr(comp_value)


class Expression(OperatorMixin, SchemaBase):
"""Expression

Base object for enabling build-up of Javascript expressions using
a Python syntax. Calling ``repr(obj)`` will return a Javascript
representation of the object and the operations it encodes.
"""

_schema = {"type": "string"}

def to_dict(self, *args, **kwargs):
return repr(self)

def __setattr__(self, attr, val):
# We don't need the setattr magic defined in SchemaBase
return object.__setattr__(self, attr, val)

# item access
def __getitem__(self, val):
Expand Down
20 changes: 14 additions & 6 deletions altair/utils/tests/test_mimebundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def require_altair_saver():
@pytest.fixture
def vegalite_spec():
return {
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
Expand Down Expand Up @@ -97,10 +97,18 @@ def vega_spec():
"name": "data_0",
"source": "source_0",
"transform": [
{
"as": ["b_start", "b_end"],
"field": "b",
"groupby": ["a"],
"offset": "zero",
"sort": {"field": [], "order": []},
"type": "stack",
},
{
"expr": 'isValid(datum["b"]) && isFinite(+datum["b"])',
"type": "filter",
}
},
],
},
],
Expand All @@ -117,8 +125,8 @@ def vega_spec():
"fill": {"value": "#4c78a8"},
"width": {"band": 1, "scale": "x"},
"x": {"field": "a", "scale": "x"},
"y": {"field": "b", "scale": "y"},
"y2": {"scale": "y", "value": 0},
"y": {"field": "b_end", "scale": "y"},
"y2": {"field": "b_start", "scale": "y"},
}
},
"from": {"data": "data_0"},
Expand All @@ -138,7 +146,7 @@ def vega_spec():
"type": "band",
},
{
"domain": {"data": "data_0", "field": "b"},
"domain": {"data": "data_0", "fields": ["b_start", "b_end"]},
"name": "y",
"nice": True,
"range": [{"signal": "height"}, 0],
Expand Down Expand Up @@ -188,7 +196,7 @@ def test_spec_to_vegalite_mimebundle(vegalite_spec):
format="vega-lite",
vegalite_version=alt.VEGALITE_VERSION,
)
assert bundle == {"application/vnd.vegalite.v4+json": vegalite_spec}
assert bundle == {"application/vnd.vegalite.v5+json": vegalite_spec}


def test_spec_to_vega_mimebundle(vega_spec):
Expand Down
6 changes: 3 additions & 3 deletions altair/utils/tests/test_plugin_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_plugin_registry():
assert plugins.get() is None
assert repr(plugins) == "TypedCallableRegistry(active='', registered=[])"

plugins.register("new_plugin", lambda x: x ** 2)
plugins.register("new_plugin", lambda x: x**2)
assert plugins.names() == ["new_plugin"]
assert plugins.active == ""
assert plugins.get() is None
Expand All @@ -46,7 +46,7 @@ def test_plugin_registry():
def test_plugin_registry_extra_options():
plugins = GeneralCallableRegistry()

plugins.register("metadata_plugin", lambda x, p=2: x ** p)
plugins.register("metadata_plugin", lambda x, p=2: x**p)
plugins.enable("metadata_plugin")
assert plugins.get()(3) == 9

Expand Down Expand Up @@ -86,7 +86,7 @@ def test_plugin_registry_global_settings():
def test_plugin_registry_context():
plugins = GeneralCallableRegistry()

plugins.register("default", lambda x, p=2: x ** p)
plugins.register("default", lambda x, p=2: x**p)

# At first there is no plugin enabled
assert plugins.active == ""
Expand Down
2 changes: 1 addition & 1 deletion altair/vegalite/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# flake8: noqa
from .v4 import *
from .v5 import *
2 changes: 1 addition & 1 deletion altair/vegalite/schema.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Altair schema wrappers"""
# flake8: noqa
from .v4.schema import *
from .v5.schema import *
Loading