Altair: Multiple Tooltip Elements

Created on 23 May 2018  路  7Comments  路  Source: altair-viz/altair

I am looking for a way to display multiple elements within one tooltip. I did not find a way to do this in Altair directly. In vega-lite, I would do something like

"tooltip": [
          {"type": "quantitative", "aggregate": "sum", "field": "A"},
          {"type": "quantitative", "aggregate": "sum", "field": "B"}
          ],

and it would show the sum of A and the sum of B like so:

vega_tooltip_demo

If Altair does not yet support this, I think this is a useful feature to be added in the future.

Regards, Immanuel

Most helpful comment

It can also be done in version 2.0, but not as conveniently. You have to bypass the encode() method and modify the encoding directly without any shorthands; e.g.

chart = alt.Chart().encode(...) #... define the full chart + encodings first

# add multiple tooltips
chart.encoding.tooltip = [
          {"type": "quantitative", "aggregate": "sum", "field": "A"},
          {"type": "quantitative", "aggregate": "sum", "field": "B"}
]

All 7 comments

This is supported in the master branch, and will be included in the version 2.1 release. Just pass multiple field names to the tooltip encoding; e.g.

chart.encode(
    tooltip=['sum(A):Q', 'sum(B):Q']
)

It can also be done in version 2.0, but not as conveniently. You have to bypass the encode() method and modify the encoding directly without any shorthands; e.g.

chart = alt.Chart().encode(...) #... define the full chart + encodings first

# add multiple tooltips
chart.encoding.tooltip = [
          {"type": "quantitative", "aggregate": "sum", "field": "A"},
          {"type": "quantitative", "aggregate": "sum", "field": "B"}
]

I am looking forward to v2.1! And thanks a lot for the fast reply with the hack for v2.0

Sadly, it is not working (but almost):

With

my_chart.tooltip = [
    {"type": "quantitative", "aggregate": "sum", "field": "A"},
    {"type": "quantitative", "aggregate": "sum", "field": "B"}
]

Altair emits the following vega-lite specification

tooltip: [
    {'type': 'quantitative', 'aggregate': 'sum', 'field': 'A'},
    {'type': 'quantitative', 'aggregate': 'sum', 'field': 'B'}
]

which has a syntactic error because the single quotes ' should be double quotes ".

Fixing the quotes in the vega-lite code manually results in properly rendered graphics.

That's really strange... the JSON output that's passed to the renderer goes through the json.dumps() function, which should produce valid JSON no matter how the chart was constructed.

Can you paste code that reproduces the error you're seeing?

First I define the chart (which is a heatmap) as follows:

activity_heatmap = alt.Chart(df_dbms).mark_bar().encode(
    x = alt.X('Time', bin=alt.Bin(maxbins=25), axis=alt.Axis(title='Time [seconds]')),
    y = alt.Y('Address', bin=alt.Bin(step=4096*2**12), axis=alt.Axis(format='0X', title='Virtual Address Range')),
    color = alt.Color('count()', title='# of Operations (x1000)'),
)

And then I set the tooltip

activity_heatmap.tooltip = [
    {"type": "quantitative", "aggregate": "sum", "field": "is_read"},
    {"type": "quantitative", "aggregate": "sum", "field": "is_write"}
]

When trying to render the plot, I get the following error:

SchemaValidationError                     Traceback (most recent call last)
/usr/lib/python3.6/site-packages/altair/vegalite/v2/api.py in to_dict(self, *args, **kwargs)
    338         if dct is None:
    339             kwargs['validate'] = 'deep'
--> 340             dct = super(TopLevelMixin, copy).to_dict(*args, **kwargs)
    341 
    342         if is_top_level:

/usr/lib/python3.6/site-packages/altair/utils/schemapi.py in to_dict(self, validate, ignore, context)
    243             result = _todict(self._args[0])
    244         elif not self._args:
--> 245             result = _todict({k: v for k, v in self._kwds.items()
    246                               if k not in ignore})
    247         else:

/usr/lib/python3.6/site-packages/altair/utils/schemapi.py in _todict(val)
    235                 return [_todict(v) for v in val]
    236             elif isinstance(val, dict):
--> 237                 return {k: _todict(v) for k, v in val.items()
    238                         if v is not Undefined}
    239             else:

/usr/lib/python3.6/site-packages/altair/utils/schemapi.py in <dictcomp>(.0)
    236             elif isinstance(val, dict):
    237                 return {k: _todict(v) for k, v in val.items()
--> 238                         if v is not Undefined}
    239             else:
    240                 return val

/usr/lib/python3.6/site-packages/altair/utils/schemapi.py in _todict(val)
    233                 return val.to_dict(validate=sub_validate, context=context)
    234             elif isinstance(val, (list, tuple)):
--> 235                 return [_todict(v) for v in val]
    236             elif isinstance(val, dict):
    237                 return {k: _todict(v) for k, v in val.items()

/usr/lib/python3.6/site-packages/altair/utils/schemapi.py in <listcomp>(.0)
    233                 return val.to_dict(validate=sub_validate, context=context)
    234             elif isinstance(val, (list, tuple)):
--> 235                 return [_todict(v) for v in val]
    236             elif isinstance(val, dict):
    237                 return {k: _todict(v) for k, v in val.items()

/usr/lib/python3.6/site-packages/altair/utils/schemapi.py in _todict(val)
    231         def _todict(val):
    232             if isinstance(val, SchemaBase):
--> 233                 return val.to_dict(validate=sub_validate, context=context)
    234             elif isinstance(val, (list, tuple)):
    235                 return [_todict(v) for v in val]

/usr/lib/python3.6/site-packages/altair/vegalite/v2/api.py in to_dict(self, *args, **kwargs)
    338         if dct is None:
    339             kwargs['validate'] = 'deep'
--> 340             dct = super(TopLevelMixin, copy).to_dict(*args, **kwargs)
    341 
    342         if is_top_level:

/usr/lib/python3.6/site-packages/altair/utils/schemapi.py in to_dict(self, validate, ignore, context)
    252                 self.validate(result)
    253             except jsonschema.ValidationError as err:
--> 254                 raise SchemaValidationError(self, err)
    255         return result
    256 

SchemaValidationError: Invalid specification

        altair.vegalite.v2.api.Chart, validating 'additionalProperties'

        Additional properties are not allowed ('tooltip' was unexpected)

from which I conclude that there is a problem validating the generated JSON specification for vega.

The emitted vega code contains the following tooltip definition:

tooltip: [
    {'type': 'quantitative', 'aggregate': 'sum', 'field': 'is_read'},
    {'type': 'quantitative', 'aggregate': 'sum', 'field': 'is_write'}
]

Now if i render the plot without the tooltop specification and open it in the vega editor and paste the erroneous tooltip specification from above, I get syntax errors. Then, replacing single quotes ' by double quotes " fixes these errors and I get the plot and tooltips as desired. The vega code then looks as follows

"encoding": {
        "tooltip": [
          {"type": "quantitative", "aggregate": "sum", "field": "is_read"},
          {"type": "quantitative", "aggregate": "sum", "field": "is_write"}
        ],
        ...
}

Sorry, my mistake. Instead of activity_heatmap.tooltip = [...] you need to use activity_heatmap.encoding.tooltip = [...].

Thanks, that did the trick ;)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bmcfee picture bmcfee  路  3Comments

maxgerma picture maxgerma  路  3Comments

DentonGentry picture DentonGentry  路  3Comments

nielsmde picture nielsmde  路  4Comments

firasm picture firasm  路  3Comments