Retain tooltip of previous layer in Altair - python
I am adding states outline overlay on my choropleth plot in altair
My choropleth plot has tooltip for it.
When I layer the state outline on top of the choropleth I lose the tooltip feature of the plot
Anyone have ideas on how to handle this?
Any help would be appreciated
import altair as alt
# saving data into a file rather than embedding into the chart
alt.data_transformers.enable('json')
alt.renderers.enable('notebook')
# alt.renderers.enable('jupyterlab')
from vega_datasets import data
import pandas as pd
from altair import Scale,Color
states = alt.topo_feature(data.us_10m.url, 'states')
counties = alt.topo_feature(data.us_10m.url+'#', 'counties')
dummy='#dbe9f6'
scheme='blues'
type1='linear'
fg = alt.Chart(counties).mark_geoshape(
stroke='black',
strokeWidth=0.05
).project(
type='albersUsa'
).transform_lookup(
lookup='id',
from_=alt.LookupData(fdf, 'fips', ['year','Pill_per_pop','BUYER_COUNTY', 'state'])
).transform_calculate(
Pill_per_pop='isValid(datum.Pill_per_pop) ? datum.Pill_per_pop : -1'
).encode(
color = alt.condition(
'datum.Pill_per_pop > 0',
alt.Color('Pill_per_pop:Q', scale=Scale(scheme=scheme,type=type1)),
alt.value(dummy)
),
tooltip=['BUYER_COUNTY:N', 'state:N','Pill_per_pop:Q','year:Q']
).properties(
width=700,
height=400,
title='Pills per 100k people'
)
outline = alt.Chart(states).mark_geoshape(stroke='black',strokeWidth=0.2).project(
type='albersUsa'
)
fg+outline
My output
However I am unable to find a way to retain the tooltip of the previous layer i.e. county level map
I figured it out
Define a new chart as follows:
fg1 = alt.Chart(counties).mark_geoshape(
stroke='black',
strokeWidth=0.05
).project(
type='albersUsa'
).transform_lookup(
lookup='id',
from_=alt.LookupData(fdf, 'fips', ['year','Pill_per_pop','BUYER_COUNTY', 'state'])
).transform_calculate(
Pill_per_pop='isValid(datum.Pill_per_pop) ? datum.Pill_per_pop : -1'
).encode(
tooltip=['BUYER_COUNTY:N', 'state:N','Pill_per_pop:Q','year:Q']
).properties(
width=700,
height=400,
title='Pills per 100k people'
)
fg+outline+fg1
Just define the chart again with encoding for tool-tip and not for color and layer it on top of the above chart.
Related
Remove legend for points in Altair
I'm following this example https://altair-viz.github.io/gallery/multiline_highlight.html, and added text points. My lines have both strokeDash and color. import altair as alt from vega_datasets import data source = data.stocks() highlight = alt.selection(type='single', on='mouseover', fields=['symbol'], nearest=True) base = alt.Chart(source).encode( x='date:T', y='price:Q', color='symbol:N', strokeDash='symbol:N' ) points = base.mark_circle().encode( opacity=alt.value(0) ).add_selection( highlight ).properties( width=600 ) lines = base.mark_line().encode( size=alt.condition(~highlight, alt.value(1), alt.value(3)) ) points + lines I'd like the legend to only show the dashed and colored lines, not the others (legend for text and points iiuc) Is it possible to completely remove the extra legends from a chart?
It is enough to explicitly remove the legend on points and text by setting the color and strokeDash attributes import altair as alt from vega_datasets import data source = data.stocks() highlight = alt.selection(type='single', on='mouseover', fields=['symbol'], nearest=True) base = alt.Chart(source).encode( x='date:T', y='price:Q', color='symbol:N', strokeDash='symbol:N' ) points = base.mark_point().encode( opacity=alt.value(0), color=alt.Color('symbol:N', legend=None), strokeDash=alt.StrokeDash('symbol:N', legend=None) ).add_selection( highlight ).properties( width=600 ) lines = base.mark_line().encode( size=alt.condition(~highlight, alt.value(1), alt.value(3)) ) text = lines.mark_text( align='left', baseline='middle', dx=7 ).encode( text='symbol', color=alt.Color('symbol:N', legend=None), strokeDash=alt.StrokeDash('symbol:N', legend=None) ) (lines + points + text).resolve_scale(color='independent', strokeDash='independent')
Parallel coordinates in Altair
I want to do a parallel coordinates plot with multiple y axis. I've found how to do it in Vega-Lite here but I haven't found the way to do it with Altair, there's only a very simple example where all the y axis are the same. Is there any way to do this plot in altair?
Note that this kind of chart is not "built-in" to Altair or Vega-Lite, so the only way to create it is with a manual sequence of transforms, and manually constructing your axes from tick and text marks. Here is an Altair version of the chart in the answer you linked to: import altair as alt from vega_datasets import data base = alt.Chart( data.iris.url ).transform_window( index="count()" ).transform_fold( ["petalLength", "petalWidth", "sepalLength", "sepalWidth"] ).transform_joinaggregate( min="min(value)", max="max(value)", groupby=["key"] ).transform_calculate( norm_val="(datum.value - datum.min) / (datum.max - datum.min)", mid="(datum.min + datum.max) / 2" ).properties(width=600, height=300) lines = base.mark_line(opacity=0.3).encode( x='key:N', y=alt.Y('norm_val:Q', axis=None), color="species:N", detail="index:N", tooltip=["petalLength:N", "petalWidth:N", "sepalLength:N", "sepalWidth:N"] ) rules = base.mark_rule( color="#ccc", tooltip=None ).encode( x="key:N", detail="count():Q", ) def ytick(yvalue, field): scale = base.encode(x='key:N', y=alt.value(yvalue), text=f"min({field}):Q") return alt.layer( scale.mark_text(baseline="middle", align="right", dx=-5, tooltip=None), scale.mark_tick(size=8, color="#ccc", orient="horizontal", tooltip=None) ) alt.layer( lines, rules, ytick(0, "max"), ytick(150, "mid"), ytick(300, "min") ).configure_axisX( domain=False, labelAngle=0, tickColor="#ccc", title=None ).configure_view( stroke=None )
How to apply borders around each y axis label for Altair in Python
I have a visualization I made in Altair and I want to place borders around each label on the y axis (sorry if I am explaining this incorrectly) to separate them. This is the code I have so far: alt.Chart(q4df).transform_fold( rosspaints, as_=['column', 'value'] ).mark_circle().encode( x = alt.X('column:N', axis=None), y = alt.Y('TITLE', title=None), size = alt.Size('value:Q', legend=None), color=alt.Color('column:N', legend=None, scale=alt.Scale( domain=['alizarin crimson','bright red','burnt umber','cadmium yellow','dark sienna', 'indian yellow','indian red','liquid black','liquid clear','black gesso', 'midnight black','phthalo blue','phthalo green','prussian blue','sap green', 'titanium white','van dyke brown','yellow ochre'], range=['#94261f','#c06341','#614f4b','#f8ed57','#5c2f08','#e6ba25','#cd5c5c', '#000000','#ffffff','#000000','#36373c','#2a64ad','#215c2c','#325fa3', '#364e00','#f9f7eb','#2d1a0c','#b28426'])) ).properties( width=400, height=700 ).configure_axis(grid=False, labelFontWeight= 'bold', labelColor='black') This is my current output: This is my desired output:
You could either set a gridline for each y-axis tick like this: import altair as alt from vega_datasets import data source = data.barley() alt.Chart(source).mark_point().encode( alt.X('yield:Q', axis=alt.Axis(grid=False)), alt.Y('variety:N', axis=alt.Axis(grid=True)), color='year:N' ).configure_view( stroke=None ) Or use facet according to the same variable you have encoded on the y-axis while resolving the y-scale so that only the y-axis entry with data points shows up in each plot: alt.Chart(source).mark_point().encode( alt.X('yield:Q', axis=alt.Axis(grid=False)), alt.Y('variety:N', title=''), color='year:N', ).facet( row=alt.Facet('variety:N', title='', header=alt.Header(labels=False)) ).resolve_scale( y='independent' )
How to color lines on mouseover in a bump chart using Altair Viz?
The goal is to highlight the entire line when hovering anywhere (not just at the data points) on the line. Imports: from IPython.display import display import pandas as pd import altair as alt Data: data = '{"Date":{"5":1560643200000,"18":1560643200000,"22":1560643200000,"24":1560643200000,"59":1560643200000,"65":1561248000000,"78":1561248000000,"82":1561248000000,"84":1561248000000,"119":1561248000000,"125":1561852800000,"138":1561852800000,"142":1561852800000,"144":1561852800000,"179":1561852800000,"185":1562457600000,"198":1562457600000,"202":1562457600000,"204":1562457600000,"239":1562457600000,"245":1563062400000,"258":1563062400000,"262":1563062400000,"264":1563062400000,"299":1563062400000,"305":1563667200000,"318":1563667200000,"322":1563667200000,"324":1563667200000,"359":1563667200000,"365":1564272000000,"378":1564272000000,"382":1564272000000,"384":1564272000000,"419":1564272000000,"425":1564876800000,"438":1564876800000,"442":1564876800000,"444":1564876800000,"479":1564876800000,"485":1565481600000,"498":1565481600000,"502":1565481600000,"504":1565481600000,"539":1565481600000,"545":1566086400000,"558":1566086400000,"562":1566086400000,"564":1566086400000,"599":1566086400000,"605":1566691200000,"618":1566691200000,"622":1566691200000,"624":1566691200000,"659":1566691200000,"665":1567296000000,"678":1567296000000,"682":1567296000000,"684":1567296000000,"719":1567296000000,"725":1567900800000,"738":1567900800000,"742":1567900800000,"744":1567900800000,"779":1567900800000,"785":1568505600000,"798":1568505600000,"802":1568505600000,"804":1568505600000,"839":1568505600000,"845":1569110400000,"858":1569110400000,"862":1569110400000,"864":1569110400000,"899":1569110400000,"905":1569715200000,"918":1569715200000,"922":1569715200000,"924":1569715200000,"959":1569715200000,"965":1570320000000,"978":1570320000000,"982":1570320000000,"984":1570320000000,"1019":1570320000000,"1025":1570924800000,"1038":1570924800000,"1042":1570924800000,"1044":1570924800000,"1079":1570924800000,"1085":1571529600000,"1098":1571529600000,"1102":1571529600000,"1104":1571529600000,"1139":1571529600000,"1145":1572134400000,"1158":1572134400000,"1162":1572134400000,"1164":1572134400000,"1199":1572134400000,"1205":1572739200000,"1218":1572739200000,"1222":1572739200000,"1224":1572739200000,"1259":1572739200000,"1265":1573344000000,"1278":1573344000000,"1282":1573344000000,"1284":1573344000000,"1319":1573344000000,"1325":1573948800000,"1338":1573948800000,"1342":1573948800000,"1344":1573948800000,"1379":1573948800000,"1385":1574553600000,"1398":1574553600000,"1402":1574553600000,"1404":1574553600000,"1439":1574553600000,"1445":1575158400000,"1458":1575158400000,"1462":1575158400000,"1464":1575158400000,"1499":1575158400000,"1505":1575763200000,"1518":1575763200000,"1522":1575763200000,"1524":1575763200000,"1559":1575763200000,"1565":1576368000000,"1578":1576368000000,"1582":1576368000000,"1584":1576368000000,"1619":1576368000000},"Store":{"5":"store1","18":"store2","22":"store3","24":"store4","59":"store5","65":"store1","78":"store2","82":"store3","84":"store4","119":"store5","125":"store1","138":"store2","142":"store3","144":"store4","179":"store5","185":"store1","198":"store2","202":"store3","204":"store4","239":"store5","245":"store1","258":"store2","262":"store3","264":"store4","299":"store5","305":"store1","318":"store2","322":"store3","324":"store4","359":"store5","365":"store1","378":"store2","382":"store3","384":"store4","419":"store5","425":"store1","438":"store2","442":"store3","444":"store4","479":"store5","485":"store1","498":"store2","502":"store3","504":"store4","539":"store5","545":"store1","558":"store2","562":"store3","564":"store4","599":"store5","605":"store1","618":"store2","622":"store3","624":"store4","659":"store5","665":"store1","678":"store2","682":"store3","684":"store4","719":"store5","725":"store1","738":"store2","742":"store3","744":"store4","779":"store5","785":"store1","798":"store2","802":"store3","804":"store4","839":"store5","845":"store1","858":"store2","862":"store3","864":"store4","899":"store5","905":"store1","918":"store2","922":"store3","924":"store4","959":"store5","965":"store1","978":"store2","982":"store3","984":"store4","1019":"store5","1025":"store1","1038":"store2","1042":"store3","1044":"store4","1079":"store5","1085":"store1","1098":"store2","1102":"store3","1104":"store4","1139":"store5","1145":"store1","1158":"store2","1162":"store3","1164":"store4","1199":"store5","1205":"store1","1218":"store2","1222":"store3","1224":"store4","1259":"store5","1265":"store1","1278":"store2","1282":"store3","1284":"store4","1319":"store5","1325":"store1","1338":"store2","1342":"store3","1344":"store4","1379":"store5","1385":"store1","1398":"store2","1402":"store3","1404":"store4","1439":"store5","1445":"store1","1458":"store2","1462":"store3","1464":"store4","1499":"store5","1505":"store1","1518":"store2","1522":"store3","1524":"store4","1559":"store5","1565":"store1","1578":"store2","1582":"store3","1584":"store4","1619":"store5"},"Rank":{"5":1.0,"18":1.0,"22":1.0,"24":1.0,"59":1.0,"65":2.0,"78":2.0,"82":2.0,"84":2.0,"119":2.0,"125":2.0,"138":2.0,"142":2.0,"144":2.0,"179":2.0,"185":2.0,"198":2.0,"202":2.0,"204":2.0,"239":2.0,"245":2.0,"258":2.0,"262":2.0,"264":2.0,"299":2.0,"305":2.0,"318":2.0,"322":2.0,"324":2.0,"359":2.0,"365":2.0,"378":2.0,"382":2.0,"384":1.0,"419":2.0,"425":3.0,"438":1.0,"442":3.0,"444":2.0,"479":3.0,"485":4.0,"498":1.0,"502":4.0,"504":3.0,"539":4.0,"545":4.0,"558":1.0,"562":3.0,"564":3.0,"599":4.0,"605":5.0,"618":1.0,"622":2.0,"624":4.0,"659":5.0,"665":6.0,"678":1.0,"682":2.0,"684":5.0,"719":6.0,"725":7.0,"738":1.0,"742":2.0,"744":5.0,"779":7.0,"785":8.0,"798":1.0,"802":2.0,"804":6.0,"839":8.0,"845":8.0,"858":1.0,"862":2.0,"864":5.0,"899":8.0,"905":8.0,"918":1.0,"922":2.0,"924":4.0,"959":8.0,"965":8.0,"978":1.0,"982":2.0,"984":4.0,"1019":8.0,"1025":10.0,"1038":1.0,"1042":2.0,"1044":5.0,"1079":10.0,"1085":10.0,"1098":1.0,"1102":2.0,"1104":5.0,"1139":10.0,"1145":11.0,"1158":1.0,"1162":2.0,"1164":5.0,"1199":11.0,"1205":12.0,"1218":1.0,"1222":2.0,"1224":6.0,"1259":12.0,"1265":13.0,"1278":2.0,"1282":1.0,"1284":7.0,"1319":13.0,"1325":13.0,"1338":2.0,"1342":1.0,"1344":6.0,"1379":13.0,"1385":14.0,"1398":2.0,"1402":1.0,"1404":6.0,"1439":14.0,"1445":3.0,"1458":2.0,"1462":1.0,"1464":6.0,"1499":8.0,"1505":3.0,"1518":2.0,"1522":1.0,"1524":6.0,"1559":4.0,"1565":3.0,"1578":2.0,"1582":1.0,"1584":8.0,"1619":5.0}}' Dataframe: df_slim = pd.read_json(data) Chart: highlight = alt.selection(type='single', on='mouseover', fields=['Store'], nearest=True, empty="none") chart = alt.Chart(df_slim).mark_line().encode( x='Date', y='Rank', #color='Store', strokeDash='Store', color=alt.condition(highlight, 'Store', alt.value("lightgray")), tooltip=['Rank','Store'] ).properties( width=800, height=600, title='Bump Chart: Store Ranking' ).configure_title( fontSize=30, font='Courier', anchor='start', color='gray' ).add_selection( highlight ) display(chart) Output: Any help? Not sure what went wrong here.
Good question! It turns out this is one of the current limitations of Vega-Lite. I found this note in the VL docs on Nearest Value The nearest transform is not supported for continuous mark types (i.e., line and area). For these mark types, consider layering a discrete mark type (e.g., point) with a 0-value opacity So for your example I would do something like this highlight = alt.selection(type='single', on='mouseover', fields=['Store'], nearest=True, empty="none") chart = alt.Chart(df_slim).mark_line().encode( x='Date', y='Rank', #color='Store', strokeDash='Store', color=alt.condition(highlight, 'Store', alt.value("lightgray")), tooltip=['Rank','Store'] ) points = alt.Chart(df_slim).mark_point(opacity=0).encode( x='Date', y='Rank',).add_selection( highlight ) chart + points
Labelling Layered Charts in Altair (Python)
I am attempting to create two layered histograms in Altair (and a vertical mean ruler for each). I would like a legend to label each of these four. I am using the first 'Birth weight I' data that can be found here My code (real long, apologies) looks something like this: from altair import datum # This histogram for baby weights of mothers who dont smoke dont = alt.Chart(babyData).mark_bar().encode( alt.X("bwt-oz:Q", axis=alt.Axis(title='Birth Weight (Ounces)'), bin=True), alt.Y('count()', axis=alt.Axis(title='Count'), scale=alt.Scale(domain=[0, 350])) ).properties( width=400, height=400 ).transform_filter( datum.smoke == 0, ) mean = alt.Chart(babyData).mark_rule(color='red').encode( x='mean(bwt-oz):Q', size=alt.value(4) ).transform_filter( datum.smoke == 0 ) dontSmokeChart = dont + mean # This histogram for baby weights of mothers who smoke do = alt.Chart(babyData).mark_bar().encode( alt.X("bwt-oz:Q", axis=alt.Axis(title='Birth Weight (Ounces)'), bin=True), alt.Y('count()', axis=alt.Axis(title='Count'), scale=alt.Scale(domain=[0, 350])) ).transform_filter( datum.smoke == 1 ).properties( width=400, height=400 ) mean2 = alt.Chart(babyData).mark_rule(color='red').encode( x='mean(bwt-oz):Q', size=alt.value(4) ).transform_filter( datum.smoke == 1 ) doSmokeChart = do + mean2 # This layers, and puts them all together layer = alt.layer( dont, mean, do, mean2 ).properties( title="Layered Histogram of Baby Weights of Mothers Who smoke Vs. Who Don't", ).configure_mark( opacity=0.5, color='blue', ) layer The final layered chart looks something like this: I would simply like a legend specifying which histogram/mean belongs to what. If I could color them too, and perhaps add a legend that way, that would be nice as well, but I am unsure how to do so. Thanks for any insight!
Rather than manually creating layers with filtered data, you should use a color encoding on your full dataset: then a legend will be generated automatically. For example: import altair as alt import pandas as pd babyData = pd.read_csv('https://www.stat.berkeley.edu/users/statlabs/data/babiesI.data', delim_whitespace=True) base = alt.Chart(babyData).transform_filter( 'datum.smoke != 9' ) hist = base.mark_bar(opacity=0.5).encode( alt.X("bwt:Q",title='Birth Weight (Ounces)', bin=True), alt.Y('count()', title='Count'), color='smoke:N' ).properties( width=400, height=400 ) mean = base.mark_rule().encode( x='mean(bwt):Q', size=alt.value(4), color='smoke:N' ) hist + mean From there you could use standard approaches to Customize the color schemes used for each mark.
#jakevdp just beat me to it! I was going to say the same thing. Here is a full example for you to work with. import pandas as pd import altair as alt # Link to data source URL = 'https://www.stat.berkeley.edu/users/statlabs/data/babiesI.data' # Read data into a pandas dataframe df = pd.read_table(URL, sep='\s+') hist = alt.Chart(df).mark_area( opacity=0.7, interpolate='step' ).encode( alt.X("bwt:Q", axis=alt.Axis(title='Birth Weight (Ounces)'), bin=True), alt.Y('count()', axis=alt.Axis(title='Count'), stack=None), alt.Color('smoke:N') ).properties( width=400, height=400 ).transform_filter(alt.datum.smoke != 9) rule = alt.Chart(df).mark_rule(color='red').encode( alt.Detail('smoke:N'), alt.Color('smoke:N'), alt.X('mean(bwt):Q'), size=alt.value(4), ).transform_filter(alt.datum.smoke != 9) hist + rule