Visualization of Spatially Continuous Reconstructions

Loading Packages

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

import numpy as np
import pandas as pd
import scanpy as sc
from sklearn.preprocessing import MinMaxScaler

import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec, GridSpecFromSubplotSpec

import sys
sys.path.append("../../../")
from SpatialCOC.preprocess import preprocessing

Loading Data

[3]:
# name of slice
slices = ['ATAC', 'H3K4me3', 'H3K27ac', 'H3K27me3']

# Create a dictionary to store data for each slice
adata_dict = {}

for slice_name in slices:
    # file path
    rna_file_path = f"../../../Data/Mouse_Brain_{slice_name}/adata_RNA.h5ad"
    peaks_file_path = f"../../../Data/Mouse_Brain_{slice_name}/adata_peaks_normalized.h5ad"

    adata_modality_1 = sc.read_h5ad(rna_file_path)
    adata_modality_2 = sc.read_h5ad(peaks_file_path)

    # preprocessing
    print(f"Processing {slice_name} now!")
    adata_modality_1, adata_modality_2 = preprocessing(adata_modality_1, adata_modality_2, 'Spatial-epigenome-transcriptome')

    # storing
    adata_dict[slice_name] = {
        'adata_RNA': adata_modality_1,
        'adata_peaks_normalized': adata_modality_2
    }
Processing ATAC now!
Spatial-epigenome-transcriptome data preprocessing have done!
Dimensions after preprocessed adata_modal_1: (9196, 3000)
Dimensions after preprocessing adata_modal_2: (9196, 121068)
Processing H3K4me3 now!
Spatial-epigenome-transcriptome data preprocessing have done!
Dimensions after preprocessed adata_modal_1: (9513, 3000)
Dimensions after preprocessing adata_modal_2: (9513, 35270)
Processing H3K27ac now!
Spatial-epigenome-transcriptome data preprocessing have done!
Dimensions after preprocessed adata_modal_1: (9323, 3000)
Dimensions after preprocessing adata_modal_2: (9323, 104162)
Processing H3K27me3 now!
Spatial-epigenome-transcriptome data preprocessing have done!
Dimensions after preprocessed adata_modal_1: (9732, 3000)
Dimensions after preprocessing adata_modal_2: (9732, 70470)

Calculating Smoothness

[10]:
import numpy as np
import pandas as pd
import squidpy as sq
import uuid

# Create dictionaries to store the results for each slice
results_dict_RNA = {}
results_dict_EPI = {}

# Define the function to calculate Moran's I
def calculate_morans_i(adata_modality, modality_name):
    # Extract data and calculate mean for each spot/cell
    X1 = adata_modality.X
    if hasattr(X1, 'toarray'):  # Handle sparse matrices
        X1 = X1.toarray()
    X1_mean = np.mean(X1, axis=1)  # Mean expression per spot/cell

    X2 = adata_modality.uns['INR']
    if hasattr(X2, 'toarray'):  # Handle sparse matrices
        X2 = X2.toarray()
    X2_mean = np.mean(X2, axis=1)  # Mean expression per spot/cell

    # Create temporary columns in adata for Moran's I calculation
    temp_col_X1 = f"temp_X1_{uuid.uuid4().hex}"
    temp_col_X2 = f"temp_X2_{uuid.uuid4().hex}"

    adata_modality.obs[temp_col_X1] = X1_mean
    adata_modality.obs[temp_col_X2] = X2_mean

    # Construct the spatial graph
    try:
        sq.gr.spatial_neighbors(adata_modality, coord_type='generic')
    except:
        # If generic fails, try grid
        sq.gr.spatial_neighbors(adata_modality, coord_type='grid')

    moran_results = {}

    # Calculate Moran's I for X1 (RNA or EPI)
    try:
        sq.gr.spatial_autocorr(
            adata_modality,
            mode="moran",
            n_perms=100,
            attr="obs",
            genes=[temp_col_X1],
            n_jobs=4,
            seed=2024
        )
        moran_X1 = adata_modality.uns['moranI'].loc[temp_col_X1, 'I']
        moran_results[modality_name] = moran_X1
    except Exception as e:
        print(f"Error calculating Moran's I for {modality_name}: {e}")
        moran_results[modality_name] = None

    # Calculate Moran's I for INR
    try:
        sq.gr.spatial_autocorr(
            adata_modality,
            mode="moran",
            n_perms=100,
            attr="obs",
            genes=[temp_col_X2],
            n_jobs=4,
            seed=2024
        )
        moran_X2 = adata_modality.uns['moranI'].loc[temp_col_X2, 'I']
        moran_results['INR'] = moran_X2
    except Exception as e:
        print(f"Error calculating Moran's I for INR: {e}")
        moran_results['INR'] = None

    # Clean up temporary columns
    if temp_col_X1 in adata_modality.obs:
        del adata_modality.obs[temp_col_X1]
    if temp_col_X2 in adata_modality.obs:
        del adata_modality.obs[temp_col_X2]

    return moran_results

# Loop through each slice's data and calculate Moran's I for RNA data
print("Processing RNA data...")
for slice_name, data in adata_dict.items():
    adata_modality_1 = data['adata_RNA']  # RNA data

    # Calculate Moran's I
    moran_results = calculate_morans_i(adata_modality_1, 'RNA')

    # Print Moran's I information for the current slice
    print(f"Slice: {slice_name}")
    print(f"Moran's I for RNA: {moran_results.get('RNA', 'N/A')}")
    print(f"Moran's I for INR: {moran_results.get('INR', 'N/A')}")
    print()

    # Store the results in the dictionary
    results_dict_RNA[slice_name] = {
        'RNA': moran_results.get('RNA'),
        'INR': moran_results.get('INR')
    }

# Loop through each slice's data and calculate Moran's I for EPI data
print("Processing EPI data...")
for slice_name, data in adata_dict.items():
    adata_modality_2 = data['adata_peaks_normalized']  # EPI data

    # Calculate Moran's I
    moran_results = calculate_morans_i(adata_modality_2, 'EPI')

    # Print Moran's I information for the current slice
    print(f"Slice: {slice_name}")
    print(f"Moran's I for EPI: {moran_results.get('EPI', 'N/A')}")
    print(f"Moran's I for INR: {moran_results.get('INR', 'N/A')}")
    print()

    # Store the results in the dictionary
    results_dict_EPI[slice_name] = {
        'EPI': moran_results.get('EPI'),
        'INR': moran_results.get('INR')
    }

# Create summary DataFrames
moran_summary_RNA = pd.DataFrame.from_dict(results_dict_RNA, orient='index')
moran_summary_EPI = pd.DataFrame.from_dict(results_dict_EPI, orient='index')

print("RNA Moran's I Summary:")
print(moran_summary_RNA)
print("\nEPI Moran's I Summary:")
print(moran_summary_EPI)

# Save to Excel with different sheets
with pd.ExcelWriter('moran_summary.xlsx') as writer:
    moran_summary_RNA.to_excel(writer, sheet_name='RNA_Results')
    moran_summary_EPI.to_excel(writer, sheet_name='EPI_Results')

print("Results saved to 'moran_summary.xlsx'")
Processing RNA data...
Slice: ATAC
Moran's I for RNA: 0.5726459320582865
Moran's I for INR: 0.9416151727753987

Slice: H3K4me3
Moran's I for RNA: 0.41542874418963655
Moran's I for INR: 0.9068438955189224

Slice: H3K27ac
Moran's I for RNA: 0.43337861194558414
Moran's I for INR: 0.9127113772274553

Slice: H3K27me3
Moran's I for RNA: 0.5406258122234978
Moran's I for INR: 0.9344302635319943

Processing EPI data...
Slice: ATAC
Moran's I for EPI: 0.3740779267580077
Moran's I for INR: 0.8051743316702477

Slice: H3K4me3
Moran's I for EPI: 0.3252534950486202
Moran's I for INR: 0.788598606261491

Slice: H3K27ac
Moran's I for EPI: 0.26778055678528934
Moran's I for INR: 0.7646067979845128

Slice: H3K27me3
Moran's I for EPI: 0.30533128533918286
Moran's I for INR: 0.6811305235993297

RNA Moran's I Summary:
               RNA       INR
ATAC      0.572646  0.941615
H3K4me3   0.415429  0.906844
H3K27ac   0.433379  0.912711
H3K27me3  0.540626  0.934430

EPI Moran's I Summary:
               EPI       INR
ATAC      0.374078  0.805174
H3K4me3   0.325253  0.788599
H3K27ac   0.267781  0.764607
H3K27me3  0.305331  0.681131
Results saved to 'moran_summary.xlsx'

Ploting

[6]:
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler

# Create a dictionary to store the results for each slice
results_dict = {}

# Define the function to calculate gradients
def calculate_gradients(adata_modality_1):
    # Extract data
    X1 = adata_modality_1.X
    X1 = np.sum(X1, axis=1)

    X2 = adata_modality_1.uns['INR']
    X2 = np.sum(X2, axis=1)

    coords = adata_modality_1.obsm['spatial']

    # Create DataFrame
    data_df = pd.DataFrame({
        'x': coords[:, 0],
        'X1': X1,
        'X2': X2
    })

    # Group by 'x' and sum
    sum_by_x = data_df.groupby('x')[['X1', 'X2']].sum().reset_index()

    # Normalize
    scaler = MinMaxScaler()
    sum_by_x[['X1', 'X2']] = scaler.fit_transform(sum_by_x[['X1', 'X2']])

    # Calculate gradients
    gradient_X1 = np.gradient(sum_by_x['X1'])
    gradient_X2 = np.gradient(sum_by_x['X2'])

    # Calculate absolute gradient values
    abs_gradient_X1 = np.abs(gradient_X1).sum()
    abs_gradient_X2 = np.abs(gradient_X2).sum()

    return sum_by_x, abs_gradient_X1, abs_gradient_X2

# Loop through each slice's data and calculate gradients
for slice_name, data in adata_dict.items():
    adata_modality_1 = data['adata_RNA']  # Assuming we use adata_RNA for calculations

    # Calculate gradients
    sum_by_x, abs_gradient_X1, abs_gradient_X2 = calculate_gradients(adata_modality_1)

    # Print gradient information for the current slice
    print(f"Slice: {slice_name}")
    print(f"Absolute Gradient X1: {abs_gradient_X1}")
    print(f"Absolute Gradient X2: {abs_gradient_X2}")
    print()

    # Store the results in the dictionary
    results_dict[slice_name] = {
        'sum_by_x': sum_by_x,
        'abs_gradient_X1': abs_gradient_X1,
        'abs_gradient_X2': abs_gradient_X2
    }
Slice: ATAC
Absolute Gradient X1: 4.9154729604586
Absolute Gradient X2: 3.34928229467236

Slice: H3K4me3
Absolute Gradient X1: 6.22466341946492
Absolute Gradient X2: 3.8888274080315357

Slice: H3K27ac
Absolute Gradient X1: 4.102195516368743
Absolute Gradient X2: 3.9591985675251324

Slice: H3K27me3
Absolute Gradient X1: 6.374758070169197
Absolute Gradient X2: 4.204938944467514

Saving Results

[8]:
import pandas as pd

output_path = 'gradient_results.xlsx'

# 创建 Excel writer
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
    for slice_name, result in results_dict.items():
        df = result['sum_by_x'].copy()

        safe_name = str(slice_name)[:31]
        for char in ['/', '\\', '[', ']', ':', '*', '?']:
            safe_name = safe_name.replace(char, '_')

        df.to_excel(writer, sheet_name=safe_name, index=False)

print(f"✓ Results saved to: {output_path}")
print(f"  Total sheets: {len(results_dict)}")
✓ Results saved to: gradient_results.xlsx
  Total sheets: 4
[44]:
import pandas as pd

# 从Excel文件读取平滑度数据
moran_data = pd.read_excel('moran_summary.xlsx', sheet_name='RNA_Results', index_col=0)

for slice_name, results in results_dict.items():
    print(f"Plot the {slice_name} slice.")
    sum_by_x = results['sum_by_x']

    rna_smoothness = moran_data.loc[slice_name, 'RNA']
    inr_smoothness = moran_data.loc[slice_name, 'INR']

    # Create a figure
    fig = plt.figure(figsize=(18, 7))

    # Create an outer GridSpec to define the overall layout of the figure
    outer_gs = GridSpec(1, 2, width_ratios=[9, 9], wspace=0.2)

    # Create the first inner GridSpec, containing the first and second subplots
    inner_gs1 = GridSpecFromSubplotSpec(1, 2, subplot_spec=outer_gs[0], width_ratios=[6, 1], wspace=0.05)

    # Create the second inner GridSpec, containing the third and fourth subplots
    inner_gs2 = GridSpecFromSubplotSpec(1, 2, subplot_spec=outer_gs[1], width_ratios=[6, 1], wspace=0.05)

    color = '#669bbc'

    # Set the global font to Arial and the global font size
    plt.rcParams['font.family'] = 'Arial'
    plt.rcParams['font.size'] = 24

    # Plot the first subplot: Bar chart of X1
    ax1 = fig.add_subplot(inner_gs1[0])
    ax1.bar(sum_by_x['x'], sum_by_x['X1'], color=color)
    ax1.set_xlabel('Spatial-x axis')
    ax1.set_ylabel('Original expression')
    ax1.set_ylim(0, 1.05)
    ax1.set_xlim(0, 99)
    for spine in ax1.spines.values():
        spine.set_linewidth(1.5)

    # Plot the second subplot: Bar chart of the absolute gradient of X1
    ax2 = fig.add_subplot(inner_gs1[1])
    bar2 = ax2.bar(['X1'], [rna_smoothness], color=color)
    ax2.set_ylim(0, 1.05)
    ax2.set_xlabel('Continuity', fontsize=24)
    ax2.set_xticks([])
    ax2.set_yticklabels([])
    ax2.set_yticks([])
    ax2.spines['top'].set_visible(False)
    ax2.spines['right'].set_visible(False)
    ax2.spines['bottom'].set_visible(False)
    ax2.spines['left'].set_visible(False)

    for bar in bar2:
        yval = bar.get_height()
        ax2.text(bar.get_x() + bar.get_width()/2, yval + 0.01, f'{yval:.2f}', ha='center', va='bottom')

    # Plot the third subplot: Line chart of X2 with filled area under the curve
    ax3 = fig.add_subplot(inner_gs2[0])
    ax3.plot(sum_by_x['x'], sum_by_x['X2'], color=color, linestyle='-')
    ax3.fill_between(sum_by_x['x'], sum_by_x['X2'], color=color, alpha=0.6)
    ax3.set_xlabel('Spatial-x axis')
    ax3.set_ylabel('INR')
    ax3.set_ylim(0, 1.05)
    ax3.set_xlim(0, 99)
    for spine in ax3.spines.values():
        spine.set_linewidth(1.5)

    # Plot the fourth subplot: Bar chart of the absolute gradient of X2
    ax4 = fig.add_subplot(inner_gs2[1])
    bar4 = ax4.bar(['X2'], [inr_smoothness], color=color)
    ax4.set_ylim(0, 1.05)
    ax4.set_xlabel('Continuity', fontsize=24)
    ax4.set_xticks([])
    ax4.set_yticklabels([])
    ax4.set_yticks([])
    ax4.spines['top'].set_visible(False)
    ax4.spines['right'].set_visible(False)
    ax4.spines['bottom'].set_visible(False)
    ax4.spines['left'].set_visible(False)
    # Display value labels above the bars
    for bar in bar4:
        yval = bar.get_height()
        ax4.text(bar.get_x() + bar.get_width()/2, yval + 0.01, f'{yval:.2f}', ha='center', va='bottom')

    plt.subplots_adjust(left=0.07, right=0.97, top=0.92, bottom=0.15)
    plt.suptitle('')

    plt.savefig(f'{slice_name}/INR_RNA.png', dpi=500)
    plt.savefig(f'{slice_name}/INR_RNA.eps')

    plt.show()
Plot the ATAC slice.
The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.
../../_images/Reproduction_Mouse_Brain_Datasets_1_INR_11_2.png
Plot the H3K4me3 slice.
The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.
../../_images/Reproduction_Mouse_Brain_Datasets_1_INR_11_5.png
Plot the H3K27ac slice.
The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.
../../_images/Reproduction_Mouse_Brain_Datasets_1_INR_11_8.png
Plot the H3K27me3 slice.
The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.
../../_images/Reproduction_Mouse_Brain_Datasets_1_INR_11_11.png