Files
rustytorch/crates/specialized/rtx-neuro-python/examples/statistical_tests.py
T
2026-03-04 00:08:42 +00:00

231 lines
7.6 KiB
Python

"""
RTX-Neuro Example: Statistical Analysis
This example demonstrates how to perform statistical tests on neuroimaging data
using the rtx_neuro library.
Available functions:
- ttest_1samp: One-sample t-test
- ttest_ind: Independent samples t-test (Welch's)
- ttest_rel: Paired samples t-test
- permutation_t_test: Non-parametric permutation test
- fdr_correction: Benjamini-Hochberg FDR correction
- bonferroni_correction: Bonferroni correction
"""
import rtx_neuro as rtx
import numpy as np
# Set random seed for reproducibility
np.random.seed(42)
# Example 1: One-sample t-test
def ttest_1samp_example():
"""Test if sample mean differs from a hypothesized value."""
# Generate sample data (e.g., ERP amplitudes)
# True mean = 2.0
data = np.random.normal(loc=2.0, scale=0.5, size=30)
# Test against null hypothesis: mean = 0
t_stat, p_value = rtx.stats.ttest_1samp(data, popmean=0.0)
print("One-Sample T-Test")
print(f" Sample mean: {np.mean(data):.3f}")
print(f" t-statistic: {t_stat:.3f}")
print(f" p-value: {p_value:.6f}")
print(f" Significant at alpha=0.05: {p_value < 0.05}")
# Test against different null
t_stat2, p_value2 = rtx.stats.ttest_1samp(data, popmean=2.0)
print(f"\nTest against true mean (2.0):")
print(f" p-value: {p_value2:.6f} (should be > 0.05)")
return t_stat, p_value
# Example 2: Independent samples t-test
def ttest_ind_example():
"""Compare means of two independent groups."""
# Group 1: Control condition
control = np.random.normal(loc=5.0, scale=1.0, size=25)
# Group 2: Treatment condition (with effect)
treatment = np.random.normal(loc=6.5, scale=1.0, size=25)
t_stat, p_value = rtx.stats.ttest_ind(control, treatment)
print("\nIndependent Samples T-Test (Welch's)")
print(f" Control mean: {np.mean(control):.3f}")
print(f" Treatment mean: {np.mean(treatment):.3f}")
print(f" t-statistic: {t_stat:.3f}")
print(f" p-value: {p_value:.6f}")
print(f" Significant at alpha=0.05: {p_value < 0.05}")
return t_stat, p_value
# Example 3: Paired samples t-test
def ttest_rel_example():
"""Compare means of paired observations (e.g., pre/post)."""
# Pre-treatment measurements
pre = np.array([4.5, 5.2, 4.8, 5.1, 4.9, 5.3, 4.7, 5.0, 4.6, 5.4])
# Post-treatment (with improvement)
post = pre + np.random.normal(loc=1.0, scale=0.3, size=10)
t_stat, p_value = rtx.stats.ttest_rel(pre, post)
print("\nPaired Samples T-Test")
print(f" Pre mean: {np.mean(pre):.3f}")
print(f" Post mean: {np.mean(post):.3f}")
print(f" Mean difference: {np.mean(post - pre):.3f}")
print(f" t-statistic: {t_stat:.3f}")
print(f" p-value: {p_value:.6f}")
print(f" Significant at alpha=0.05: {p_value < 0.05}")
return t_stat, p_value
# Example 4: Permutation test
def permutation_test_example():
"""Non-parametric test using permutation of labels."""
# Two groups with small sample sizes (good for permutation)
group_a = np.array([3.2, 3.8, 4.1, 3.5, 4.0, 3.9])
group_b = np.array([5.1, 4.8, 5.3, 5.0, 4.9, 5.2])
# Run permutation test
obs_stat, p_value = rtx.stats.permutation_t_test(
group_a, group_b,
n_permutations=10000,
seed=42 # For reproducibility
)
print("\nPermutation T-Test")
print(f" Group A mean: {np.mean(group_a):.3f}")
print(f" Group B mean: {np.mean(group_b):.3f}")
print(f" Observed statistic: {obs_stat:.3f}")
print(f" p-value: {p_value:.6f}")
print(f" Significant at alpha=0.05: {p_value < 0.05}")
return obs_stat, p_value
# Example 5: Multiple comparison correction
def multiple_comparison_example():
"""Correct for multiple comparisons (e.g., many channels/timepoints)."""
# Simulate p-values from 100 tests
# Mix of true effects and null effects
n_tests = 100
n_true_effects = 10
# Generate p-values
p_values = np.ones(n_tests)
# True effects (small p-values)
true_effect_indices = np.random.choice(n_tests, n_true_effects, replace=False)
for idx in true_effect_indices:
p_values[idx] = np.random.uniform(0.001, 0.01)
# Null effects (uniform p-values)
null_indices = np.setdiff1d(np.arange(n_tests), true_effect_indices)
p_values[null_indices] = np.random.uniform(0.05, 1.0, len(null_indices))
# Add some borderline cases
p_values[0] = 0.04
p_values[1] = 0.03
print("\nMultiple Comparison Correction")
print(f" Number of tests: {n_tests}")
print(f" True effects: {n_true_effects}")
print(f" Uncorrected significant (p<0.05): {np.sum(p_values < 0.05)}")
# FDR correction (less conservative)
rejected_fdr, pvals_fdr = rtx.stats.fdr_correction(p_values, alpha=0.05)
print(f"\n FDR (Benjamini-Hochberg) correction:")
print(f" Rejected: {np.sum(rejected_fdr)}")
print(f" Min corrected p-value: {np.min(pvals_fdr):.6f}")
# Bonferroni correction (more conservative)
rejected_bonf, pvals_bonf = rtx.stats.bonferroni_correction(p_values, alpha=0.05)
print(f"\n Bonferroni correction:")
print(f" Rejected: {np.sum(rejected_bonf)}")
print(f" Min corrected p-value: {np.min(pvals_bonf):.6f}")
return rejected_fdr, rejected_bonf
# Example 6: Realistic neuroimaging workflow
def neuroimaging_workflow_example():
"""
Complete workflow: Load data, compute statistics, correct for multiple comparisons.
"""
print("\n" + "=" * 50)
print("Complete Neuroimaging Statistics Workflow")
print("=" * 50)
# Simulate epoched data: 20 subjects, 64 channels, 100 timepoints
n_subjects = 20
n_channels = 64
n_timepoints = 100
# Condition A (baseline)
condition_a = np.random.normal(0, 1, (n_subjects, n_channels, n_timepoints))
# Condition B (with effect in channels 10-15, timepoints 50-70)
condition_b = condition_a.copy()
condition_b[:, 10:15, 50:70] += np.random.normal(0.8, 0.3, (n_subjects, 5, 20))
print(f"\nData shape: {n_subjects} subjects x {n_channels} channels x {n_timepoints} timepoints")
print(f"True effect: channels 10-15, timepoints 50-70")
# Perform paired t-tests at each channel-timepoint
p_values = np.zeros((n_channels, n_timepoints))
for ch in range(n_channels):
for t in range(n_timepoints):
# Get data for this channel-timepoint across subjects
a = condition_a[:, ch, t]
b = condition_b[:, ch, t]
_, p = rtx.stats.ttest_rel(a, b)
p_values[ch, t] = p
# Flatten for correction
p_flat = p_values.flatten()
# Apply FDR correction
rejected, _ = rtx.stats.fdr_correction(p_flat, alpha=0.05)
rejected_2d = rejected.reshape(n_channels, n_timepoints)
# Count discoveries
n_significant = np.sum(rejected)
n_true_region = 5 * 20 # channels 10-15, timepoints 50-70
# Check true positive region
true_positives = np.sum(rejected_2d[10:15, 50:70])
false_positives = n_significant - true_positives
print(f"\nResults after FDR correction:")
print(f" Total significant: {n_significant}")
print(f" True positives (in effect region): {true_positives}")
print(f" False positives: {false_positives}")
print(f" Sensitivity: {true_positives / n_true_region:.2%}")
return p_values, rejected_2d
if __name__ == "__main__":
print("RTX-Neuro Statistical Analysis Examples")
print("=" * 50)
# Run all examples
ttest_1samp_example()
ttest_ind_example()
ttest_rel_example()
permutation_test_example()
multiple_comparison_example()
neuroimaging_workflow_example()
print("\n" + "=" * 50)
print("All examples completed successfully!")