Source code for uvisbox.Modules.ConeGlyphs.cone_glyphs_stats

 
import numpy as np
# from sklearn.decomposition import PCA
# from uvisbox.Core.BandDepths.vector_depths import calculate_spread_2D, compute_vector_depths_2D
from uvisbox.Core.BandDepths.vector_depths import calculate_spread_3D, compute_vector_depths_3D
from uvisbox.Core.BandDepths.vector_depths import cartesian_to_polar, cartesian_to_spherical



[docs] def cone_glyphs_summary_statistics(ensemble_vectors, percentile): """ Compute vector depth statistics for 3D squid glyphs. Parameters ----------- ensemble_vectors : numpy.ndarray Shape (n, m, 3) - Cartesian ensemble vectors percentile : float Percentile of ensemble members to include based on depth ranking (0-100). Higher values include more vectors (larger glyphs showing more variation). For example, 50 includes the deepest 50%, 95 includes the deepest 95%, and 100 includes all vectors. Returns -------- stats_3d : dict Contains ``ensemble_spherical_vectors``, ``depths``, ``median_vectors``, ``spread_min_vectors``, ``spread_max_vectors``, ``glyph_types``, and ``num_glyphs``. """ num_positions, num_ensemble = ensemble_vectors.shape[0], ensemble_vectors.shape[1] # Convert to spherical coordinates ensemble_spherical_vectors = np.zeros_like(ensemble_vectors) for i in range(num_positions): ensemble_spherical_vectors[i] = cartesian_to_spherical(ensemble_vectors[i]) # Compute depths depths = np.zeros((num_positions, num_ensemble)) for i in range(num_positions): depths[i] = compute_vector_depths_3D(ensemble_spherical_vectors[i]) # Calculate spreads and build min/max/median vectors spread_min_vectors = np.zeros((num_positions, 3)) spread_max_vectors = np.zeros((num_positions, 3)) median_vectors = np.zeros((num_positions, 3)) glyph_types = np.zeros(num_positions, dtype=int) num_glyphs = 0 for i in range(num_positions): median_idx, min_mag_idx, max_mag_idx, min_theta_idx, max_theta_idx, min_phi_idx, max_phi_idx = \ calculate_spread_3D(ensemble_spherical_vectors[i], depths[i], percentile) # Median vector median_vectors[i] = ensemble_spherical_vectors[i][median_idx] if median_idx is not None else [0, 0, 0] # Min/max vectors min_mag_vec = ensemble_spherical_vectors[i][min_mag_idx] if min_mag_idx is not None else [0, 0, 0] max_mag_vec = ensemble_spherical_vectors[i][max_mag_idx] if max_mag_idx is not None else [0, 0, 0] min_phi = ensemble_spherical_vectors[i][min_phi_idx][2] if min_phi_idx is not None else 0 max_phi = ensemble_spherical_vectors[i][max_phi_idx][2] if max_phi_idx is not None else 0 min_theta = ensemble_spherical_vectors[i][min_theta_idx][1] if min_theta_idx is not None else 0 max_theta = ensemble_spherical_vectors[i][max_theta_idx][1] if max_theta_idx is not None else 0 spread_min_vectors[i] = [min_mag_vec[0], min_theta, min_phi] spread_max_vectors[i] = [max_mag_vec[0], max_theta, max_phi] # Classify glyph type if (np.abs(spread_max_vectors[i][0] - spread_min_vectors[i][0]) > 1e-5) and \ (np.abs(spread_max_vectors[i][1] - spread_min_vectors[i][1]) > 1e-5) and \ (np.abs(spread_max_vectors[i][2] - spread_min_vectors[i][2]) > 1e-5): glyph_types[i] = 1 # Full glyph num_glyphs += 1 elif spread_max_vectors[i][0] > 1e-3: glyph_types[i] = 2 # Arrow only # # Compute directional variations (PCA) # # FIXED: Pass percentile directly - getDirectionalVariations will handle threshold # pca_components = getDirectionalVariations( # ensemble_spherical_vectors, depths, percentile, # spread_min_vectors, median_vectors, spread_max_vectors # ) return { 'ensemble_spherical_vectors': ensemble_spherical_vectors, 'depths': depths, 'median_vectors': median_vectors, 'spread_min_vectors': spread_min_vectors, 'spread_max_vectors': spread_max_vectors, 'glyph_types': glyph_types, # 'pca_components': pca_components, 'num_glyphs': num_glyphs }