Skip to content

Visualization

Functions for visualizing time series data and components using lets_plot.

plot_components

plot_components(dataset: Dict, sample_indices: Union[int, List[int], Dict[int, int], None] = None, components: List[str] = None, dimensions: List[int] = None, show_indicators: bool = True, line_color: str = 'black', line_size: float = 1.5, hline_intercept: float = 0, rect_fill: str = 'red', rect_alpha: float = 0.25, facet_order: Dict[str, str] = {'y': 'class', 'x': 'component'}, x_order: int = 1, y_order: int = 1, panel_width: int = 250, panel_height: int = 150) -> Union[Any, List[Any]]

Create time series visualization with feature indicators as rectangles.

This function visualizes time series data by showing its components (aggregated series, background, features) with options to highlight feature locations.

Parameters:

Name Type Description Default
dataset Dict

Dataset containing time series data and components.

required
sample_indices Union[int, List[int], Dict[int, int], None]

Specifies which samples to visualize. Note: This function shows ONE sample per class for comparison purposes. Can be provided in several formats: - int: A single sample index (shows that sample and its class) - List[int]: Multiple sample indices. These are grouped by class, and if multiple samples belong to the same class, only the last one is shown. - Dict[int, int]: Mapping from class labels to sample indices (e.g., {0: 5, 1: 10} means sample at index 5 for class 0, sample at index 10 for class 1). The sample at each index must actually belong to the specified class. - None: Use first sample from each class (default behavior)

None
components Optional[List[str]]

List of components to include. Can be used to exclude certain components. Default: ["aggregated", "features", "background"].

None
dimensions Optional[List[int]]

List of dimensions to include. If None, include all dimensions. For multivariate time series, this allows selecting specific dimensions.

None
show_indicators bool

Whether to show feature indicators.

True
line_color str

Color of the time series lines.

'black'
line_size float

Size of the time series lines.

1.5
hline_intercept float

Y-intercept for horizontal line at y=hline_intercept. If None, no line is shown.

0
rect_fill str

Fill color for feature rectangles.

'red'
rect_alpha float

Alpha transparency for feature rectangles.

0.25
facet_order Dict[str, str]

Order of facets, dict with "x" and "y" keys. x corresponds to columns, y to rows.

{'y': 'class', 'x': 'component'}
x_order int

Order of x-axis facets. 1=ascending, -1=descending, 0=no order. Uses names of the column facet variable.

1
y_order int

Order of y-axis facets. 1=ascending, -1=descending, 0=no order. Uses names of the row facet variable.

1
panel_width int

Width of each panel in pixels.

250
panel_height int

Height of each panel in pixels.

150

Returns:

Type Description
Union[Any, List[Any]]

Union[Any, List[Any]]: lets_plot visualization or list of visualizations for multivariate data.

Raises:

Type Description
IndexError

If a sample index is out of range.

Examples:

Show first sample of each class (default)

plot_components(dataset)

Show a specific sample by index (shows only the class of that sample)

plot_components(dataset, sample_indices=5)

Show samples from different classes (grouped by class, one sample per class)

If indices 0, 10, 20 are from classes 0, 1, 0 respectively, only samples 10 and 20

will be shown (last sample for class 0, and sample 10 for class 1)

plot_components(dataset, sample_indices=[0, 10, 20])

Explicit mapping: show sample 5 for class 0, sample 10 for class 1

(the sample at index 5 must belong to class 0, and sample at index 10 to class 1)

plot_components(dataset, sample_indices={0: 5, 1: 10})

Source code in xaitimesynth/visualization.py
def plot_components(
    dataset: Dict,
    sample_indices: Union[int, List[int], Dict[int, int], None] = None,
    components: List[str] = None,
    dimensions: List[int] = None,
    show_indicators: bool = True,
    line_color: str = "black",
    line_size: float = 1.5,
    hline_intercept: float = 0,
    rect_fill: str = "red",
    rect_alpha: float = 0.25,
    facet_order: Dict[str, str] = {"y": "class", "x": "component"},
    x_order: int = 1,
    y_order: int = 1,
    panel_width: int = 250,
    panel_height: int = 150,
) -> Union[Any, List[Any]]:
    """Create time series visualization with feature indicators as rectangles.

    This function visualizes time series data by showing its components (aggregated series,
    background, features) with options to highlight feature locations.

    Args:
        dataset (Dict): Dataset containing time series data and components.
        sample_indices (Union[int, List[int], Dict[int, int], None]): Specifies which samples to visualize.
            Note: This function shows ONE sample per class for comparison purposes.
            Can be provided in several formats:
            - int: A single sample index (shows that sample and its class)
            - List[int]: Multiple sample indices. These are grouped by class, and if
              multiple samples belong to the same class, only the last one is shown.
            - Dict[int, int]: Mapping from class labels to sample indices
              (e.g., {0: 5, 1: 10} means sample at index 5 for class 0, sample at index 10
              for class 1). The sample at each index must actually belong to the specified class.
            - None: Use first sample from each class (default behavior)
        components (Optional[List[str]]): List of components to include. Can be used to exclude
            certain components. Default: ["aggregated", "features", "background"].
        dimensions (Optional[List[int]]): List of dimensions to include. If None, include all dimensions.
            For multivariate time series, this allows selecting specific dimensions.
        show_indicators (bool): Whether to show feature indicators.
        line_color (str): Color of the time series lines.
        line_size (float): Size of the time series lines.
        hline_intercept (float): Y-intercept for horizontal line at y=hline_intercept.
            If None, no line is shown.
        rect_fill (str): Fill color for feature rectangles.
        rect_alpha (float): Alpha transparency for feature rectangles.
        facet_order (Dict[str, str]): Order of facets, dict with "x" and "y" keys.
            x corresponds to columns, y to rows.
        x_order (int): Order of x-axis facets. 1=ascending, -1=descending, 0=no order.
            Uses names of the column facet variable.
        y_order (int): Order of y-axis facets. 1=ascending, -1=descending, 0=no order.
            Uses names of the row facet variable.
        panel_width (int): Width of each panel in pixels.
        panel_height (int): Height of each panel in pixels.

    Returns:
        Union[Any, List[Any]]: lets_plot visualization or list of visualizations for multivariate data.

    Raises:
        IndexError: If a sample index is out of range.

    Examples:
        # Show first sample of each class (default)
        plot_components(dataset)

        # Show a specific sample by index (shows only the class of that sample)
        plot_components(dataset, sample_indices=5)

        # Show samples from different classes (grouped by class, one sample per class)
        # If indices 0, 10, 20 are from classes 0, 1, 0 respectively, only samples 10 and 20
        # will be shown (last sample for class 0, and sample 10 for class 1)
        plot_components(dataset, sample_indices=[0, 10, 20])

        # Explicit mapping: show sample 5 for class 0, sample 10 for class 1
        # (the sample at index 5 must belong to class 0, and sample at index 10 to class 1)
        plot_components(dataset, sample_indices={0: 5, 1: 10})
    """
    # Ensure dataset is in channels_last format for visualization
    dataset = _ensure_visualization_format(dataset)

    # Process sample_indices parameter to normalize to dict format
    processed_indices = None

    if sample_indices is not None:
        if isinstance(sample_indices, int):
            # Single sample index
            if sample_indices < 0 or sample_indices >= len(dataset["y"]):
                raise IndexError(
                    f"Sample index {sample_indices} is out of range (0-{len(dataset['y']) - 1})"
                )
            class_label = dataset["y"][sample_indices]
            processed_indices = {class_label: sample_indices}

        elif isinstance(sample_indices, list):
            # Multiple sample indices - group by class
            for idx in sample_indices:
                if idx < 0 or idx >= len(dataset["y"]):
                    raise IndexError(
                        f"Sample index {idx} is out of range (0-{len(dataset['y']) - 1})"
                    )
            processed_indices = {}
            for idx in sample_indices:
                class_label = dataset["y"][idx]
                processed_indices[class_label] = idx

        elif isinstance(sample_indices, dict):
            # Validate the indices
            for class_label, idx in sample_indices.items():
                if idx < 0 or idx >= len(dataset["y"]):
                    raise IndexError(
                        f"Sample index {idx} is out of range (0-{len(dataset['y']) - 1})"
                    )
                actual_class = dataset["y"][idx]
                if actual_class != class_label:
                    raise ValueError(
                        f"Sample at index {idx} has class {actual_class}, not {class_label} as specified"
                    )
            processed_indices = sample_indices

    components_to_use = components if components is not None else DEFAULT_COMPONENTS

    # Prepare data for visualization
    df = prepare_plot_data(dataset, processed_indices, components_to_use, dimensions)
    assert len(df) > 0, "No data to display"

    is_multivariate = len(df["dim"].unique()) > 1
    df = _capitalize_components(df)
    df = _apply_component_facet_order(df)
    df = _add_line_group_column(df)

    # Prepare rectangles for feature highlighting
    rectangles = None
    if show_indicators:
        rectangles = prepare_feature_highlights(
            dataset, processed_indices, components_to_use, dimensions
        )
        if rectangles is not None and "component" in rectangles.columns:
            rectangles = rectangles.copy()
            rectangles["component"] = rectangles["component"].str.capitalize()
            rectangles = _apply_component_facet_order(rectangles)

    # For multivariate time series, use specialized dimension-based plotting
    if is_multivariate:
        return _plot_dimensions(
            df,
            rectangles,
            line_color=line_color,
            line_size=line_size,
            hline_intercept=hline_intercept,
            rect_fill=rect_fill,
            rect_alpha=rect_alpha,
            panel_width=panel_width,
            panel_height=panel_height,
        )

    # For univariate case
    y_min, y_max = _calculate_y_limits(df["value"])

    # Calculate plot dimensions
    n_components = len(df["component"].unique())
    n_classes = len(df["class"].unique())
    total_width = panel_width * (
        n_components
        if "x" in facet_order and facet_order["x"] == "component"
        else n_classes
    )
    total_height = panel_height * (
        n_classes
        if "y" in facet_order and facet_order["y"] == "class"
        else n_components
    )

    # Start building the plot
    p = ggplot(df, aes(x="time", y="value"))

    if hline_intercept is not None:
        p = p + geom_hline(yintercept=hline_intercept, linetype="dashed", color="gray")

    # Add rectangles for feature regions
    if show_indicators and rectangles is not None:
        rectangles = rectangles.copy()
        rectangles["ymin"] = y_min
        rectangles["ymax"] = y_max
        p = _add_geom_rect(p, rectangles, rect_fill, rect_alpha)

    p = _add_geom_line(p, line_color, line_size, data=df)

    # Use regular facet grid with proper ordering
    p = p + facet_grid(**facet_order, scales="fixed", x_order=x_order, y_order=y_order)
    p = p + scale_y_continuous(limits=[y_min, y_max])

    p = p + labs(x="Time Steps", y="Value")
    p = p + ggsize(total_width, total_height)

    return p

plot_component

plot_component(signal: np.ndarray = None, component_type: str = None, n_timesteps: int = 100, rng: np.random.RandomState = None, width: int = 500, height: int = 250, line_color: str = 'black', line_size: float = 1.5, hline_intercept: float = None, normalization: str = 'none', normalization_kwargs: Dict[str, Any] = None, title: str = None, **kwargs: Any) -> Any

Plot a time series signal generated by one of the generator functions.

This function can be used in two ways: 1. Pass a pre-generated signal array directly 2. Specify a component_type and parameters to generate a new signal

Parameters:

Name Type Description Default
signal Optional[ndarray]

Pre-generated signal array.

None
component_type Optional[str]

Type of component to generate (if signal not provided).

None
n_timesteps int

Length of time series to generate (if signal not provided).

100
rng Optional[RandomState]

Random number generator.

None
width int

Plot width in pixels.

500
height int

Plot height in pixels.

250
line_color str

Color of the signal line.

'black'
line_size float

Size of the signal line.

1.5
hline_intercept Optional[float]

Y-intercept for horizontal line. If None, no line is shown.

None
normalization str

Normalization method. Options: "none", "minmax", "zscore". "none" means no normalization, "minmax" scales to [0, 1], "zscore" standardizes.

'none'
normalization_kwargs Optional[Dict[str, Any]]

Additional parameters for normalization methods. For "minmax", you can pass feature_range (tuple of min and max).

None
title Optional[str]

Title for the plot.

None
**kwargs Any

Additional parameters for the generator function.

{}

Returns:

Name Type Description
Any Any

lets_plot visualization object.

Source code in xaitimesynth/visualization.py
def plot_component(
    signal: np.ndarray = None,
    component_type: str = None,
    n_timesteps: int = 100,
    rng: np.random.RandomState = None,
    width: int = 500,
    height: int = 250,
    line_color: str = "black",
    line_size: float = 1.5,
    hline_intercept: float = None,
    normalization: str = "none",
    normalization_kwargs: Dict[str, Any] = None,
    title: str = None,
    **kwargs: Any,
) -> Any:
    """Plot a time series signal generated by one of the generator functions.

    This function can be used in two ways:
    1. Pass a pre-generated signal array directly
    2. Specify a component_type and parameters to generate a new signal

    Args:
        signal (Optional[np.ndarray]): Pre-generated signal array.
        component_type (Optional[str]): Type of component to generate (if signal not provided).
        n_timesteps (int): Length of time series to generate (if signal not provided).
        rng (Optional[np.random.RandomState]): Random number generator.
        width (int): Plot width in pixels.
        height (int): Plot height in pixels.
        line_color (str): Color of the signal line.
        line_size (float): Size of the signal line.
        hline_intercept (Optional[float]): Y-intercept for horizontal line.
            If None, no line is shown.
        normalization (str): Normalization method. Options: "none", "minmax", "zscore".
            "none" means no normalization, "minmax" scales to [0, 1], "zscore" standardizes.
        normalization_kwargs (Optional[Dict[str, Any]]): Additional parameters for normalization methods.
            For "minmax", you can pass feature_range (tuple of min and max).
        title (Optional[str]): Title for the plot.
        **kwargs (Any): Additional parameters for the generator function.

    Returns:
        Any: lets_plot visualization object.
    """
    # Handle default for normalization_kwargs
    if normalization_kwargs is None:
        normalization_kwargs = {}

    # Create default RNG if not provided
    if rng is None:
        rng = np.random.RandomState(None)  # Use system time as seed

    # Generate signal if not provided directly
    if signal is None:
        if component_type is None:
            raise ValueError("Either signal or component_type must be provided")

        if component_type not in GENERATOR_FUNCS:
            raise ValueError(
                f"Unknown component type: {component_type}. "
                f"Valid types are: {', '.join(GENERATOR_FUNCS.keys())}"
            )

        signal = generate_component(component_type, n_timesteps, rng, **kwargs)

    # Ensure signal is numpy array & normalize signal
    signal = np.asarray(signal)
    signal = normalize(signal, method=normalization, **normalization_kwargs)

    # Create dataframe for plotting - using pandas DataFrame to ensure proper types
    n_points = len(signal)
    data = pd.DataFrame({"time": np.arange(n_points), "value": signal})

    # Create the plot
    p = ggplot(data, aes(x="time", y="value")) + geom_line(
        color=line_color, size=line_size
    )

    if hline_intercept is not None:
        p = p + geom_hline(yintercept=hline_intercept, linetype="dashed", color="gray")

    # Get title
    if title is None:
        title = f"{component_type.replace('_', ' ').capitalize() if component_type else 'Precomputed Signal'}"
        title += f" ({normalization} normalized)" if normalization != "none" else ""

    # Add labels and theme
    p = p + labs(title=title, x="Time Steps", y="Value") + ggsize(width, height)

    return p

plot_sample

plot_sample(X: np.ndarray, y: np.ndarray = None, feature_masks: Dict[str, np.ndarray] = None, components: List[Any] = None, sample_idx: int = 0, components_to_include: List[str] = None, line_color: str = 'black', rect_fill: str = 'red', panel_width: int = 225, panel_height: int = 175) -> Any

Plot a time series sample with its components and feature masks using lets_plot.

Parameters:

Name Type Description Default
X ndarray

Time series data.

required
y Optional[ndarray]

Class labels.

None
feature_masks Optional[Dict[str, ndarray]]

Dictionary of feature masks.

None
components Optional[List[Any]]

List of TimeSeriesComponents objects.

None
sample_idx int

Index of the sample to plot.

0
components_to_include Optional[List[str]]

List of components to include.

None
line_color str

Color of the time series lines.

'black'
rect_fill str

Fill color for feature rectangles.

'red'
panel_width int

Width of each panel in pixels.

225
panel_height int

Height of each panel in pixels.

175

Returns:

Name Type Description
Any Any

lets_plot visualization.

Source code in xaitimesynth/visualization.py
def plot_sample(
    X: np.ndarray,
    y: np.ndarray = None,
    feature_masks: Dict[str, np.ndarray] = None,
    components: List[Any] = None,
    sample_idx: int = 0,
    components_to_include: List[str] = None,
    line_color: str = "black",
    rect_fill: str = "red",
    panel_width: int = 225,
    panel_height: int = 175,
) -> Any:
    """Plot a time series sample with its components and feature masks using lets_plot.

    Args:
        X (np.ndarray): Time series data.
        y (Optional[np.ndarray]): Class labels.
        feature_masks (Optional[Dict[str, np.ndarray]]): Dictionary of feature masks.
        components (Optional[List[Any]]): List of TimeSeriesComponents objects.
        sample_idx (int): Index of the sample to plot.
        components_to_include (Optional[List[str]]): List of components to include.
        line_color (str): Color of the time series lines.
        rect_fill (str): Fill color for feature rectangles.
        panel_width (int): Width of each panel in pixels.
        panel_height (int): Height of each panel in pixels.

    Returns:
        Any: lets_plot visualization.
    """
    # Prepare dataset in the expected format
    dataset = {"X": X, "components": components}

    if y is not None:
        dataset["y"] = y
    else:
        # Create dummy labels if not provided (must be int for class label formatting)
        dataset["y"] = np.zeros(len(X), dtype=int)

    if feature_masks is not None:
        dataset["feature_masks"] = feature_masks

    # Set metadata for format detection - assume channels_first if shape is correct
    if len(X.shape) == 3 and X.shape[1] < X.shape[2]:  # Likely channels_first
        dataset["metadata"] = {"data_format": "channels_first"}

    # Set up sample indices
    class_label = dataset["y"][sample_idx]
    sample_indices = {class_label: sample_idx}

    # Create and return plot
    return plot_components(
        dataset,
        sample_indices=sample_indices,
        components=components_to_include,
        line_color=line_color,
        rect_fill=rect_fill,
        panel_width=panel_width,
        panel_height=panel_height,
    )