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.

In [1]:
import numpy as np
import pandas as pd
from lets_plot import *
In [2]:
LetsPlot.setup_html()

1. Delhi Mean Temperature Chart¶

In [3]:
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)
Out[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
In [4]:
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
Out[4]:

2. Fictional 'Daily Ice Cream Cost' Data¶

In [5]:
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
In [6]:
# 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)
Out[6]:
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¶

In [7]:
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
Out[7]:

4. Composing the 'Temperature' and 'Daily Cost' Plots into a Deck¶

In [8]:
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)
Out[8]:

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.
In [9]:
# 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)
Out[9]:

6. Assembling the Updated 'Daily Cost' Plot into a Final Deck¶

In [10]:
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)
Out[10]: