Source code for Modules.ContourBoxplot.contour_boxplot

import matplotlib.pyplot as plt
import numpy as np
from .contour_boxplot_stats import contour_boxplot_summary_statistics
from .contour_boxplot_mesh import contour_boxplot_mesh
from .contour_boxplot_vis import visualize_contour_boxplot
from uvisbox.Core.CommonInterface import BoxplotStyleConfig


[docs] def contour_boxplot(ensemble_images, isovalue, boxplot_style=None, ax=None, workers=11): """ Create a contour boxplot visualization from an ensemble of scalar fields. This function processes ensemble images by extracting binary contours at a given isovalue, computing their band depths, and visualizing the uncertainty using band envelopes. Parameters: ----------- ensemble_images : np.ndarray 3D or 4D array containing the ensemble scalar fields. Can be shape (n_ensemble, y_dim, x_dim) or will be rearranged to this format. isovalue : float Threshold value for creating binary images. Pixels with values < isovalue are set to 1. boxplot_style : BoxplotStyleConfig, optional Configuration for the boxplot visualization including percentiles, and median/outlier styling. If None, uses default configuration. The percentile_colormap is used for the band sum visualization. ax : matplotlib.axes.Axes, optional Matplotlib Axes object to plot on. If None, a new figure and axes will be created. workers : int, optional Number of parallel workers for band depth computation. Default is 12. Returns: -------- ax : matplotlib.axes.Axes The Axes object with the contour boxplot visualization. Examples: --------- >>> # Basic usage with defaults >>> ensemble = np.random.randn(50, 100, 100) # 50 ensemble members >>> ax = contour_boxplot(ensemble, isovalue=0.5) >>> # Custom styling >>> from uvisbox.Core.CommonInterface import BoxplotStyleConfig >>> style = BoxplotStyleConfig( ... percentiles=[25, 50, 75], ... percentile_colormap='hot', ... show_median=True, ... show_outliers=True ... ) >>> ax = contour_boxplot(ensemble, isovalue=0.5, boxplot_style=style) """ # Use default config if none provided if boxplot_style is None: boxplot_style = BoxplotStyleConfig() # Make a copy and ensure correct shape (n_ensemble, y_dim, x_dim) ensemble_copy = np.array(ensemble_images, copy=True) # Rearrange to (n_ensemble, y_dim, x_dim) if needed if ensemble_copy.ndim == 3: # Assume input is already (n_ensemble, y, x) or needs rearranging # Check if first dimension is likely the ensemble dimension if ensemble_copy.shape[0] > ensemble_copy.shape[2]: # Likely (y, x, n_ensemble) -> transpose to (n_ensemble, y, x) ensemble_copy = np.transpose(ensemble_copy, (2, 0, 1)) elif ensemble_copy.ndim == 4: # Handle 4D case if needed (e.g., time, ensemble, y, x) raise ValueError("4D arrays not yet supported. Please provide 3D array of shape (n_ensemble, y, x)") # Pipeline: Stats -> Mesh -> Vis # Step 1: Compute summary statistics stats = contour_boxplot_summary_statistics( ensemble_copy, isovalue, boxplot_style=boxplot_style, workers=workers ) # Step 2: Process through mesh (aggregate bands) mesh_data = contour_boxplot_mesh(stats) # Step 3: Visualize ax = visualize_contour_boxplot( mesh_data, boxplot_style=boxplot_style, ax=ax ) return ax