Skip to content

Plotting and Visualization

meteaudata provides built-in visualization capabilities for exploring time series data and processing dependencies using Plotly interactive plots.

Overview

meteaudata visualization includes:

  1. TimeSeries.plot() - Individual time series plotting
  2. Signal.plot() - Multi-time series plotting within a signal
  3. Signal.plot_dependency_graph() - Processing dependency visualization
  4. Dataset.plot() - Multi-signal plotting with subplots

Basic Time Series Plotting

import numpy as np
import pandas as pd
from meteaudata import Signal, DataProvenance
from meteaudata import resample, linear_interpolation, subset, replace_ranges

# Set random seed for reproducible examples
np.random.seed(42)

# Create a standard provenance for examples
provenance = DataProvenance(
    source_repository="Example System",
    project="Documentation Example",
    location="Demo Location",
    equipment="Temperature Sensor v2.1",
    parameter="Temperature",
    purpose="Documentation example",
    metadata_id="doc_example_001"
)

# Create simple time series data
timestamps = pd.date_range('2024-01-01', periods=100, freq='h')
data = pd.Series(np.random.randn(100) * 10 + 20, index=timestamps, name="RAW")

# Create a simple signal
signal = Signal(
    input_data=data,
    name="Temperature",
    provenance=provenance,
    units="°C"
)

# Plot individual time series
print(f"Signal: {signal.name} has {len(signal.time_series)} time series")
Signal: Temperature#1 has 1 time series
# Get the raw time series
raw_ts_name = "Temperature#1_RAW#1"
raw_ts = signal.time_series[raw_ts_name]
print(f"Plotting {raw_ts_name} with {len(raw_ts.series)} data points")
Plotting Temperature#1_RAW#1 with 100 data points
# Create basic plot and save it
import os
from pathlib import Path
output_dir = OUTPUT_DIR if 'OUTPUT_DIR' in globals() else Path('docs/assets/generated')

fig = raw_ts.plot(title="Individual Time Series Plot")
plot_path = output_dir / "viz_timeseries_basic.html"
fig.write_html(str(plot_path))
print(f"Saved plot to {plot_path}")
Saved plot to docs/assets/generated/viz_timeseries_basic.html

Signal Plotting

Plot multiple time series from the same signal:

# Apply processing to create more time series
from meteaudata import linear_interpolation

signal.process(["Temperature#1_RAW#1"], linear_interpolation)

# Plot multiple time series from the signal and save it
ts_names = ["Temperature#1_RAW#1", "Temperature#1_LIN-INT#1"]
fig = signal.plot(ts_names, title="Raw vs Processed Data")
plot_path = output_dir / "viz_signal_multi.html"
fig.write_html(str(plot_path))
print(f"Plotted {len(ts_names)} time series together")
print(f"Saved plot to {plot_path}")
Plotted 2 time series together
Saved plot to docs/assets/generated/viz_signal_multi.html
# Show processing type information
for ts_name in ts_names:
    ts = signal.time_series[ts_name]
    if ts.processing_steps:
        last_step = ts.processing_steps[-1]
        print(f"{ts_name}: {last_step.type}")
    else:
        print(f"{ts_name}: RAW (no processing)")
Temperature#1_RAW#1: RAW (no processing)
Temperature#1_LIN-INT#1: ProcessingType.GAP_FILLING

Dependency Graph Visualization

Visualize processing relationships:

# Create dependency graph and save it
dep_fig = signal.plot_dependency_graph("Temperature#1_LIN-INT#1")
dep_path = output_dir / "viz_dependency_graph.html"
dep_fig.write_html(str(dep_path))
print("Generated dependency graph showing processing lineage")
print(f"Saved dependency graph to {dep_path}")
Generated dependency graph showing processing lineage
Saved dependency graph to docs/assets/generated/viz_dependency_graph.html

Dataset Plotting

Plot multiple signals using subplots:

import numpy as np
import pandas as pd
from meteaudata import Signal, DataProvenance, Dataset
from meteaudata import resample, linear_interpolation, subset, replace_ranges
from meteaudata import average_signals

# Set random seed for reproducible examples
np.random.seed(42)

# Create multiple time series for complex examples
timestamps = pd.date_range('2024-01-01', periods=100, freq='h')

# Temperature data with daily cycle
temp_data = pd.Series(
    20 + 5 * np.sin(np.arange(100) * 2 * np.pi / 24) + np.random.normal(0, 0.5, 100),
    index=timestamps,
    name="RAW"
)

# pH data with longer cycle
ph_data = pd.Series(
    7.2 + 0.3 * np.sin(np.arange(100) * 2 * np.pi / 48) + np.random.normal(0, 0.1, 100),
    index=timestamps,
    name="RAW"
)

# Dissolved oxygen data with some correlation to temperature
do_data = pd.Series(
    8.5 - 0.1 * (temp_data - 20) + np.random.normal(0, 0.2, 100),
    index=timestamps,
    name="RAW"
)

# Temperature signal
temp_provenance = DataProvenance(
    source_repository="Plant SCADA",
    project="Multi-parameter Monitoring",
    location="Reactor R-101",
    equipment="Thermocouple Type K",
    parameter="Temperature",
    purpose="Process monitoring",
    metadata_id="temp_001"
)
temperature_signal = Signal(
    input_data=temp_data,
    name="Temperature",
    provenance=temp_provenance,
    units="°C"
)

# pH signal
ph_provenance = DataProvenance(
    source_repository="Plant SCADA",
    project="Multi-parameter Monitoring",
    location="Reactor R-101",
    equipment="pH Sensor v1.3",
    parameter="pH",
    purpose="Process monitoring",
    metadata_id="ph_001"
)
ph_signal = Signal(
    input_data=ph_data,
    name="pH",
    provenance=ph_provenance,
    units="pH units"
)

# Dissolved oxygen signal
do_provenance = DataProvenance(
    source_repository="Plant SCADA",
    project="Multi-parameter Monitoring",
    location="Reactor R-101",
    equipment="DO Sensor v2.0",
    parameter="Dissolved Oxygen",
    purpose="Process monitoring",
    metadata_id="do_001"
)
do_signal = Signal(
    input_data=do_data,
    name="DissolvedOxygen",
    provenance=do_provenance,
    units="mg/L"
)

# Create signals dictionary for easy access
signals = {
    "temperature": temperature_signal,
    "ph": ph_signal,
    "dissolved_oxygen": do_signal
}

# Create a complete dataset
dataset = Dataset(
    name="reactor_monitoring",
    description="Multi-parameter monitoring of reactor R-101",
    owner="Process Engineer",
    purpose="Process control and optimization",
    project="Process Monitoring Study",
    signals={
        "temperature": temperature_signal,
        "ph": ph_signal,
        "dissolved_oxygen": do_signal
    }
)

# Check what signals are available
signal_names = list(dataset.signals.keys())
print(f"Available signals: {signal_names}")
Available signals: ['Temperature#1', 'pH#1', 'DissolvedOxygen#1']
# Plot multiple signals from dataset using actual signal names and save it
import os
from pathlib import Path
output_dir = OUTPUT_DIR if 'OUTPUT_DIR' in globals() else Path('docs/assets/generated')

selected_signals = signal_names[:2]  # Get first two signals
ts_names = [f"{signal_name}_RAW#1" for signal_name in selected_signals]
print(f"Time series names: {ts_names}")
Time series names: ['Temperature#1_RAW#1', 'pH#1_RAW#1']
fig = dataset.plot(
    signal_names=selected_signals,
    ts_names=ts_names,
    title="Multi-Signal Dashboard"
)
dataset_plot_path = output_dir / "viz_dataset_multi.html"
fig.write_html(str(dataset_plot_path))
print(f"Created dataset plot with {len(selected_signals)} signals")
print(f"Saved dataset plot to {dataset_plot_path}")
Created dataset plot with 2 signals
Saved dataset plot to docs/assets/generated/viz_dataset_multi.html

Rich Display System

meteaudata provides multiple ways to explore and visualize metadata:

Text Representation

Simple text-based metadata overview:

# Text representation - quick overview
print("=== TEXT REPRESENTATION ===")
signal_name = list(dataset.signals.keys())[0]
# Suppress text output during build - users can run this in notebooks
# dataset.signals[signal_name].display(format="text", depth=2)
print(f"Signal: {signal_name}")
print(f"To see full text representation, run: dataset.signals['{signal_name}'].display(format='text', depth=2)")
=== TEXT REPRESENTATION ===
Signal: Temperature#1
To see full text representation, run: dataset.signals['Temperature#1'].display(format='text', depth=2)

HTML Representation with Foldable Drill-downs

Interactive HTML with collapsible sections:

# HTML representation with collapsible sections
print("=== HTML REPRESENTATION ===")
signal_name = list(dataset.signals.keys())[0]

# Save to file for documentation (v0.10.0)
import os
from pathlib import Path
output_dir = OUTPUT_DIR if 'OUTPUT_DIR' in globals() else Path('docs/assets/generated')
html_path = output_dir / "display_content_html_rep.html"

dataset.signals[signal_name].display(
    format="html",
    depth=3,
    output_file=str(html_path)
)
print(f"Generated HTML display and saved to {html_path}")
=== HTML REPRESENTATION ===
Generated HTML display and saved to docs/assets/generated/display_content_html_rep.html

Web View with Interactive Box Diagram

The interactive box diagram provides the most comprehensive view of your data's metadata structure. Unlike the static text and HTML representations above, this creates a fully interactive visualization where you can:

  • Navigate visually - See how signals, time series, and processing steps connect
  • Explore interactively - Click any box to see detailed attributes in the side panel
  • Control complexity - Expand/collapse sections using +/- buttons to focus on what matters
  • Pan and zoom - Navigate large metadata structures with mouse controls

This is particularly useful for understanding complex processing pipelines and data relationships.

Using the Interactive Graph

The simplest way to view the interactive graph is with the show_graph_in_browser() method:

# Open interactive visualization directly in your browser
# This works for Signals, TimeSeries, and Datasets

dataset.show_graph_in_browser()

You can also customize the visualization:

# Customize the graph display
dataset.show_graph_in_browser(
    max_depth=4,           # How many levels deep to show
    width=1400,            # Browser window width
    height=900,            # Browser window height
    title="My Dataset"     # Custom title
)

For individual signals or time series:

# View a single signal's metadata structure
signal = dataset.signals["Temperature#1"]
signal.show_graph_in_browser()

# Or view a specific time series
time_series = signal.time_series["Temperature#1_RESAMPLED#1"]
time_series.show_graph_in_browser()

Example Interactive View

Below is an example of what the interactive graph looks like. In your own code, calling show_graph_in_browser() will open this in a new browser tab where you can interact with it fully.

# Generate the interactive graph for documentation display
from meteaudata.graph_display import render_meteaudata_graph_html
import os
from pathlib import Path

# Create the HTML content
html_content = render_meteaudata_graph_html(
    dataset,
    max_depth=4,
    width=1400,
    height=900,
    title="Interactive Dataset Metadata Explorer"
)

# Save for iframe display in documentation
output_dir = OUTPUT_DIR if 'OUTPUT_DIR' in globals() else Path('docs/assets/generated')
iframe_path = output_dir / "meteaudata_dataset_graph.html"
with open(iframe_path, 'w', encoding='utf-8') as f:
    f.write(html_content)

print("Example graph generated for documentation")

Example graph generated for documentation

See Also