CHAOS Visualization of the Mouse Spleen Dataset

Loading Packages

[2]:
import warnings
warnings.filterwarnings('ignore')

import scanpy as sc
import numpy as np
import matplotlib.pyplot as plt

import sys
sys.path.append('../Mouse_Spleen/')
sys.path.append('../../../')
from SpatialCOC.utils import calculate_chaos

Loading Data

[3]:
slices = ['1', '2', '3']
# List of methods
methods = ['SpatialCOC', 'COSMOS', 'SpatialGlue', 'Seurat', 'MultiVI', 'MultiMAP', 'STAGATE', 'SpaGCN', 'Modality1', 'Modality2']
# Create a dictionary to store data for each slice
adata_results = {}

# Load data and store it
for slice_name in slices:
    path = f'../../Mouse_Thymus_Replicate{slice_name}.h5ad'
    result = sc.read_h5ad(path)
    adata_results[slice_name] = {
        'adata': result,
        'chaos_scores': {}
    }

Calculating CHAOS

[7]:
for slice_name, data in adata_results.items():
    adata = data['adata']
    spatial_coords = adata.obsm['spatial']
    for method in adata.obs.columns:
        labels = adata.obs[method].values
        chaos_values = calculate_chaos(spatial_coords, labels)
        data['chaos_scores'][method] = chaos_values
[6]:
import pandas as pd
import numpy as np

# Create the result dataframe
all_results = {}

for slice_name, data in adata_results.items():
    adata = data['adata']
    spatial_coords = adata.obsm['spatial']

    slice_chaos_results = {}

    for method in adata.obs.columns:
        labels = adata.obs[method].values
        chaos_values = calculate_chaos(spatial_coords, labels)
        data['chaos_scores'][method] = chaos_values

        if isinstance(chaos_values, (list, np.ndarray)):
            slice_chaos_results[method] = [float(v) for v in chaos_values]
        else:
            slice_chaos_results[method] = [float(chaos_values)]

    all_results[slice_name] = slice_chaos_results

# Save
output_path = 'chaos_results.xlsx'
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:

    for slice_name, chaos_results in all_results.items():

        df = pd.DataFrame.from_dict(chaos_results, orient='index')

        # Rename
        df.columns = [f'CHAOS_{i+1}' for i in df.columns]
        df.index.name = 'Method'
        df = df.reset_index()

        sheet_name = str(slice_name)[:31]
        df.to_excel(writer, sheet_name=sheet_name, index=False)

        print(f"Sheet '{sheet_name}' created with {len(df)} methods")

print(f"\nResults saved to: {output_path}")
Sheet '1' created with 10 methods
Sheet '2' created with 10 methods
Sheet '3' created with 10 methods

Results saved to: chaos_results.xlsx

Ploting

[11]:
# Define methods and corresponding colors
methods = ['SpatialCOC', 'COSMOS','SpatialGlue', 'Seurat', 'MultiVI', 'MultiMAP', 'STAGATE', 'SpaGCN']
colors = ['#D0836F', '#df871b', '#E4BE64', '#94aebd', '#87AC9A', '#9B8169', '#8D8E99', '#F5ECBA']

# Set global font size and font
fontsize = 20
plt.rcParams['font.sans-serif'] = ['Arial']
plt.rcParams['font.size'] = fontsize


# Iterate through each slice
for slice_name in slices:
    # Get the CHAOS score data for the current slice
    chaos_scores = adata_results[slice_name]['chaos_scores']

    # Prepare data: CHAOS scores for each method
    data = [chaos_scores[method] for method in methods]

    # Calculate the medians of Modality1 and Modality2 separately
    median_modality1 = np.median(data[0])  # Median of Modality1
    median_modality2 = np.median(data[1])  # Median of Modality2

    # Determine the best performance between the two medians
    max_median = min(median_modality1, median_modality2)

    # Automatically calculate the y-axis range
    all_scores = np.concatenate(data)  # Combine all data into one array
    y_min = np.min(all_scores) - 0.05 * (np.max(all_scores) - np.min(all_scores))  # Slightly less than the minimum value
    y_max = np.max(all_scores) + 0.05 * (np.max(all_scores) - np.min(all_scores))  # Slightly more than the maximum value

    # Create a new figure
    plt.figure(figsize=(5, 5))

    # Draw the boxplot
    bp = plt.boxplot(data, patch_artist=True,
                     boxprops=dict(linestyle='-', linewidth=1, edgecolor='black'),  # Box edges
                     medianprops=dict(linestyle='-', linewidth=1, color='black'),  # Median line
                     whiskerprops=dict(linestyle='-', linewidth=1, color='black'),  # Whiskers
                     capprops=dict(linestyle='-', linewidth=1, color='black'),  # Caps
                     widths=0.85,  # Box width
                     showfliers=False)  # Do not show outliers

    # Color the boxes
    for patch, color in zip(bp['boxes'], colors):
        patch.set_facecolor(color)

    # Remove x-axis tick labels
    plt.xticks([])  # Remove x-axis tick labels

    # Set the unified y-axis range
    # y_max = 2.6 if slice_name == '1' else 3
    # plt.ylim(y_min, y_max)

    # Add y-axis label
    plt.ylabel('CHAOS Score', fontsize=fontsize)

    # Thicken the plot's border
    for spine in plt.gca().spines.values():
        spine.set_linewidth(2)

    # Add grid lines
    plt.grid(axis='y', color='gray', linestyle='--', alpha=1, zorder=-10, linewidth=1.5)

    # Add vertical dashed lines
    for x in range(1, len(methods) + 1):
        plt.axvline(x=x, color='gray', linestyle='--', alpha=1, zorder=-10, linewidth=1.5)

    # Draw a horizontal line to represent the best median performance
    plt.axhline(y=max_median, color='red', linestyle='--', linewidth=2)

    # Print plotting information
    print(f"Drawing plot for {slice_name} - CHAOS Score")

    plt.tight_layout(pad=0.1, rect=[0, 0, 1, 1])

    # Save the image
    # save_path = f"./replicate{slice_name}/"
    # plt.savefig(save_path + f"CHAOS_Score.png", dpi=500)
    # plt.savefig(save_path + f"CHAOS_Score.eps")

    plt.show()
Drawing plot for 1 - CHAOS Score
../../_images/Reproduction_Mouse_Thymus_Datasets_5_CHAOS_9_1.png
Drawing plot for 2 - CHAOS Score
../../_images/Reproduction_Mouse_Thymus_Datasets_5_CHAOS_9_3.png
Drawing plot for 3 - CHAOS Score
../../_images/Reproduction_Mouse_Thymus_Datasets_5_CHAOS_9_5.png