Download notebook (.ipynb)

Plot Overlay with ggdeck()#

The ggdeck() function enables multivariate comparison by synchronized overlaying of independent plots. This allows you to combine separate plot objects—each with its own coordinate system and geometries—into a unified view.

import numpy as np
import pandas as pd

from lets_plot import *
LetsPlot.setup_html()

1. Delhi Mean Temperature Chart#

climate_data = pd.read_csv("https://raw.githubusercontent.com/JetBrains/lets-plot-docs/refs/heads/master/data/delhi_climate.csv")
climate_data['date'] = pd.to_datetime(climate_data['date'])
climate_data.head(3)
date meantemp humidity wind_speed meanpressure
0 2013-01-01 10.000000 84.5 0.000000 1015.666667
1 2013-01-02 7.400000 92.0 2.980000 1017.800000
2 2013-01-03 7.166667 87.0 4.633333 1018.666667
temperature_plot = (
    ggplot(climate_data) 
        + geom_line(aes('date', 'meantemp', color='meantemp')) 
        + scale_color_gradient2(low='#0571b0', mid='#f7f7f7', high='#ca0020', midpoint=30)
        + labs(y='Mean Temperature [°C]')
        + theme(axis_text_y=element_text(color='#0571b0'), axis_title_y=element_text(color='#0571b0'))
)
temperature_plot

2. Fictional ‘Daily Ice Cream Cost’ Data#

def calculate_daily_cost(t):
    base_cost = 1.5
    surge = 0
    
    if t > 30:
        # Sharp surge above 30 degrees
        surge = 0.15 * ((t - 30) ** 2)
        
    return base_cost + surge
# Synthesize daily cost data and aggregate by month
cost_data = (
    climate_data.assign(daily_cost=climate_data['meantemp'].apply(calculate_daily_cost))
    .resample('MS', on='date')
    .agg(
        avg_daily_cost=('daily_cost', 'mean'),
        min_daily_cost=('daily_cost', 'min'),
        max_daily_cost=('daily_cost', 'max'),
    )
    .reset_index()
)

cost_data.head(3)
date avg_daily_cost min_daily_cost max_daily_cost
0 2013-01-01 1.5 1.5 1.5
1 2013-02-01 1.5 1.5 1.5
2 2013-03-01 1.5 1.5 1.5

3. ‘Daily Cost’ Plot with Y-Axis on the Right#

cost_plot = (
    ggplot(cost_data)
        + geom_bar(aes('date', 'avg_daily_cost'), stat='identity', 
                fill='#F39C12', alpha=0.4)
        + geom_pointrange(aes('date', 'avg_daily_cost', ymin='min_daily_cost', ymax='max_daily_cost'),
                color='#A04000', fatten=3, size=0.8)
        + labs(y='Daily Ice Cream Cost per Kid [$]')
        + theme(axis_text_y=element_text(color='#A04000'), axis_title_y=element_text(color='#A04000'))
        + scale_y_continuous(position='right')
)             

cost_plot

4. Composing the ‘Temperature’ and ‘Daily Cost’ Plots into a Deck#

ggdeck([
    temperature_plot + theme(legend_position='none'),
    cost_plot,
]) + \
theme(
    plot_title=element_text(hjust=0.5, size=21),
) + \
ggtitle('Delhi Mean Temperature & Ice Cream Cost') +\
ggsize(800, 400)

5. Managing Tooltips in Composites#

Since ggdeck() overlays independent plots, multiple tooltips may trigger simultaneously. This can lead to visual clutter and overlapping labels, especially on the axes.

To maintain clarity:

  • Silence background layers: Disable tooltips on secondary geometries to reduce hover noise.

  • Remove redundant axis labels: Suppress axis tooltips on overlaid plots to avoid duplication.

  • Use anchored tooltips: Consolidate information into a fixed position.

# Configure tooltips on the 'Daily Cost' plot

cost_plot2 = (
    ggplot(cost_data)
        + geom_bar(aes('date', 'avg_daily_cost'), stat='identity', 
                fill='#F39C12', alpha=0.4,
                tooltips='none')           # <-- Disable all tooltips on bars

        + geom_pointrange(aes('date', 'avg_daily_cost', ymin='min_daily_cost', ymax='max_daily_cost'),
                color='#A04000', fatten=3, size=0.8,
                tooltips=layer_tooltips()  # <-- Consolidate 'sideways' tooltips in a single panel 
                    .title('@date')
                    .line('Avg | ^y')
                    .line('Min | ^ymin')
                    .line('Max | ^ymax')
                    .format('^Y', '${.2f}')
                    .format('@date', '%b')
                    .anchor('top_right')   # <-- Anchor to the top-right corner 
                )
        + theme(axis_tooltip_x='blank')    # <-- Suppress redundant X-axis tooltips
        + labs(y='Daily Ice Cream Cost per Kid [$]')
        + theme(axis_text_y=element_text(color='#A04000'), axis_title_y=element_text(color='#A04000'))
        + scale_y_continuous(position='right')
)

cost_plot2 + ggsize(800, 400)

6. Assembling the Updated ‘Daily Cost’ Plot into a Final Deck#

ggdeck([
    temperature_plot + theme(legend_position='none'),
    cost_plot2,
]) + \
theme(
    plot_title=element_text(hjust=0.5, size=21),
) + \
ggtitle('Delhi Mean Temperature & Ice Cream Cost') +\
ggsize(800, 400)