Reputation: 211
I am trying to create a plot similar to this one:
https://altair-viz.github.io/gallery/multiline_tooltip.html
I want to add a drop-down menu to select different objects.
I've modified my code to create an example to illustrate:
import altair as alt
import pandas as pd
import numpy as np
np.random.seed(42)
source = pd.DataFrame(np.cumsum(np.random.randn(100, 3), 0).round(2),
columns=['A', 'B', 'C'], index=pd.RangeIndex(100, name='x'))
source = source.reset_index().melt('x', var_name='category', value_name='y')
source['Type'] = 'First'
source_1 = source.copy()
source_1['y'] = source_1['y'] + 5
source_1['Type'] = 'Second'
source_2 = source.copy()
source_2['y'] = source_2['y'] - 5
source_2['Type'] = 'Third'
source = pd.concat([source, source_1, source_2])
input_dropdown = alt.binding_select(options=['First', 'Second', 'Third'])
selection = alt.selection_single(name='Select', fields=['Type'],
bind=input_dropdown)
# color = alt.condition(select_state,
# alt.Color('Type:N', legend=None),
# alt.value('lightgray'))
# Create a selection that chooses the nearest point & selects based on x-value
nearest = alt.selection(type='single', nearest=True, on='mouseover',
fields=['x'], empty='none')
# The basic line
base = alt.Chart(source).encode(
x='x:Q',
y='y:Q',
color='category:N'
)
# add drop-down menu
lines = base.mark_line(interpolate='basis').add_selection(selection
).transform_filter(selection)
# Transparent selectors across the chart. This is what tells us
# the x-value of the cursor
selectors = alt.Chart(source).mark_point().encode(
x='x:Q',
opacity=alt.value(0),
).add_selection(
nearest
)
# Draw points on the line, and highlight based on selection
points = base.mark_point().encode(
opacity=alt.condition(nearest, alt.value(1), alt.value(0))
)
# Draw text labels near the points, and highlight based on selection
text = base.mark_text(align='left', dx=5, dy=-5).encode(
text=alt.condition(nearest, 'y:Q', alt.value(' '))
)
# Draw a rule at the location of the selection
rules = alt.Chart(source).mark_rule(color='gray').encode(
x='x:Q',
).transform_filter(
nearest
)
#Put the five layers into a chart and bind the data
alt.layer(
lines, selectors, points, rules, text
).properties(
width=500, height=300
)
As you can see, every time I select one type ('First', 'Second' or 'Third), the interactive plot still shows the points from all the three instead of just one, although only the lines of one type are shown.
Original Question:
I am trying to create a plot similar to this one:
https://altair-viz.github.io/gallery/multiline_tooltip.html
with export, import and deficit. I want to add a drop-down menu to select different states (so each state will have such a plot).
My data looks like this:
State Year Category Trade, in Million Dollars
0 Texas 2008 Export 8970.979210
1 California 2008 Export 11697.850116
2 Washington 2008 Import 8851.678608
3 South Carolina 2008 Deficit 841.495319
4 Oregon 2008 Import 2629.939168
I've tried a few different methods but all failed. If I only want to plot 'line' object, I can do it fine. But I cannot combine 'line' with 'points'.
import altair as alt
states = list(df_trade_china.State.unique())
input_dropdown = alt.binding_select(options=states)
select_state = alt.selection_single(name='Select', fields=['State'],
bind=input_dropdown)
# Create a selection that chooses the nearest point & selects based on x-value
nearest = alt.selection(type='single', nearest=True, on='mouseover',
fields=['Year'], empty='none')
# The basic line
line = alt.Chart(df_trade_china).mark_line().encode(
x='Year:O',
y='Trade, in Million Dollars:Q',
color='Category:N'
).add_selection(select_state
).transform_filter(select_state
)
# Transparent selectors across the chart. This is what tells us
# the x-value of the cursor
selectors = alt.Chart(df_trade_china).mark_point().encode(
x='Year:O',
opacity=alt.value(0),
).add_selection(
nearest
)
# Draw points on the line, and highlight based on selection
points = line.mark_point().encode(
opacity=alt.condition(nearest, alt.value(1), alt.value(0))
)
# Draw text labels near the points, and highlight based on selection
text = line.mark_text(align='left', dx=5, dy=-5).encode(
text=alt.condition(nearest, 'Trade, in Million Dollars:Q', alt.value(' '))
)
# Draw a rule at the location of the selection
rules = alt.Chart(df_trade_china).mark_rule(color='gray').encode(
x='Year:Q',
).transform_filter(
nearest
)
#Put the five layers into a chart and bind the data
alt.layer(
line
).properties(
width=500, height=300
)
Here is the error message I got.
JavaScript Error: Duplicate signal name: "Select_tuple"
This usually means there's a typo in your chart specification. See the javascript console for the full traceback.
Upvotes: 2
Views: 2039
Reputation: 86443
New answer to new question:
Your filter transform is only being applied to the line data, and so it only filters the lines. If you want to filter every layer, make certain that every layer has the filter transform.
Here is how that would look with your code:
import altair as alt
import pandas as pd
import numpy as np
np.random.seed(42)
source = pd.DataFrame(np.cumsum(np.random.randn(100, 3), 0).round(2),
columns=['A', 'B', 'C'], index=pd.RangeIndex(100, name='x'))
source = source.reset_index().melt('x', var_name='category', value_name='y')
source['Type'] = 'First'
source_1 = source.copy()
source_1['y'] = source_1['y'] + 5
source_1['Type'] = 'Second'
source_2 = source.copy()
source_2['y'] = source_2['y'] - 5
source_2['Type'] = 'Third'
source = pd.concat([source, source_1, source_2])
input_dropdown = alt.binding_select(options=['First', 'Second', 'Third'])
selection = alt.selection_single(name='Select', fields=['Type'],
bind=input_dropdown, init={'Type': 'First'})
# color = alt.condition(select_state,
# alt.Color('Type:N', legend=None),
# alt.value('lightgray'))
# Create a selection that chooses the nearest point & selects based on x-value
nearest = alt.selection(type='single', nearest=True, on='mouseover',
fields=['x'], empty='none')
# The basic line
base = alt.Chart(source).encode(
x='x:Q',
y='y:Q',
color='category:N'
).transform_filter(
selection
)
# add drop-down menu
lines = base.mark_line(interpolate='basis').add_selection(selection
)
# Transparent selectors across the chart. This is what tells us
# the x-value of the cursor
selectors = alt.Chart(source).mark_point().encode(
x='x:Q',
opacity=alt.value(0),
).add_selection(
nearest
)
# Draw points on the line, and highlight based on selection
points = base.mark_point().encode(
opacity=alt.condition(nearest, alt.value(1), alt.value(0))
)
# Draw text labels near the points, and highlight based on selection
text = base.mark_text(align='left', dx=5, dy=-5).encode(
text=alt.condition(nearest, 'y:Q', alt.value(' '))
)
# Draw a rule at the location of the selection
rules = alt.Chart(source).mark_rule(color='gray').encode(
x='x:Q',
).transform_filter(
nearest
)
#Put the five layers into a chart and bind the data
alt.layer(
lines, selectors, points, rules, text
).properties(
width=500, height=300
)
Original answer to original question:
An individual selection can only be added to a chart once. When you write something like this:
line = alt.Chart(...).add_selection(selection)
points = line.mark_point()
the same selection is added in both line
and points
(because points
is derived from line
). When you layer them, each layer declares an identical selection, which leads to a Duplicate Signal Name error.
To fix this, avoid adding the same selection to multiple components of a single chart.
For example, you can do something like this (switching to an example dataset, because you didn't provide data in your question):
import altair as alt
from vega_datasets import data
stocks = data.stocks()
stocks.symbol.unique().tolist()
input_dropdown = alt.binding_select(options=stocks.symbol.unique().tolist())
selection = alt.selection_single(fields=['symbol'], bind=input_dropdown,
name='Company', init={'symbol': 'GOOG'})
color = alt.condition(selection,
alt.Color('symbol:N', legend=None),
alt.value('lightgray'))
base = alt.Chart(stocks).encode(
x='date',
y='price',
color=color
)
line = base.mark_line().add_selection(
selection
)
point = base.mark_point()
line + point
Note that the selection declaration via add_selection()
can only be called on a single component of the chart, while the effect of the selection (here, the color condition) can be added to multiple components of the chart.
Upvotes: 1