HTML Module
The html module provides functions for converting calendar data into interactive HTML visualizations using Altair.
Installation
This installs altair>=5.0.0 as an optional dependency.
Overview
The HTML module transforms weekly calendar data (168 time slots) into interactive heatmap charts that can be:
- Embedded in web pages
- Used in Jupyter notebooks
- Integrated with mapping libraries (see Folium Integration)
API Reference
HTML calendar generation using Altair for interactive visualizations.
This module provides functions to create interactive calendar heatmaps using Altair and export them as HTML. These calendars can be embedded in web pages, Jupyter notebooks, or used with mapping libraries like Folium.
Note: This module requires altair to be installed:
Examples:
Create a basic calendar chart:
import pandas as pd
import numpy as np
from latent_calendar.html import create_calendar_chart
# Generate sample calendar data (168 time slots: 7 days × 24 hours)
calendar_data = pd.Series(np.random.rand(168))
# Create interactive Altair chart
chart = create_calendar_chart(
calendar_data,
title="Weekly Pattern",
color_scheme='greens'
)
# Save as HTML file
chart.save('calendar.html')
Create faceted chart directly from cal.aggregate_events:
from latent_calendar.datasets import load_chicago_bikes
from latent_calendar.html import create_calendar_chart
# Load data and aggregate by group
df = load_chicago_bikes()
df_agg = df.cal.aggregate_events("member_casual", "started_at")
# Works directly - automatically converts multi-row wide format!
chart = create_calendar_chart(df_agg, width=250, height=200, color_scheme='viridis')
# Apply faceting using Altair's .facet() method
faceted = chart.facet(column='member_casual:N')
faceted.save('faceted_calendar.html')
Chain methods for compact code:
# Create chart with custom properties and faceting in one expression
faceted = (
create_calendar_chart(df_agg, color_scheme='viridis')
.properties(width=250, height=200, title="Bike Share Patterns")
.facet(column='member_casual:N', columns=2)
)
faceted.save('faceted_calendar.html')
Generate HTML string using Altair's to_html():
# Get HTML string for embedding
html = chart.to_html()
# Or with custom embed options
html = chart.to_html(embed_options={'actions': False})
# Write to file or embed in application
with open('calendar.html', 'w') as f:
f.write(html)
create_calendar_chart(calendar_data, *, title=None, width=400, height=300, color_scheme='greens', monday_start=True, interactive=True, show_values=True)
Create an Altair calendar heatmap chart.
Generates an interactive calendar visualization showing patterns across the week (day-of-week on x-axis) and day (hour on y-axis) with color intensity representing the data values.
The returned Altair Chart object can be: - Displayed directly in Jupyter notebooks - Saved to HTML file using chart.save('filename.html') - Converted to HTML string using chart.to_html() - Faceted using chart.facet() for comparing multiple groups
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
calendar_data
|
Calendar data in wide or long format: - Wide format: Series, numpy array, list, or DataFrame with 168 columns (7 days × 24 hours). Multi-row DataFrames are automatically converted to long format with the index used as the group column. - Long format: DataFrame (pandas or Polars) with columns 'day_of_week', 'hour', 'value' (or 'num_events'), and optionally a group column for faceting. Polars DataFrames are supported directly via narwhals. |
required | |
title
|
str | None
|
Chart title. If None, no title is shown. |
None
|
width
|
int
|
Chart width in pixels. Default is 400. |
400
|
height
|
int
|
Chart height in pixels. Default is 300. |
300
|
color_scheme
|
str
|
Altair color scheme name. Options include 'greens', 'blues', 'reds', 'oranges', 'purples', 'viridis', 'plasma', 'inferno', 'magma', 'cividis', etc. Default is 'greens'. |
'greens'
|
monday_start
|
bool
|
Start week on Monday (True) or Sunday (False). Default is True. Only applies when converting from wide format. |
True
|
interactive
|
bool
|
Enable tooltips and zoom/pan interactions. Default is True. |
True
|
show_values
|
bool
|
Show values in tooltips when interactive=True. Default is True. |
True
|
Returns:
| Type | Description |
|---|---|
Chart
|
Altair Chart object that can be displayed, saved, converted to HTML, or faceted |
Examples:
Create and save to file:
import pandas as pd
import numpy as np
from latent_calendar.html import create_calendar_chart
calendar_data = pd.Series(np.random.rand(168))
chart = create_calendar_chart(calendar_data, title="Weekly Pattern")
chart.save('calendar.html')
Create faceted chart directly from cal.aggregate_events output:
from latent_calendar.datasets import load_chicago_bikes
from latent_calendar.html import create_calendar_chart
# Load and aggregate data by group
df = load_chicago_bikes()
df_agg = df.cal.aggregate_events("member_casual", "started_at")
# Works directly - no need for dataframe_to_long_format!
chart = create_calendar_chart(df_agg, width=250, height=200)
# Apply faceting using Altair's .facet() method
# The group column name comes from the DataFrame index name
faceted = chart.facet(column='member_casual:N')
faceted.save('faceted_calendar.html')
Chain methods for compact code:
faceted = (
create_calendar_chart(df_agg, color_scheme='viridis')
.properties(width=250, height=200, title="Bike Share Patterns")
.facet(column='member_casual:N', columns=2)
)
Customize appearance:
chart = create_calendar_chart(
calendar_data,
title="Custom Calendar",
width=600,
height=400,
color_scheme='reds',
monday_start=False # Start on Sunday
)
Display in Jupyter notebooks:
# Chart will display automatically in Jupyter
chart = create_calendar_chart(calendar_data)
chart # Display in notebook
Source code in latent_calendar/html.py
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | |
dataframe_to_long_format(df_wide, group_col='group', monday_start=True)
Convert wide calendar DataFrame with multiple rows to long format for Altair faceting.
Transforms a DataFrame where each row represents a different group's calendar data (7 days × 24 hours = 168 columns) into a long-format DataFrame suitable for faceted Altair visualizations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df_wide
|
DataFrame
|
DataFrame where each row is a calendar (168 columns) and index contains group names. Can be the result of df.cal.aggregate_events("group_col", "timestamp_col"). |
required |
group_col
|
str
|
Name for the column that will contain the group identifiers from the index. Default is "group". |
'group'
|
monday_start
|
bool
|
Start week on Monday (True) or Sunday (False). Default is True. |
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with columns: group, day_of_week (int 0-6), hour (int 0-23), value (float) |
Raises:
| Type | Description |
|---|---|
ValueError
|
If DataFrame columns don't have exactly 168 values per row |
Examples:
Convert multi-row aggregated data:
from latent_calendar.datasets import load_chicago_bikes
from latent_calendar.html import dataframe_to_long_format
df = load_chicago_bikes()
df_agg = df.cal.aggregate_events("member_casual", "started_at")
# Convert to long format for faceting
df_long = dataframe_to_long_format(df_agg, group_col="rider_type")
print(df_long.head())
# rider_type day_of_week hour value
# 0 member 0 0 42.0
# 1 member 0 1 28.0
# 2 member 0 2 15.0
# 3 member 0 3 8.0
# 4 member 0 4 12.0
Source code in latent_calendar/html.py
wide_to_long_format(calendar_data, monday_start=True)
Convert wide calendar format (168 cols) to long format for Altair.
Transforms a calendar in wide format (7 days × 24 hours = 168 values) into a long-format DataFrame suitable for Altair visualization with columns for day_of_week, hour, and value.
This function leverages the existing iterate_long_array() generator from the plot.iterate module, demonstrating how dataclasses can be easily converted to DataFrames.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
calendar_data
|
Series or array-like with 168 values (7 days × 24 hours). Can be pandas Series, numpy array, or list. |
required | |
monday_start
|
bool
|
Start week on Monday (True) or Sunday (False). Default is True. |
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with columns: day_of_week (int 0-6), hour (int 0-23), value (float) |
Raises:
| Type | Description |
|---|---|
ValueError
|
If calendar_data does not have exactly 168 values |
Examples:
import pandas as pd
import numpy as np
from latent_calendar.html import wide_to_long_format
# Create sample calendar data
calendar_data = pd.Series(np.random.rand(168))
# Convert to long format
df_long = wide_to_long_format(calendar_data)
print(df_long.head())
# day_of_week hour value
# 0 0 0 0.417022
# 1 0 1 0.720324
# 2 0 2 0.000114
# 3 0 3 0.302333
# 4 0 4 0.146756
With Sunday start:
Source code in latent_calendar/html.py
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
Usage Examples
Basic HTML Generation
from latent_calendar.datasets import load_ufo_sightings
from latent_calendar.html import create_calendar_chart
# Load and aggregate data
df = load_ufo_sightings()
df_states = df[df['country'] == 'us'].cal.aggregate_events(
by='state/province',
timestamp_col='Date_time'
)
# Create chart for California
chart = create_calendar_chart(
df_states.loc['ca'],
title="California UFO Sightings",
width=500,
height=350,
color_scheme='greens'
)
# Save directly to file
chart.save('calendar.html')
# Or get HTML string for embedding
html = chart.to_html()
with open('calendar.html', 'w') as f:
f.write(html)
Faceted Charts (Comparing Multiple Groups)
Create side-by-side calendar comparisons using Altair's .facet() method.
Direct usage with cal.aggregate_events():
from latent_calendar.datasets import load_chicago_bikes
from latent_calendar.html import create_calendar_chart
# Load data and aggregate by group
df = load_chicago_bikes()
df_agg = df.cal.aggregate_events("member_casual", "started_at")
# Create base chart directly from aggregated data
# The function automatically detects multi-row format and converts it
chart = create_calendar_chart(
df_agg,
width=250,
height=200,
color_scheme='viridis'
)
# Apply faceting using Altair's .facet() method
# Use the index name from aggregate_events as the grouping column
faceted = chart.facet(column='member_casual:N')
faceted.save('faceted_calendar.html')
Chain methods for compact code:
# Create chart with custom properties and faceting in one expression
faceted = (
create_calendar_chart(df_agg, color_scheme='viridis')
.properties(width=250, height=200, title="Bike Share Patterns")
.facet(column='member_casual:N', columns=2)
)
faceted.save('faceted_calendar.html')
Using explicit conversion (optional):
If you need more control over the group column name, you can still use dataframe_to_long_format():
from latent_calendar.html import dataframe_to_long_format
# Explicitly convert with custom group column name
df_long = dataframe_to_long_format(df_agg, group_col="rider_type")
chart = create_calendar_chart(df_long)
faceted = chart.facet(column='rider_type:N')
Advanced faceting options:
# Two-dimensional faceting (e.g., by season and rider type)
faceted_2d = chart.facet(
row='season:N',
column='rider_type:N'
)
# Custom number of columns in facet grid
faceted = chart.facet(
column='station_name:N',
columns=3 # 3 charts per row
)
# Adjust sizing before faceting
smaller_chart = chart.properties(width=200, height=150)
faceted = smaller_chart.facet(column='group:N')
Creating Altair Chart Objects
from latent_calendar.html import create_calendar_chart
# Create a chart object for further customization
chart = create_calendar_chart(
df_states.loc['ca'],
title="Weekly Pattern",
width=400,
height=300,
color_scheme='viridis',
show_values=True,
monday_start=True
)
# Customize further with Altair API
chart = chart.configure_view(strokeWidth=0)
# Export as HTML
html = chart.to_html()
Jupyter Notebook Display
from latent_calendar.html import create_calendar_chart
# Create and display chart in notebook
chart = create_calendar_chart(
calendar_data,
title="Activity Pattern",
color_scheme='greens'
)
# Chart displays automatically in Jupyter
chart
Data Format Conversion
from latent_calendar.html import wide_to_long_format, dataframe_to_long_format
# Convert single row (168 columns) to long format
df_long = wide_to_long_format(
df_states.loc['ca'],
monday_start=True
)
print(df_long)
# Output:
# day_of_week hour value
# 0 0 0 45
# 1 0 1 32
# ...
# 167 6 23 67
# Convert multiple rows for faceting
df_long_multi = dataframe_to_long_format(
df_states.head(3), # Multiple states
group_col="state"
)
print(df_long_multi)
# Output:
# state day_of_week hour value
# 0 ca 0 0 45.0
# 1 ca 0 1 32.0
# ...
# 503 tx 6 23 28.0
Color Schemes
The module supports all Altair/Vega color schemes. Default is 'greens'.
Sequential (single-hue):
- 'greens', 'blues', 'reds', 'purples', 'greys', 'oranges'
Perceptual (multi-hue):
- 'viridis', 'plasma', 'inferno', 'magma', 'cividis', 'turbo'
Diverging:
- 'redblue', 'redgrey', 'blueorange', 'purpleorange'
Categorical:
- 'category10', 'category20', 'tableau10', 'tableau20'
See Altair Color Schemes for the complete list.
Interactivity
By default, charts include:
- Zoom/Pan: Mouse wheel to zoom, click and drag to pan
- Tooltips: Hover over cells to see exact values
- Reset: Double-click to reset view
Disable interactivity:
Customization Tips
Compact Charts (No Values)
For smaller file sizes and cleaner appearance:
chart = create_calendar_chart(
data,
show_values=False, # Hide numeric labels
width=350, # Smaller dimensions
height=250
)
html = chart.to_html()
Sunday Week Start
To start weeks on Sunday instead of Monday:
Custom Embed Options
Control Altair rendering behavior:
chart = create_calendar_chart(data)
html = chart.to_html(embed_options={
'mode': 'vega-lite',
'renderer': 'svg', # Use SVG instead of Canvas
'actions': False # Hide action menu
})
Technical Details
Data Flow
Single Calendar:
Input: pd.Series with 168 values
↓
wide_to_long_format()
↓
DataFrame with (day_of_week, hour, value) columns
↓
create_calendar_chart()
↓
Altair Chart object
↓
chart.to_html() or chart.save()
↓
Standalone HTML document
Faceted Calendars:
Input: pd.DataFrame with multiple rows × 168 columns
↓
create_calendar_chart() [auto-converts to long format internally]
↓
Altair Chart object
↓
chart.facet(column='group:N')
↓
Faceted Altair Chart object
↓
chart.to_html() or chart.save()
↓
Standalone HTML document
Note: Multi-row wide DataFrames are automatically converted to long format.
The index name is used as the group column (or "group" if unnamed).
Chart Structure
The generated Altair chart uses:
- Mark Type:
rect(rectangles for heatmap cells) - Encoding:
- X-axis: Hour of day (0-23)
- Y-axis: Day of week (0-6)
- Color: Event count/value
- Tooltip: Day name, hour, value
- Scale: Sequential color scale based on data range
HTML Output Format
The HTML includes:
- Complete
<!DOCTYPE html>document - Embedded Vega-Lite specification
- Bundled Vega/Vega-Lite/Vega-Embed JavaScript libraries
- All styles and scripts for standalone viewing
File size varies by chart complexity:
- Simple chart with show_values=False: ~50 KB
- Chart with values and large dataset: ~80-120 KB
See Also
- Folium Integration - Use calendars in maps
.calAccessor - Data aggregation and preparation- Altair Documentation - Chart customization
- UFO Sightings Example - Example dataset