Source code for Modules.ContourBoxplot.contour_boxplot_mesh

import numpy as np


[docs] def contour_boxplot_mesh(summary_statistics, x_coords=None, y_coords=None): """ Process summary statistics to create mesh data for visualization. This function aggregates percentile bands by overwriting values in descending percentile order, creating a single image that shows all bands with appropriate color values. Parameters: ----------- summary_statistics : dict Dictionary from contour_boxplot_summary_statistics containing: - 'median': Binary image for median contour - 'percentile_bands': List of (percentile, band_image) tuples - 'outliers': List of outlier binary images x_coords : np.ndarray, optional 1D array of x-axis coordinates defining the spatial domain. Length must match the image width. If None, pixel indices are used. y_coords : np.ndarray, optional 1D array of y-axis coordinates defining the spatial domain. Length must match the image height. If None, pixel indices are used. Returns: -------- dict Dictionary containing: - 'percentile_bands_image': 2D array where each pixel value represents the percentile/100 it belongs to. Lower percentiles overwrite higher ones. - 'median': Binary image (unchanged from input) - 'outliers': List of binary images (unchanged from input) - 'extent': Tuple (x_min, x_max, y_min, y_max) defining the spatial domain, or None if no coordinates were provided. - 'x_coords': 1D array of x-axis coordinates, or None. - 'y_coords': 1D array of y-axis coordinates, or None. Notes: ------ The aggregation works by: 1. Starting with highest percentile band (e.g., 90th) 2. Painting pixels with value 0.90 where band is non-zero 3. Overwriting with lower percentile bands (e.g., 75th → 0.75) 4. Continuing until lowest percentile This creates a layered visualization where inner bands overwrite outer bands. Examples: --------- >>> stats = contour_boxplot_summary_statistics(ensemble, isovalue=0.5) >>> mesh_data = contour_boxplot_mesh(stats) >>> print(mesh_data['percentile_bands_image'].shape) (100, 100) """ # Get image dimensions from median median = summary_statistics['median'] height, width = median.shape # Validate coordinate arrays if provided if x_coords is not None: x_coords = np.asarray(x_coords) if x_coords.ndim != 1 or len(x_coords) != width: raise ValueError(f"x_coords must be 1D with length {width}, got shape {x_coords.shape}") if y_coords is not None: y_coords = np.asarray(y_coords) if y_coords.ndim != 1 or len(y_coords) != height: raise ValueError(f"y_coords must be 1D with length {height}, got shape {y_coords.shape}") # Compute extent from coordinates if x_coords is not None and y_coords is not None: extent = (x_coords[0], x_coords[-1], y_coords[0], y_coords[-1]) else: extent = None # Initialize aggregated image with zeros percentile_bands_image = np.zeros((height, width), dtype=np.float32) # Get percentile bands and sort by percentile in descending order percentile_bands = summary_statistics['percentile_bands'] sorted_bands = sorted(percentile_bands, key=lambda x: x[0], reverse=True) # Apply bands in descending order (highest to lowest) # Lower percentile values will overwrite higher ones for percentile, band_image in sorted_bands: # Where band is non-zero, overwrite with the band value mask = band_image > 0 percentile_bands_image[mask] = band_image[mask] return { 'percentile_bands_image': percentile_bands_image, 'median': median, 'outliers': summary_statistics['outliers'], 'extent': extent, 'x_coords': x_coords, 'y_coords': y_coords, }