231 lines
7.6 KiB
Python
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!")
|