Skip to content

Commit

Permalink
Merge pull request #2528 from ChristopherDavisUCI/samplev52
Browse files Browse the repository at this point in the history
WIP: update to Vega-Lite 5.2
  • Loading branch information
jakevdp authored Mar 24, 2022
2 parents 702470f + af0c3b4 commit fc7f551
Show file tree
Hide file tree
Showing 33 changed files with 69,211 additions and 93 deletions.
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:/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:/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
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 *
23 changes: 23 additions & 0 deletions altair/vegalite/v5/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# flake8: noqa
from .schema import *
from .api import *

from ...datasets import list_datasets, load_dataset

from ... import expr
from ...expr import datum

from .display import VegaLite, renderers

from .data import (
MaxRowsError,
pipe,
curry,
limit_rows,
sample,
to_json,
to_csv,
to_values,
default_data_transformer,
data_transformers,
)
Loading

0 comments on commit fc7f551

Please sign in to comment.