Altair interactive plots become static on touch screen devices - altair

I made an Altair interactive plot which contains several subplots with cross-filtering and save it as an html file.
When opened on computer browsers, everything works fine. But on my phone and tablet, the plot becomes static (both Android Chrome and Firefox).
Is there a way to make it work on these platforms?
Reproducible code
import altair as alt
from vega_datasets import data
cars = data.cars()
interval = alt.selection_interval()
base = alt.Chart(cars).mark_point().encode(
y='Horsepower',
color=alt.condition(interval, 'Origin', alt.value('lightgray')),
tooltip='Name'
).add_selection(
interval
)
hist = alt.Chart(cars).mark_bar().encode(
x='count()',
y='Origin',
color='Origin'
).properties(
width=800,
height=80
).transform_filter(
interval
)
scatter = base.encode(x='Miles_per_Gallon') | base.encode(x='Acceleration')
chart = scatter & hist
chart.display(renderer='svg')
chart.save('chart.html', scale_factor=3)
<iframe src="https://chart.tiiny.site/" style="width: 100%; height: 1000px;">
</iframe>

This is an open issue in Vega-Lite, the library used to render Altair charts: https://github.com/vega/vega-lite/issues/4661
That issue has a few suggestions for workarounds related to interactions via touchscreens, but there is not yet any full solution to your question.

Related

Is there a way to show Altair Tooltip all the time?

I like the way tooltip looks way more than when I add text as labels for the points in my plot, is there a way to make it visible wether the mouse is on it or not?
I looked it up but haven't found any solutions, maybe with messing around with conditions?
example code from doc if you have ideas you'd like to test out :
import altair as alt
from vega_datasets import data
source = data.cars()
alt.Chart(source).mark_circle(size=60).encode(
x='Horsepower',
y='Miles_per_Gallon',
color='Origin',
tooltip=['Name', 'Origin', 'Horsepower', 'Miles_per_Gallon']
).interactive()
thank u :)
As per my comment above, I think the easiest way to do this is adding a styled text box. You can see an example of how to style it in this issue, which I also pasted below:
import altair as alt
from vega_datasets import data
cars = data.cars()
chart = alt.Chart(cars).mark_circle().encode(
alt.X('Miles_per_Gallon', scale=alt.Scale(domain=(5,50))),
y='Weight_in_lbs')
corl = cars[['Miles_per_Gallon','Weight_in_lbs']].corr().iloc[0,1]
text = alt.Chart({'values':[{}]}).mark_text(
align="left", baseline="top"
).encode(
x=alt.value(5), # pixels from left
y=alt.value(5), # pixels from top
text=alt.value([f"r: {corl:.3f}", 'Line 2']))
box = alt.Chart({'values':[{}]}).mark_rect(stroke='black', color='orange').encode(
x=alt.value(3),
x2=alt.value(50),
y=alt.value(3),
y2=alt.value(30))
chart + box + text + chart.transform_regression('Miles_per_Gallon','Weight_in_lbs').mark_line()

Is it possible to display one faceted chart in Altair at a time and toggle between different charts?

I'm working on a project for a class where I've been creating faceted scatterplots in Altair where each chart is a different type of food. I was wondering if it would be possible to still use these faceted charts but only display one at a time and give the user the ability to toggle between graphs instead of having each graph displayed one after the other?
Here is a rough diagram of what I'm trying to do and here's the code I have now (and what the output looks like at the moment):
import altair as alt
from vega_datasets import data
hover = alt.selection_single(on='mouseover', nearest=True, empty='none')
base = alt.Chart("Food Nutrition Info Compiled.csv").encode(
x='Energ_Kcal:N',
y='Water_(g):Q',
color=alt.condition(hover, 'Type:N', alt.value('lightgray'))
).properties(
width=180,
height=180,
)
points = base.mark_point().add_selection(
hover
)
text = base.mark_text(dy=-5).encode(
text = 'Shrt_Desc:N',
opacity = alt.condition(hover, alt.value(1), alt.value(0))
)
alt.layer(points, text).facet(
'Type:N',
)
Thanks and I hope this makes sense!
Displaying one faceted chart at a time sounds the same as showing a single subset of the data at any one point. This is currently possible by creating a selection that is bound to e.g. a dropdown and the using that with transform_filter:
import altair as alt
from vega_datasets import data
source = data.cars()
dropdown_options = source['Origin'].unique().tolist()
dropdown = alt.binding_select(
options=dropdown_options,
name='Origin '
)
selection = alt.selection_multi(
fields=['Origin'],
value=[{'Origin': dropdown_options[0]}],
bind=dropdown
)
alt.Chart(source).mark_circle().encode(
x=alt.X('Weight_in_lbs:Q', title=''),
y='Horsepower',
color='Origin',
).add_selection(
selection
).transform_filter(
selection
)

Is there a way to display the value of a mark next to the mark in Altair

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

Is there a way to make a bubble plot in Altair which has circles that aren't filled?

I am trying to develop a new data visualization/graphic, and the bubble plot available here is very similar to what I am trying to make in shape:
https://altair-viz.github.io/gallery/table_bubble_plot_github.html
However, the graph I am trying to make involves some shaded bubbles and some filled in. Is there a way to edit this graph so that the bubble marks are not always filled?
Thank you!
You could use the fillOpacity encoding linked to a field in your data and then set the domain and range of its scale, so that only the values you want have a completely transparent fill:
import altair as alt
from vega_datasets import data
source = data.github.url
fill_threshold = 12
alt.Chart(source).mark_circle(
stroke='black'
).encode(
x='hours(time):O',
y='day(time):O',
size='sum(count):Q',
fillOpacity=alt.FillOpacity(
'sum(count):Q',
scale=alt.Scale(
domain=[fill_threshold, fill_threshold + 0.01],
range=[0 ,1]
)
)
)
You can use the fillOpacity and stroke mark properties to make the marks into circles with no fill. For example:
import altair as alt
from vega_datasets import data
source = data.github.url
alt.Chart(source).mark_circle(
fillOpacity=0,
stroke='black'
).encode(
x='hours(time):O',
y='day(time):O',
size='sum(count):Q',
)

Set opacity for marks but not in legend

I can modify the classic Simple Scatter Plot with Tooltips, to add opacity to marks, but I'd like to legend colors to stay 100% opaque. In the chart I'm trying to make, I have a df with tens of thousands of rows.
import altair as alt
from vega_datasets import data
source = data.cars()
alt.Chart(source).mark_circle(size=60, opacity=0.1).encode(
x='Horsepower',
y='Miles_per_Gallon',
color='Origin'
)
I've tried alt.Legend's symbolOpacity and gradientOpacity to no avail
color=alt.Color(
'Origin:N',
legend=alt.Legend(
# symbolOpacity=1,
gradientOpacity=1,
)
)
As of now, this seems to be a bug with vega-lite. Per #jvp's suggestion, I've filed a bug report here
UPDATE -- IT'S FIXED

Resources