Multivariate statistics coursework (UPF, Statistical Models, 3rd year, autumn 2023), written in MATLAB. Three labs, each a self-contained hypothesis-testing exercise:
| Lab | Topic | Techniques implemented |
|---|---|---|
| Lab 2 | Comparing group means | MANOVA via Wilks' Lambda, repeated-measures ANOVA, multivariate/univariate post-hoc tests, Q-Q and chi-square normality assessment |
| Lab 4 | Resampling | Parametric and non-parametric bootstrap, bootstrap percentile intervals, bias correction, Hotelling's T² under H0 by resampling |
| Lab 6 | Dimensionality reduction | Permutation test for significant principal components, PCA, ISOMAP, Kernel PCA, Hotelling's T² on reduced spaces |
Almost everything is implemented from first principles rather than by calling MATLAB's
built-in manova1 / pca / bootstrp: Mahalanobis distances, SSB/SSW decomposition,
Wilks' Lambda and its F transformation, the bootstrap loops, the covariance eigen-decomposition,
the ISOMAP geodesic embedding and the Kernel PCA Gram matrix are all written out explicitly.
That was the point of the course — the built-ins appear only for distribution quantiles
(norminv, chi2inv, finv, tinv, prctile) and for plotting.
Taken from the students' own commentary in the scripts:
- Normality is checked before, not assumed. Every test is preceded by univariate Q-Q correlation checks against the critical-point table, then chi-square plots of Mahalanobis distances for every pair of variables and for all variables jointly. A chi-square plot is read through the slope of a fitted line: a slope far from 1 is evidence against normality.
- When normality fails, fix the data rather than the test. In Lab 2 exercise 1, outlier removal was tried and rejected (the best corrected Q-Q correlation reachable was 0.9279 against a critical value of 0.9591), and a log transformation was used instead — which did bring Group A into a range where normality could not be rejected.
- Equal covariance is verified before MANOVA, by checking that the ratio of the squared
diagonal elements of the two group covariance matrices lies inside
[1/4, 4]. - Bootstrap is used as a cross-check, not a replacement. In Labs 4 and 6, every parametric conclusion is repeated with a bootstrap percentile interval, and the two are compared explicitly.
- Multiple comparisons are Bonferroni-corrected (Lab 4 uses
alpha_corrected = alpha/3).
src/
lab2_ex1_gait_manova.m MANOVA on gait-analysis data, 4 patient groups x 3 variables
lab2_ex2_repeated_measures.m Repeated-measures ANOVA on 5 lab grades for 57 students
lab2_ex3_fish_manova.m MANOVA on a 3-method x 4-attribute fish tasting panel
lab4_bootstrap.m Lab 4, converted from the .mlx live script
lab6_dimensionality_reduction.m Lab 6, converted from the .mlx live script
livescripts/
lab4_bootstrap.mlx Original MATLAB live scripts (the submitted artifacts)
lab6_dimensionality_reduction.mlx
data/ The five course-provided .xlsx datasets (all anonymous)
docs/
RESULTS.md Every numeric result recovered from the stored outputs
SM2023_Lab02.pdf Course-provided lab statements
SM2023_Lab04.pdf
SM2023_Lab06.pdf
| File | Shape | Contents |
|---|---|---|
SM_GaitAnalysis_4.xlsx |
81 rows x 4 cols | tSWING, tDBLSTANCE, Velocity, Patient Group (A=12, B=23, C=20, D=26) |
SM22_LabGrades.xlsx |
57 rows x 5 cols | Anonymous grades for LAB 1, 2, 4, 5, 6 |
SM23_Lab_2_Fish.xlsx |
3 methods x 12 samples x 4 cols | Aroma, Flavor, Texture, Moisture for cooking Methods A/B/C |
SM23_Lab_4_Height_and_Weight.xlsx |
179 rows x 2 cols | Weight, Height (BMI is derived in the script) |
SM23_BabyFeatures.xlsx |
285 rows x 138 cols | Column 1 is the boy/girl label (174 boys, 111 girls); columns 2-138 are 137 features |
None of these contain names or identifiers — they are anonymous course datasets.
Requires MATLAB with the Statistics and Machine Learning Toolbox
(norminv, chi2inv, finv, tinv, prctile, skewness, randsample, anova1).
No add-ons beyond that.
cd data % the scripts call xlsread with bare filenames, so run from data/
run('../src/lab2_ex1_gait_manova.m')
run('../src/lab2_ex2_repeated_measures.m')
run('../src/lab2_ex3_fish_manova.m')
run('../src/lab4_bootstrap.m')
run('../src/lab6_dimensionality_reduction.m')Lab 4 takes 2-3 minutes end to end (the authors' own note in the live script) because of the number of bootstrap replicates and plots. Lab 6 runs 1000 column-wise permutations of a 285x137 matrix and is also slow.
To open the originals as live scripts instead, open livescripts/*.mlx in MATLAB — those
keep the rendered equations, the formatted prose and the stored figures, none of which
survive the conversion to .m.
All numbers below were read out of the stored outputs embedded in the original .mlx
files (matlab/output.xml) or out of comments the authors wrote after running the code.
Nothing here was re-run or recomputed.
| Quantity | Value |
|---|---|
| Chi-square plot fit, Group A dims 1-2 (original data) | slope 0.6540, intercept 0.5627, correlation 0.9770 |
| Bootstrap p-values (B=1000) for correlation / slope / intercept | 0.2348 / 0.5874 / 0.4186 |
| Bonferroni-corrected alpha (alpha = 0.07, 3 tests) | 0.0233 |
| Percentile interval, correlation | [0.8263, 0.9938] |
| Percentile interval, slope | [0.3508, 1.1586] |
| Percentile interval, intercept | [-0.4176, 1.1517] |
| Chi-square plot fit, Group A all 3 dims | slope 0.4614, intercept 1.3935, correlation 0.9147 |
| Hotelling's T² observed, Group A (n=12) vs Group D (m=26) | 32.2547 |
| Bootstrap critical interval for T² | [-0.2696, 12.6125] |
| Bootstrap p-value for T² | 9.9900e-04 |
| Skewness of BMI, original dataset | 13.1274 |
| Bootstrap bias of the skewness estimator | -5.7831 |
| Bootstrap standard error of the skewness estimator | 5.3158 |
| Bias-corrected skewness | 7.3443 |
| Student-t interval for skewness (alpha = 0.05) | [-3.1454, 17.8339] |
| Bootstrap percentile interval for skewness | [13.0973, 25.8856] |
Conclusions as written by the authors: none of the three bootstrap p-values approach the corrected alpha, so multivariate normality of Group A cannot be rejected; all three observed statistics fall inside their percentile intervals, which agrees. For Problem 2, T² = 32.2547 exceeds the critical value and the p-value is far below alpha, so the means of Groups A and D differ. For Problem 3 the Student-t interval and the bootstrap percentile interval for skewness disagree — the authors note the t interval assumes normality, which does not hold here, so it is not accurate.
| Quantity | Value |
|---|---|
| Significant principal components (permutation test, 1000 perms, alpha=0.05) | 41 of 137 |
| Retained variance at 41 components | 0.5713 |
| Bias-corrected bootstrap 95% CI for retained variance | [0.4854, 0.6659] |
| Covariance-similarity check (non-similar diagonal elements) | 0 |
| Hotelling's T² with Spool, boys vs girls, 41 PCA dims | 74.9842 vs critical 31.1019 → reject H0 |
| Bootstrap 95% CI for that statistic | [90.7783, 205.5828] → observed lies outside → reject H0 |
| Variance retained by ISOMAP, 3 dims | 0.29237 |
| Variance retained by PCA, 3 dims | 0.57133 |
| Hotelling's T² for large independent populations, 3 ISOMAP dims | 6.3087 vs critical 7.8147 → fail to reject H0 |
| Variance retained by Kernel PCA, 42 dims | 0.57083 |
| Hotelling's T² with Spool, Kernel PCA | 61.3985 vs critical 32.1522 → reject H0 |
| Bootstrap 95% CI for the KPCA statistic | [79.8651, 177.1283] → reject H0 |
Conclusions as written by the authors: the boy/girl mean difference is detectable in the 41-dimensional PCA space and in the Kernel PCA space, but not in the 3-dimensional ISOMAP embedding — 3 dimensions simply do not carry enough of the variance. Kernel PCA retains essentially the same variance as linear PCA (0.57083 vs 0.57133) and reaches the same conclusion, which the authors read as evidence that the relevant structure in this dataset is linear and KPCA buys nothing here.
The permutation-test component count (41) and the bootstrap intervals will shift slightly between runs — the scripts do not set a random seed. The authors flag this themselves ("41 may vary slightly between executions due to the permutations").
Lab 2 was submitted as plain .m files, which do not store their outputs, so the only
recorded numbers are the ones the authors wrote into their comments:
- Best Q-Q correlation reachable for Group D variable 2 after outlier removal: 0.9279, against a critical value of 0.9591 → normality still rejected.
- Group A slopes were "very far from 1" before a log transform and "almost 0.8" after it (the authors' words; the exact per-plot slopes were not recorded).
- Lab 2 ex. 2: at 99% confidence the variable pairs whose difference intervals exclude 0 are 1-2, 1-3, 1-5 and 3-4.
- Lab 2 ex. 3: the multivariate post-hoc test rejects equality for pairs A-C and B-C, but not A-B.
- Lab 4 and Lab 6
.mfiles are machine-converted from the.mlxlive scripts by extractingmatlab/document.xmland emitting code paragraphs verbatim with prose as comments. The code is byte-for-byte the original; what is lost is: rendered LaTeX equations (Lab 4 Problem 2b's Hotelling's T² formula was an embedded image and is simply absent from the.m), inline figures, and text formatting. The original.mlxfiles are shipped inlivescripts/for that reason. - An operator-precedence bug was fixed. The original code wrote
f_transform = ((1-(wilks_lambda)^1/2) / 6) / ((wilks_lambda^1/2)/df2). In MATLAB^binds tighter than/, sowilks_lambda^1/2evaluates as(wilks_lambda^1)/2— half of Wilks' Lambda — rather than the square root the Wilks-to-F transformation requires. It now readswilks_lambda^(1/2)in bothlab2_ex1_gait_manova.mandlab2_ex3_fish_manova.m. Consequence: the F-statistics those two lines produce are now different from the values stored indocs/RESULTS.md, which were recorded with the original code. The fix is correct; the stored outputs are the historical record. They are not expected to agree. - The PDFs in
docs/are course-provided lab statements, not student work.
- José Mª Pérez Clar
- Guillem Sans
Universitat Pompeu Fabra, Statistical Models, 3rd year, Trimester 1, 2023.