Today we will make a graph that I just love - Ridgeline Plots. We will visualize the median mobility across US states as they appeared in the article by Axios : How the coronavirus pandemic changed mobility habits, by state

Mobility

The data comes from Descartes Lab's GitHub Repository

import altair as alt
import pandas as pd
mobility_uri = 'https://raw.githubusercontent.com/descarteslabs/DL-COVID-19/master/DL-us-mobility-daterow.csv'
alt.renderers.set_embed_options(actions=False)
RendererRegistry.enable('default')
mobility = pd.read_csv(mobility_uri)
mobility.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 631087 entries, 0 to 631086
Data columns (total 9 columns):
 #   Column        Non-Null Count   Dtype  
---  ------        --------------   -----  
 0   date          631087 non-null  object 
 1   country_code  631087 non-null  object 
 2   admin_level   631087 non-null  int64  
 3   admin1        630854 non-null  object 
 4   admin2        618971 non-null  object 
 5   fips          630854 non-null  float64
 6   samples       631087 non-null  int64  
 7   m50           631087 non-null  float64
 8   m50_index     631087 non-null  int64  
dtypes: float64(2), int64(3), object(4)
memory usage: 43.3+ MB
mobility.head()
date country_code admin_level admin1 admin2 fips samples m50 m50_index
0 2020-03-01 US 1 Alabama NaN 1.0 133826 8.331 79
1 2020-03-02 US 1 Alabama NaN 1.0 143632 10.398 98
2 2020-03-03 US 1 Alabama NaN 1.0 146009 10.538 100
3 2020-03-04 US 1 Alabama NaN 1.0 149352 10.144 96
4 2020-03-05 US 1 Alabama NaN 1.0 144109 10.982 104
mobility_states = mobility[mobility['admin2'].isnull() & mobility['admin1'].notnull()]
mobility_states.head()
date country_code admin_level admin1 admin2 fips samples m50 m50_index
0 2020-03-01 US 1 Alabama NaN 1.0 133826 8.331 79
1 2020-03-02 US 1 Alabama NaN 1.0 143632 10.398 98
2 2020-03-03 US 1 Alabama NaN 1.0 146009 10.538 100
3 2020-03-04 US 1 Alabama NaN 1.0 149352 10.144 96
4 2020-03-05 US 1 Alabama NaN 1.0 144109 10.982 104

The data also contains the aggreagted median mobility for US as a country. We will filter that for our chart as we want only states -

#mobility_states.groupby('admin1')['m50_index'].max()
usa_mobility = mobility[mobility['admin2'].isnull() & mobility['admin1'].isnull()]
usa_mobility.head()
date country_code admin_level admin1 admin2 fips samples m50 m50_index
554459 2020-03-01 US 0 NaN NaN NaN 5705566 5.320 68
554460 2020-03-02 US 0 NaN NaN NaN 5970602 7.789 99
554461 2020-03-03 US 0 NaN NaN NaN 6100493 7.821 100
554462 2020-03-04 US 0 NaN NaN NaN 6274372 7.783 99
554463 2020-03-05 US 0 NaN NaN NaN 6023240 8.288 105

If you are interested in USA's mobility as a whole then you can visualize the following dataframe -

alt.data_transformers.enable('json')
#alt.data_transformers.enable('data_server')
DataTransformerRegistry.enable('json')

I have taken the liberty to also color the facets by the median of the mobility values. To get it exactly like the chart by Axios, just remove the fill encoding.

url = 'https://raw.githubusercontent.com/armsp/covidviz/master/assets/2020-08-31_Mobility_Data.json' # comment this when running locally
# url = mobility_states # uncomment this when running locally

highlight = alt.selection_single(on='mouseover', empty='all') # it looks like at present "facet" is not accepted as an encoding here even though it has been added as an encoding in a traditional sense of usage

alt.Chart(url, height=40, width=700,).mark_area().transform_window(
    avg_m50 ='mean(m50_index)', frame=(-6,0), groupby=['fips']
).encode(
    x=alt.X('date:T', title=None, axis=alt.Axis(domain=True, ticks=False, labels=True, format="%b", tickCount=5)),
    y = alt.Y('avg_m50:Q', title=None, axis=None, scale=alt.Scale(range=[50, -100], zero=False),),#zero=False is important because of how vega-lite handles the range of data internally. When we used the raw values without averaging then it did not think that we may have negative values etc so the graph was not expanded for that. But when we did the averaging, somehow it decided to have some space for negative values, that's why your graph was pulled up from x-axis. By mentioning that we will not show zero or below values you manually fixed that range and the graph was pulled down as it was earlier.
    fillOpacity= alt.condition(highlight, alt.value(0.8), alt.value(0.4)),
    fill = alt.Fill(
        'median(avg_m50):Q',
        legend=None,
        scale=alt.Scale(domain=[0,170],scheme='yellowgreenblue') #setting up domain gives favourable colours
    ),
    facet = alt.Facet('admin1:N', title=None, columns=1, header=alt.Header(labelAngle=0, labelOrient='left', labelAlign='left', labelAnchor='middle'))

).configure_facet(spacing=0,).properties(bounds='flush', title='Median Mobility',).configure_view(stroke=None).configure_title(
    anchor='middle'
).add_selection(highlight)