Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

World map + slider in Altair (Python)

I would like to build an interactive chart with world map with COVID-19 confirmed cases per country and date slider using Altair library in python.

Data format:

"country_region","date","confirmed_cases"
"Afghanistan",2020-01-22,0
"Afghanistan",2020-01-23,0
"Afghanistan",2020-01-24,0
"Afghanistan",2020-01-25,0

I have managed to build non-interactive chart, but can't set up slider. My chart now looks like this: enter image description here

I want to add slider to chose date, but can't set up it properly.

I use transform_lookup method to add colors to countries according to the data (confirmed cases / population) and transform_filter as far as I understand should be added to perform data filtering when slider position is changed by user. But this doesn't work. My assumption is that in transform_lookup always original data is used and filtering doesn't work there. I haven't found examples or documentation about using transform_lookup and slider at the same time.

Would be grateful to hear any ideas what can help me with this problem.

Code:

import requests
import json

import pandas as pd

import altair as alt
from vega_datasets import data
from altair import datum

df = pd.read_csv('https://raw.githubusercontent.com/MariaKokshaikina/any-data/main/covid19_global_confirmed_cases%20(1).csv')

country_info = requests.get(
    'https://raw.githubusercontent.com/MariaKokshaikina/any-data/main/country_info.json'
).json()

df = df[df['country_region'].isin(country_info)]
df = df.sort_values('date', ascending=True)
df = df.tail(5000)

def timestamp(t):
    return pd.to_datetime(t).timestamp() * 1000

df['id'] = df['country_region'].map(lambda x: country_info[x]['numericCode'])
df['rate'] = df['confirmed_cases'] / df['country_region'].map(lambda x: country_info[x]['population'])
df['timestamp'] = df['date'].map(timestamp)

countries = alt.topo_feature(data.world_110m.url, 'countries')

slider = alt.binding_range(
    step=24 * 60 * 60 * 1000,
    min=df['timestamp'].min(), 
    max=df['timestamp'].max()
)

select_date = alt.selection_single(
    name="slider", 
    fields=['timestamp'],
    bind=slider, 
)

alt.Chart(countries).mark_geoshape()\
    .encode(color='rate:Q')\
    .add_selection(select_date)\
    .transform_filter(select_date)\
    .transform_lookup(
        lookup='id',
        from_=alt.LookupData(df, key='id', fields=['rate'])
    )\
    .project('equirectangular')\
    .properties(
        width=500,
        height=300,
        title='Title'
    )

  • You can remove .transform_filter(select_date)\ line to see working map with slider which doesn't change anything.
like image 662
maria Avatar asked Nov 08 '25 02:11

maria


2 Answers

The problem appears to be that in Vega, lookup transforms do not dynamically recompute in response to selections. You can address this by switching which is the primary data source, so that all timestamps appear in the final joined dataset:

alt.Chart(df).mark_geoshape()\
    .encode(color='rate:Q')\
    .add_selection(select_date)\
    .transform_filter(select_date)\
    .transform_lookup(
        lookup='id',
        from_=alt.LookupData(countries, key='id',
                             fields=["type", "properties", "geometry"])
    )\
    .project('equirectangular')\
    .properties(
        width=500,
        height=300,
        title='Title'
    )

enter image description here

like image 165
jakevdp Avatar answered Nov 10 '25 16:11

jakevdp


I realize this is an older question, but for anyone still struggling to resolve the issue applying Jake's suggestion in their own code (as I did), I've found an alternative solution!

I ended up ditching the "transform_lookup" altogether and handled it directly in GeoPandas instead:

import geopandas as gpd
    
url = data.world_110m.url #in my case, this was a github url
gdf = gpd.read_file(url)
    
merged_data = pd.merge(gdf, df, on='id', how='left')

And then the code to the map would be something closer to a "normal" graph, just loading one data:

alt.Chart(merged_data).mark_geoshape()\
    .encode(color='rate:Q')\
    .add_selection(select_date)\
    .transform_filter(select_date)\
    .project('equirectangular')\
    .properties(
        width=500,
        height=300,
        title='Title'
    )

I hope this helps someone that is having difficulties making the transform_lookup work :)

like image 24
Maisa Fraiz Avatar answered Nov 10 '25 16:11

Maisa Fraiz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!