Rapidly emulating professional visualizations from The New York Times in Python using Altair¶

EXPECTATIONS¶

Data NYT 1
NYT The Economist
NYT Economist 2

ALTAIR¶

Altair is a declarative statistical visualization library for Python, based on Vega-Lite and the Grammar of Graphics paradigm.

VEGA-LITE¶

Vega-Lite is a high-level grammar of interactive graphics.

JSON Spec Output Plot

Grammar of Graphics¶

Fundamental Building Blocks¶

  1. Data
  2. Transformation
  3. Marks
  4. Encoding
  5. Scale
  6. Guides

Declarative Visualization¶

Declare What instead of implementing How

Takeaway¶

  • Altair is to Python as ggplot2 is to R
  • Quick, concise and intuitive

Creating a visualization in Altair - Pattern¶

Altair Pattern

MARKS¶

Basic Marks¶

  • mark_area - Area charts
  • mark_bar - Bar charts
  • mark_point - Scatterplots
  • mark-geoshape - Maps
  • mark_line - Line Charts
  • mark_rule - Horizontal and Vertical Lines
  • mark_rect - Heatmaps
  • mark_text - Text Charts

Composite Marks¶

  • mark_boxplot - Box Plots
  • mark_errorbar - Errorbar around a point
  • mark_errorband - Errorband around a line

ENCODING CHANNELS¶

General¶

  • x
  • y
  • longitude
  • latitude
  • color
  • size
  • tooltip

Subplots¶

  • column
  • row
  • facet

ENCODING DATA TYPES¶

Data Type Shorthand Code Description
quantitative Q a continuous real-valued quantity
ordinal O a discrete ordered quantity
nominal N a discrete unordered category
temporal T a time or date value
geojson G a geographic shape

WEATHER DATA EXPLORATION¶

In [1]:
import altair as alt  
import pandas as pd

weather_data = "https://github.com/vega/vega-datasets/blob/master/data/weather.csv?raw=True"
data = pd.read_csv(weather_data)
data['date'] = pd.to_datetime(data['date'])
data.head()
Out[1]:
location date precipitation temp_max temp_min wind weather
0 Seattle 2012-01-01 0.0 12.8 5.0 4.7 drizzle
1 Seattle 2012-01-02 10.9 10.6 2.8 4.5 rain
2 Seattle 2012-01-03 0.8 11.7 7.2 2.3 rain
3 Seattle 2012-01-04 20.3 12.2 5.6 4.7 rain
4 Seattle 2012-01-05 1.3 8.9 2.8 6.1 rain
In [2]:
alt.Chart(data).mark_point().encode(
    x = 'date',
    y = 'temp_max',
    column = 'location'
)
Out[2]:

Let's give different colors to each location

In [3]:
alt.Chart(data).mark_point().encode(
    x = 'date',
    y = 'temp_min',
    column = 'location',
    color = 'location'
)
Out[3]:

I wonder what the distribution of temperature_maximum is for both locations? How do we arrive at that? Well x axis stays the same, on y axis we say the count of temp_max and we do not facet!

In [4]:
alt.Chart(data).mark_bar().encode(
    x = 'temp_max',
    y = 'count(temp_max)'
)
Out[4]:

COMPOUND CHARTS¶

  • +
  • |
  • &
In [5]:
scatter = alt.Chart(data).mark_point().encode(
    x = 'precipitation',
    y = 'wind'
)

regression = alt.Chart(data).transform_regression('precipitation', 'wind').mark_line().encode(
    x = 'precipitation',
    y = 'wind'
)
In [6]:
scatter | regression
Out[6]:

LAYERING¶

In [7]:
scatter + regression.mark_line(color="red")
Out[7]:

VISUALIZING NYT's COVID DATASET¶

In [8]:
import altair as alt
import pandas as pd
import geopandas as gpd

us_covid_data_uri = 'https://github.com/nytimes/covid-19-data/blob/master/us-counties.csv?raw=true'
raw_data = pd.read_csv(us_covid_data_uri)
raw_data['date'] = pd.to_datetime(raw_data['date'])
covid = raw_data.copy()
covid.head()
Out[8]:
date county state fips cases deaths
0 2020-01-21 Snohomish Washington 53061.0 1 0
1 2020-01-22 Snohomish Washington 53061.0 1 0
2 2020-01-23 Snohomish Washington 53061.0 1 0
3 2020-01-24 Cook Illinois 17031.0 1 0
4 2020-01-24 Snohomish Washington 53061.0 1 0

MAPS¶

Shapefiles from US Census Website¶

In [9]:
county_shpfile = './cb_2019_us_county_20m'
county = gpd.read_file(county_shpfile)

Adding longitude and latitude in the data¶

We need to have longitude and latitude so that the marks are positioned where they are supposed to be

county['lon'] = county['geometry'].centroid.x
county['lat'] = county['geometry'].centroid.y
<class 'geopandas.geodataframe.GeoDataFrame'>
RangeIndex: 3220 entries, 0 to 3219
Data columns (total 4 columns):
 #   Column    Non-Null Count  Dtype   
---  ------    --------------  -----   
 0   fips      3220 non-null   int64   
 1   geometry  3220 non-null   geometry
 2   lon       3220 non-null   float64 
 3   lat       3220 non-null   float64 
dtypes: float64(2), geometry(1), int64(1)
memory usage: 100.8 KB
In [16]:
alt.Chart(county).mark_geoshape().encode().properties(width=800, height=600)
Out[16]:
In [17]:
alt.Chart(county).mark_geoshape().encode().properties(width=800, height=600).project('albersUsa')
Out[17]: