pruefsumme
pruefsumme

Reputation: 341

Multi-line tooltip for faceted chart leads to error

I am trying to setup a multi-line tooltip for a faceted chart in Altair. The facet helps to compare multi-year data (i.e. so that January 2018 can easily be compared to January 2019).

Below is a code sample with random data, following the example found in the documentation: https://altair-viz.github.io/gallery/multiline_tooltip.html.

Depending on where I define the facet I get one of the following errors:

  1. AttributeError: 'FacetChart' object has no attribute 'mark_point' : occurs when I facet the line chart in the beginning and then try to apply mark_point() on this line chart

  2. ValueError: Faceted charts cannot be layered. : occurs when I facet the line chart in the end (when building the layer)

Is there a workaround?

start = pd.Timestamp('20180101', tz='Europe/Rome')
end = pd.Timestamp('20200101', tz='Europe/Rome')
index = pd.date_range(start,end,freq='M')[:-1]

source = pd.DataFrame(np.cumsum(np.random.randn(len(index), 3), 0).round(2),
                    columns=['A', 'B', 'C'], index=index)
source.index.names = ['x']
source = source.reset_index().melt('x', var_name='category', value_name='y')

### line chart with facet
line = alt.Chart(source).mark_line(point=True).encode(alt.X('month(x):O', title=''), alt.Y('y:Q', title=''), color='category:N').facet(row=alt.Row('year(x):O', title=''))


nearest = alt.selection(type='single', nearest=True, on='mouseover',
                        fields=['x'], empty='none')
selectors = alt.Chart(source).mark_point().encode(
    x='x:Q',
    opacity=alt.value(0),
).add_selection(
    nearest
)
points = line.mark_point().encode(
    opacity=alt.condition(nearest, alt.value(1), alt.value(0))
)
text = line.mark_text(align='left', dx=5, dy=-5).encode(
    text=alt.condition(nearest, 'y:Q', alt.value(' '))
)
rules = alt.Chart(source).mark_rule(color='gray').encode(
    x='x:Q',
).transform_filter(
    nearest
)
alt.layer(
    line, selectors, points, rules, text
).properties(
    width=600, height=300
)

Upvotes: 1

Views: 506

Answers (1)

jakevdp
jakevdp

Reputation: 86320

If you would like to facet a layered chart, you should call the .facet() method on that layered chart.

You also probably want to change your x-encodings in the layers so that they all encode the same thing.

The result looks like this:

import numpy as np
import pandas as pd
import altair as alt

start = pd.Timestamp('20180101', tz='Europe/Rome')
end = pd.Timestamp('20200101', tz='Europe/Rome')
index = pd.date_range(start,end,freq='M')[:-1]

source = pd.DataFrame(np.cumsum(np.random.randn(len(index), 3), 0).round(2),
                    columns=['A', 'B', 'C'], index=index)
source.index.names = ['x']
source = source.reset_index().melt('x', var_name='category', value_name='y')

### line chart with facet
line = alt.Chart(source).mark_line(point=True).encode(alt.X('month(x):O', title=''), alt.Y('y:Q', title=''), color='category:N')


nearest = alt.selection(type='single', nearest=True, on='mouseover',
                        fields=['x'], empty='none')
selectors = alt.Chart(source).mark_point().encode(
    x='month(x):O',
    opacity=alt.value(0),
).add_selection(
    nearest
)
points = line.mark_point().encode(
    opacity=alt.condition(nearest, alt.value(1), alt.value(0))
)
text = line.mark_text(align='left', dx=5, dy=-5).encode(
    text=alt.condition(nearest, 'y:Q', alt.value(' '))
)
rules = alt.Chart(source).mark_rule(color='gray').encode(
    x='month(x):O',
).transform_filter(
    nearest
)
alt.layer(
    line, selectors, points, rules, text
).properties(
    width=600, height=300
).facet(row=alt.Row('year(x):O', title=''))

enter image description here

Upvotes: 1

Related Questions