I try to adapt the Selection Detail Example from altair doc (https://altair-viz.github.io/gallery/select_detail.html#selection-detail-example).
I won't detailed my Dataframe structure which is identical with the one from the example (included variable names).
The native code is working well :
# Data is prepared, now make a chart
selector = alt.selection_single(empty='all', fields=['id'])
base = alt.Chart(data).properties(
width=250,
height=250
).add_selection(selector)
points = base.mark_point(filled=True, size=200,opacity=0.9).encode(
x=alt.X('mean(y)',title='Durée de perception',scale=alt.Scale(domain=(11, 23))),
y=alt.Y('mean(x)',title='Taux de marge (%PM)'),
color=alt.condition(selector, 'id:O', alt.value('lightgray')),
tooltip = ['mean(y)','mean(x)']
)
timeseries = base.mark_bar(opacity=1).encode(
x=alt.X('time', title='Items'),
y=alt.Y('value', scale=alt.Scale(domain=(-1, 1)),stack=None),
color=alt.Color('id:O',scale=alt.Scale(domain=domain, range=range_))
#, legend=None)
).transform_filter(
selector
)
points | timeseries
No problem at this stage even if it could be useful to hide all the bars on right chart when no selection is made on the right chart (don't know if it's possible ?)
After that I try to add text to the scatter plot adding this at the end of the code :
text = points.mark_text(dy=-5).encode(
x=alt.X('mean(y)',title='Durée de perception',scale=alt.Scale(domain=(11, 23))),
y=alt.Y('mean(x)',title='NBV (%CA)'),
text='id:O'
)
(points + text) | timeseries
which leads to the following error message :
Javascript Error: Duplicate signal name: "selector094_tuple"
This usually means there's a typo in your chart specification. See the javascript console for the full traceback.
If you have any idea on how to do, i would be grateful
Thanks
The issue is that you cannot add the same selection to two different layers, which you do implicitly by deriving text from points. Try this instead:
text = alt.Chart(data).mark_text(dy=-5).encode(
x=alt.X('mean(y)',title='Durée de perception',scale=alt.Scale(domain=(11, 23))),
y=alt.Y('mean(x)',title='NBV (%CA)'),
text='id:O'
)
(points + text) | timeseries
Related
My DataFrame looks similar to this:
name
reached points
Jose Laderman
13
William Kane
13
I am currently displaying the aggregated count of students reached points of an assignment on an Altair bar chart within Streamlit like this:
brush = alt.selection(type='interval', encodings=['x'])
interactive_test = alt.Chart(df_display_all).mark_bar(opacity=1, width=5).encode(
x= alt.X('reached points', scale=alt.Scale(domain=[0, maxPoints])),
y=alt.Y('count()', type='quantitative', axis=alt.Axis(tickMinStep=1), title='student count'),
).properties(width=1200)
upper = interactive_test.encode(
alt.X('reached points', sort=alt.EncodingSortField(op='count', order='ascending'), scale=alt.Scale(domain=brush, domainMin=-0.5))
)
lower = interactive_test.properties(
height=60
).add_selection(brush)
concat_distribution_interactive = alt.vconcat(upper, lower)
Which produces this output and everything looks fine
The information I want my tooltip to show is a list of students that reached the specific amounts of reached points I'm hovering over. When adding something like:
tooltip='name'
the way my bar chart seems to display values has now been altered to this
When adding something like
tooltip='reached points'
The data seems to be displayed normally but without a tooltip that gives me the necessary information. Is it possible to display tooltip data that isn't used in my x or y axis but still part of the DataFrame I'm putting into the chart?
I was playing around with the following example from the Altair Gallery:
https://altair-viz.github.io/gallery/airports_count.html
As of right now, the only way to display the actual count appears to be via the tooltip, as the example shows. However, I am trying to code a static visualization for which it would be very helpful if the exact value was displayed right next to the mark itself, without the user having to hover or interact in any way. Is there a way to achieve this?
You can do this by manually calculating offsets for text labels, though this is admittedly difficult when the points become crowded:
import altair as alt
from vega_datasets import data
airports = data.airports.url
states = alt.topo_feature(data.us_10m.url, feature='states')
# US states background
background = alt.Chart(states).mark_geoshape(
fill='lightgray',
stroke='white'
).properties(
width=500,
height=300
).project('albersUsa')
# airport positions on background
base = alt.Chart(airports).transform_aggregate(
latitude='mean(latitude)',
longitude='mean(longitude)',
count='count()',
groupby=['state']
).encode(
longitude='longitude:Q',
latitude='latitude:Q',
)
points = base.mark_circle().encode(
size=alt.Size('count:Q', title='Number of Airports'),
color=alt.value('steelblue'),
tooltip=['state:N','count:Q']
).properties(
title='Number of airports in US'
)
text = base.mark_text(
dx=15, dy=10
).encode(
text='count:Q'
)
background + points + text
Long-term, a better solution will be to use vega-label, which will be able to do this automatically once it's part of the Vega-Lite package. For Altair, this feature is tracked in this bug: https://github.com/altair-viz/altair/issues/1731
I’m new to plotly and I’m creating a gantt chart using px.timeline. There are 3 categories of data in my dataset, a normal task with a start and end time, and two types of task where the start and end time are same. I want the normal task to be a rectangle (which is how it is being plot) and the other two tasks to have a hourglass marker and a triangle marker instead of a very thin line ?
This is how my data looks :
data = [dict(Task=’’, start=’’, end=’’, shape=’<rect, hour, tri>’)]
Sample Data :
df = [dict(Task="Job A", Start='2009-01-01', Finish='2009-01-01', shape='hourglass'),
dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15', shape='rectangle'),
dict(Task="Job C", Start='2009-05-30', Finish='2009-05-30', shape='triangle')]
Code :
fig = px.timeline(data, x_start="Start", x_end="Finish", y="Task")
fig.update_yaxes(autorange="reversed", ticklabelposition="outside left")
fig.update_layout(showlegend=False, height=2000, width=1255, margin_pad=10)
fig.show()
Example:
Sample Plot in Excel
Is there any way I can achieve this ?
Thanks !
I solved this after some hours of searching.
Split the data into three each corresponding to 3 different shapes and then plot and combine.
Create 3 individual Plots :
rect = px.timeline(rect, x_start="Start", x_end="Finish", y="Task", color="color")
dia = px.scatter(dia, x="Start", y="Task", color="color", symbol_sequence=['diamond'])
coll = px.scatter(coll, x="Start", y="Task", color="color", symbol_sequence=['hourglass'])
Update traces for individual plots if needed :
rect.update_traces(marker=dict(line=dict(width=1, color='black')))
dia.update_traces(marker=dict(size=12, line=dict(width=2)))
coll.update_traces(marker=dict(size=12, line=dict(width=2)))
Set the timeline plot's axis:
rect.update_xaxes(tickformat="%H:%M:%S.%L", tickmode='linear', dtick='120000')
rect.update_yaxes(autorange='reversed')
rect.update_layout(title=title, showlegend=False, height=2800, width=2000)
Overlay all Plots:
new_fig = go.Figure(data=rect.data + dia.data + coll.data, layout=rect.layout)
new_fig.show()
Just like we can highlight point(s) in one of the charts of a concatenated chart or a faceted chart and the corresponding point(s) will also get highlighted in the other chart, I was wondering if the same can be done with a tooltip.
I have been able to come up with a demo using mark_text as you can see below. But the biggest challenge is not being able to show multiple encodings as text. Tooltips make that really easy by just mentioning all the encodings in a list. So I was thinking if there is a way to do that in Altair.
Or is this not the expected behavior of Tooltips, and they are ONLY supposed to highlight the point over which the mouse is hovering even if other charts may have a common selection?
Whatever I have tried with Tooltips only works in a single chart, the other point despite being highlighted does not also show a tooltip.
Code:
import altair as alt
from vega_datasets import data
source = data.cars()
highlight = alt.selection(type='single', on="mouseover", empty='none')
base = alt.Chart(source).encode(
y='Miles_per_Gallon',
color=alt.condition(highlight, 'Origin', alt.ColorValue('gray')),
)
mpg = base.mark_point().encode(
x='Horsepower',
size=alt.condition(highlight, alt.value(150), alt.value(50))).add_selection(
highlight
)
acc = base.mark_point().encode(
x='Acceleration',
size=alt.condition(highlight, alt.value(150), alt.value(50))).add_selection(
highlight
)
text_mpg = base.mark_text(dx=5, dy=-10, size=15).encode(
x='Horsepower',
y='Miles_per_Gallon',
text=alt.condition(highlight, 'Horsepower:Q', alt.value('')),
)
text_acc = base.mark_text(dx=5, dy=-10, size=15).encode(
x='Acceleration',
y='Miles_per_Gallon',
text=alt.condition(highlight, 'Acceleration:Q', alt.value('')),
)
(mpg+text_mpg)|(acc+text_acc)
Demo:
Expected Output:
Show tooltips instead of text, with multiple encodings
I am however starting to feel that this is not the intended behavior of tooltip.
I'm trying to create a chart somewhat along the lines of the Multi-Line Tooltip example, but I'd like to format the string that is being printed to have some text added at the end. I'm trying to modify this part:
# 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, 'y:Q', alt.value(' '))
)
Specifically, rather than 'y:Q' I want something along the lines of 'y:Q' + " suffix". I've tried doing something like this:
# 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, 'y:Q', alt.value(' '), format=".2f inches")
)
Alternatively, I've tried:
# Draw text labels near the points, and highlight based on selection
y_fld = 'y'
text = line.mark_text(align='left', dx=5, dy=-5).encode(
text=alt.condition(nearest, f"{y_fld:.2f} inches", alt.value(' '))
)
I think I see why those don't work, but I can't figure out how to intercept the value of y and pass it through a format string. Thanks!
I think the easiest way to do this is to calculate a new field using transform_calculate to compute the label that you want.
Using the example from the documentation, I would change the text chart like this:
text = line.mark_text(align='left', dx=5, dy=-5).encode(
text=alt.condition(nearest, 'label:N', alt.value(' '))
).transform_calculate(label='datum.y + " inches"')
That leads to this chart:
If you want more control, you could change the dataset with pandas beforhand. Be sure to set the type to Nominal (and not Quantitative) otherwise you would get NaNs in the tooltips.