Skip to content

Commit 6336e3b

Browse files
Refactor error handling in visualization functions
Replaces instructional matplotlib plots with raised exceptions for missing or invalid data in spatial domains and cell communication visualizations. This change ensures that errors fail honestly and provide actionable messages, improving clarity for users and maintaining consistency across visualization functions.
1 parent e494129 commit 6336e3b

1 file changed

Lines changed: 94 additions & 88 deletions

File tree

chatspatial/tools/visualization.py

Lines changed: 94 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Any, Dict, List, Optional, Tuple, Union
1010

1111
import anndata as ad
12+
1213
# Set non-interactive backend for matplotlib to prevent GUI popups on macOS
1314
import matplotlib
1415

@@ -34,17 +35,22 @@
3435
INFERCNVPY_AVAILABLE = False
3536

3637
from ..models.data import VisualizationParameters # noqa: E402
38+
3739
# Import spatial coordinates helper from data adapter
3840
from ..utils.data_adapter import get_spatial_coordinates # noqa: E402
41+
3942
# Import error handling utilities
4043
from ..utils.error_handling import DataCompatibilityError # noqa: E402
4144
from ..utils.error_handling import DataNotFoundError # noqa: E402
4245
from ..utils.error_handling import InvalidParameterError, ProcessingError # noqa: E402
46+
4347
# Import standardized image utilities
4448
from ..utils.image_utils import optimize_fig_to_image_with_cache # noqa: E402
49+
4550
# Import path utilities for safe file operations
4651
from ..utils.path_utils import get_output_dir_from_config # noqa: E402
4752
from ..utils.path_utils import get_safe_output_path # noqa: E402
53+
4854
# Import color utilities for categorical data
4955
from ._color_utils import _ensure_categorical_colors # noqa: E402
5056

@@ -2846,42 +2852,51 @@ async def create_spatial_domains_visualization(
28462852
adata: ad.AnnData, params: VisualizationParameters, context=None
28472853
) -> plt.Figure:
28482854
"""Create spatial domains visualization"""
2849-
# Look for spatial domain results in adata.obs
2850-
domain_keys = [
2851-
col
2852-
for col in adata.obs.columns
2853-
if "spatial_domains" in col.lower() or "domain" in col.lower()
2854-
]
2855+
# Check if user explicitly specified cluster_key parameter
2856+
if params.cluster_key:
2857+
if params.cluster_key not in adata.obs.columns:
2858+
raise ValueError(
2859+
f"Specified cluster_key '{params.cluster_key}' not found in data.\n\n"
2860+
f"Available columns: {', '.join(list(adata.obs.columns)[:20])}"
2861+
)
2862+
domain_keys = [params.cluster_key]
2863+
else:
2864+
# Look for spatial domain results in adata.obs
2865+
domain_keys = [
2866+
col
2867+
for col in adata.obs.columns
2868+
if "spatial_domains" in col.lower() or "domain" in col.lower()
2869+
]
28552870

2856-
# Also check for any categorical clustering results that might represent domains
2871+
# FAIL HONESTLY: Don't guess which column contains spatial domains
28572872
if not domain_keys:
2858-
# Find categorical columns (potential clustering/domain results)
2873+
# Show available categorical columns for user reference
28592874
categorical_cols = [
28602875
col
28612876
for col in adata.obs.columns
28622877
if adata.obs[col].dtype.name in ["object", "category"]
28632878
]
2864-
domain_keys = categorical_cols[
2865-
:5
2866-
] # Take first 5 categorical columns as potential domains
28672879

2868-
if not domain_keys:
2869-
# No spatial domains found, suggest running domain identification first
2870-
fig, ax = plt.subplots(figsize=params.figure_size or (10, 8))
2871-
ax.text(
2872-
0.5,
2873-
0.5,
2880+
error_msg = (
28742881
"No spatial domains found in dataset.\n\n"
2875-
"Please run spatial domain identification first:\n"
2876-
'identify_spatial_domains(data_id="data_1", params={"method": "spagcn"})',
2877-
ha="center",
2878-
va="center",
2879-
transform=ax.transAxes,
2880-
fontsize=12,
2882+
f"Available categorical columns ({len(categorical_cols)} total):\n"
2883+
f" {', '.join(categorical_cols[:15])}"
28812884
)
2882-
ax.set_title("Spatial Domains - Not Available")
2883-
ax.axis("off")
2884-
return fig
2885+
2886+
if len(categorical_cols) > 15:
2887+
error_msg += f"\n ... and {len(categorical_cols) - 15} more"
2888+
2889+
error_msg += (
2890+
"\n\nSOLUTIONS:\n"
2891+
"1. Run spatial domain identification first:\n"
2892+
' identify_spatial_domains(data_id="your_data_id", params={"method": "spagcn"})\n\n'
2893+
"2. Or specify an existing clustering column explicitly:\n"
2894+
' visualize_data(data_id, params={"plot_type": "spatial_domains", "cluster_key": "leiden"})\n\n'
2895+
"NOTE: ChatSpatial uses 'cluster_key' parameter.\n"
2896+
" This maintains consistency with other visualization functions."
2897+
)
2898+
2899+
raise ValueError(error_msg)
28852900

28862901
# Use the first available domain key
28872902
domain_key = domain_keys[0]
@@ -3058,23 +3073,18 @@ async def create_cell_communication_visualization(
30583073
elif lr_columns:
30593074
return _create_lr_expression_plot(adata, lr_columns, params, context)
30603075
else:
3061-
# No communication data found, create instructional plot
3062-
fig, ax = plt.subplots(figsize=params.figure_size or (10, 8))
3063-
ax.text(
3064-
0.5,
3065-
0.5,
3076+
# No communication data found - fail honestly
3077+
raise DataNotFoundError(
30663078
"No cell communication results found in dataset.\n\n"
3067-
"To analyze cell communication, first run:\n"
3068-
'analyze_cell_communication(data_id="data_1", params={"method": "liana"})\n\n'
3069-
"This will generate LIANA+ results for visualization.",
3070-
ha="center",
3071-
va="center",
3072-
transform=ax.transAxes,
3073-
fontsize=12,
3079+
"SOLUTIONS:\n"
3080+
"1. Run cell communication analysis first:\n"
3081+
' analyze_cell_communication(data_id="your_data_id", '
3082+
'params={"species": "human", "cell_type_key": "your_cell_type_column"})\n\n'
3083+
"2. Ensure analysis completed successfully and generated results in:\n"
3084+
" - adata.uns (for cluster-based analysis)\n"
3085+
" - adata.obsm (for spatial analysis)\n\n"
3086+
"Available analysis methods: liana, cellphonedb, cellchat_liana"
30743087
)
3075-
ax.set_title("Cell Communication - Not Available")
3076-
ax.axis("off")
3077-
return fig
30783088

30793089

30803090
def _create_spatial_communication_plot(
@@ -3135,18 +3145,17 @@ def _create_spatial_communication_plot(
31353145
pair_indices = []
31363146

31373147
if not top_pairs:
3138-
fig, ax = plt.subplots(figsize=(8, 6))
3139-
ax.text(
3140-
0.5,
3141-
0.5,
3142-
"No communication pairs found in spatial scores",
3143-
ha="center",
3144-
va="center",
3145-
transform=ax.transAxes,
3148+
raise DataNotFoundError(
3149+
"No communication pairs found in spatial scores.\n\n"
3150+
"POSSIBLE CAUSES:\n"
3151+
"1. Spatial communication analysis generated empty results\n"
3152+
"2. No significant L-R pairs detected in your dataset\n"
3153+
"3. Analysis parameters too stringent\n\n"
3154+
"SOLUTIONS:\n"
3155+
"1. Check if cell communication analysis completed successfully\n"
3156+
"2. Try adjusting analysis parameters (e.g., lower significance threshold)\n"
3157+
"3. Verify spatial coordinates and cell type annotations are correct"
31463158
)
3147-
ax.set_title("Cell Communication - Spatial")
3148-
ax.axis("off")
3149-
return fig
31503159

31513160
# Create subplot layout
31523161
n_pairs = min(len(top_pairs), 6) # Limit to 6 for display
@@ -3234,18 +3243,17 @@ def _create_spatial_communication_plot(
32343243
return fig
32353244

32363245
except Exception as e:
3237-
fig, ax = plt.subplots(figsize=(8, 6))
3238-
ax.text(
3239-
0.5,
3240-
0.5,
3241-
f"Error creating spatial communication plot:\n{str(e)}",
3242-
ha="center",
3243-
va="center",
3244-
transform=ax.transAxes,
3245-
)
3246-
ax.set_title("Cell Communication - Error")
3247-
ax.axis("off")
3248-
return fig
3246+
raise ProcessingError(
3247+
f"Failed to create spatial communication plot: {str(e)}\n\n"
3248+
"POSSIBLE CAUSES:\n"
3249+
"1. Invalid spatial coordinates\n"
3250+
"2. Incompatible data format in adata.obsm['liana_spatial_scores']\n"
3251+
"3. Missing or corrupted communication results\n\n"
3252+
"SOLUTIONS:\n"
3253+
"1. Verify spatial coordinates exist in adata.obsm['spatial']\n"
3254+
"2. Re-run cell communication analysis\n"
3255+
"3. Check data integrity"
3256+
) from e
32493257

32503258

32513259
def _create_cluster_communication_plot(adata, communication_results, params, context):
@@ -3317,18 +3325,17 @@ def _create_cluster_communication_plot(adata, communication_results, params, con
33173325
return fig
33183326

33193327
except Exception as e:
3320-
fig, ax = plt.subplots(figsize=(8, 6))
3321-
ax.text(
3322-
0.5,
3323-
0.5,
3324-
f"Error creating cluster communication plot:\n{str(e)}",
3325-
ha="center",
3326-
va="center",
3327-
transform=ax.transAxes,
3328-
)
3329-
ax.set_title("Cell Communication - Error")
3330-
ax.axis("off")
3331-
return fig
3328+
raise ProcessingError(
3329+
f"Failed to create cluster communication plot: {str(e)}\n\n"
3330+
"POSSIBLE CAUSES:\n"
3331+
"1. Invalid communication results format\n"
3332+
"2. Missing required columns in results DataFrame\n"
3333+
"3. Data corruption in adata.uns\n\n"
3334+
"SOLUTIONS:\n"
3335+
"1. Verify communication analysis completed successfully\n"
3336+
"2. Check adata.uns contains valid LIANA+ results\n"
3337+
"3. Re-run cell communication analysis"
3338+
) from e
33323339

33333340

33343341
def _create_lr_expression_plot(adata, lr_columns, params, context):
@@ -3416,18 +3423,17 @@ def _create_lr_expression_plot(adata, lr_columns, params, context):
34163423
return fig
34173424

34183425
except Exception as e:
3419-
fig, ax = plt.subplots(figsize=(8, 6))
3420-
ax.text(
3421-
0.5,
3422-
0.5,
3423-
f"Error creating LR expression plot:\n{str(e)}",
3424-
ha="center",
3425-
va="center",
3426-
transform=ax.transAxes,
3427-
)
3428-
ax.set_title("Cell Communication - Error")
3429-
ax.axis("off")
3430-
return fig
3426+
raise ProcessingError(
3427+
f"Failed to create ligand-receptor expression plot: {str(e)}\n\n"
3428+
"POSSIBLE CAUSES:\n"
3429+
"1. Missing spatial coordinates\n"
3430+
"2. Invalid L-R columns in adata.obs\n"
3431+
"3. Data format incompatibility\n\n"
3432+
"SOLUTIONS:\n"
3433+
"1. Verify spatial coordinates exist in adata.obsm['spatial']\n"
3434+
"2. Check L-R expression columns in adata.obs\n"
3435+
"3. Re-run cell communication analysis"
3436+
) from e
34313437

34323438

34333439
async def create_multi_gene_umap_visualization(

0 commit comments

Comments
 (0)