Skip to content

Commit 924f52c

Browse files
torwagerclaude
andcommitted
Factor-score visualizations (plotFactorScores) + project-summary generator
- matlab/plotFactorScores.m: loads the saved per-model factors and visualizes them 6 ways with CANlab core tools — time series (plot_matrix_cols), mango heatmap, force-directed graph (canlab_force_directed_graph), correlation matrix (plot_correlation_matrix), t-SNE, and UMAP (run_umap) — all colored by feature category (domain hues matching the feature summary table). Returns the factor struct; saves svg+png to matlab/figures/. Verified end-to-end. - tools/build_project_summary.py + docs/summarize_project.md: reusable "summarize the project now" recipe. Writes a <=1/2 page narrative review + the feature table + 3 figures with captions into docs/feature_summary_table.docx, pulling live numbers from the template, corpus_stats, and factor_reducibility. - analysis/factor_reducibility.json: per-category first-PC % (from the MATLAB EFA). - CONTENTS.md/walkthrough.m: document the new functions. .gitignore: keep figure PNGs, exclude the large regenerable SVGs; ignore Office ~$ temp files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 04ae6ec commit 924f52c

15 files changed

Lines changed: 539 additions & 0 deletions

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ __pycache__/
66
# macOS
77
.DS_Store
88

9+
# Office temp/lock files (created while a .docx/.xlsx is open)
10+
~$*
11+
912
# mdBook documentation site — both are generated:
1013
# book/src by tools/build_book.py (mirror of docs/)
1114
# book/book by `mdbook build`
@@ -19,6 +22,10 @@ __pycache__/
1922
# ~36 MB). Regenerate in MATLAB with extractCategoryFactors; kept Dropbox-only.
2023
/analysis/extracted_factors.mat
2124

25+
# plotFactorScores exports: keep the lightweight PNGs in git; the vector SVGs are large
26+
# (~6 MB, the force-directed one alone is ~2.6 MB) and regenerable, so keep them Dropbox-only.
27+
/matlab/figures/*.svg
28+
2229
# Reference PDFs — copyrighted publisher/preprint PDFs of the papers behind each
2330
# model. Kept in Dropbox only, excluded from git (the docs link to public arXiv/DOI
2431
# URLs instead, which resolve on the web book). See PDFs/README.md.

analysis/factor_reducibility.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"generated":"from extractCategoryFactors corpus","n_timepoints":28237,"categories":[{"category":"emonet","class":"affect","model":"Kragel2019-EmoNet(emonet-pytorch)","nVars":20,"pc1_pct":6.8},{"category":"face_emotion","class":"affect","model":"HSEmotion-enet_b0_8_va_mtl","nVars":8,"pc1_pct":20.7},{"category":"text_emotion","class":"affect","model":"SamLowe/roberta-base-go_emotions","nVars":28,"pc1_pct":9.2},{"category":"text_sentiment","class":"affect","model":"cardiffnlp/twitter-roberta-base-sentiment-latest","nVars":3,"pc1_pct":53.2},{"category":"audioset_tags","class":"audio","model":"AST(BEATs-substitute):MIT/ast-finetuned-audioset-10-10-0.4593","nVars":527,"pc1_pct":4.5},{"category":"clap_embedding","class":"audio","model":"laion/clap-htsat-unfused","nVars":512,"pc1_pct":17.6},{"category":"clap_probe","class":"audio","model":"laion/clap-htsat-unfused","nVars":12,"pc1_pct":20.9},{"category":"chroma","class":"audio","model":"librosa","nVars":12,"pc1_pct":56.3},{"category":"mfcc","class":"audio","model":"librosa","nVars":13,"pc1_pct":31.5},{"category":"action_posteriors","class":"visual","model":"MCG-NJU/videomae-base-finetuned-kinetics","nVars":400,"pc1_pct":4.5},{"category":"dino_embedding","class":"visual","model":"facebook/dinov2-small","nVars":384,"pc1_pct":4.9},{"category":"siglip_embedding","class":"visual","model":"google/siglip2-base-patch16-224","nVars":768,"pc1_pct":5.4},{"category":"siglip_probe","class":"visual","model":"google/siglip2-base-patch16-224","nVars":16,"pc1_pct":9.6},{"category":"interpretable_scalars","class":"mixed","model":"(mixed)","nVars":65,"pc1_pct":9.7}]}

docs/CONTENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ Details: [`../analysis/web/README.md`](../analysis/web/README.md).
155155
| `plotFeatureMatrix(C)` | heatmap of the full feature time series, color-coded by category |
156156
| `factorAnalysisCorpus(C)` | exploratory factor analysis (EFA / `factoran`) across all features + color-coded loadings plot |
157157
| `extractCategoryFactors(C)` | factor analysis **within each model/category**`C.extracted_factors` (per-model factor time series), saveable to `.mat` |
158+
| `plotFactorScores()` | load the saved factors and visualize 6 ways (time series, mango heatmap, force-directed graph, correlation matrix, t-SNE, UMAP) with CANlab tools; saves svg+png to `matlab/figures/` |
158159

159160
---
160161

docs/feature_summary_table.docx

655 KB
Binary file not shown.

docs/summarize_project.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Reusable prompt — "summarize the project now"
2+
3+
When the user says **"summarize the project now"** (or asks for a project summary), produce a
4+
short narrative review of the whole project and its current annotation state, and write it —
5+
together with the feature table and 1–3 figures — into **`docs/feature_summary_table.docx`**
6+
(Word format, for import into Google Docs).
7+
8+
## Audience & tone
9+
Expert scientists who are **not** necessarily AI/ML experts. Convey **what** was done, the
10+
**current state** of the annotations, and — importantly — **why** it matters (the scientific
11+
motivation), clearly and fairly. Be accurate; do not overstate. Narrative ≤ **half a page**.
12+
13+
## What the summary must contain (in this order, in the .docx)
14+
1. **Narrative review** (two short paragraphs, ≤ ½ page):
15+
- *What & why:* naturalistic neuroimaging needs quantitative, second-by-second descriptions
16+
of dynamic movie/story stimuli; the scoping review of best-in-class visual/auditory/language
17+
models; the selection criteria (large training data, **open weights** for reproducibility,
18+
best-in-class validated performance); the selected extractors (interpretable classical-ML /
19+
signal-processing features **and** large foundation/transformer models, with concrete
20+
examples such as DINOv2, SigLIP2, CLAP, VideoMAE, EmoNet); the six broad domains (visual,
21+
auditory, language, social, emotional, situational) organized low-/mid-/high-level (Table 1).
22+
- *Application & structure:* how many feature-variable time series were extracted from how many
23+
clips/stories and how many seconds (1 Hz); the interactive viewer for QC; the within-domain
24+
exploratory factor analysis and what it showed — some model outputs are **irreducible**
25+
(first principal factor explains only a few %; e.g., action posteriors, DINOv2, SigLIP2,
26+
AudioSet, EmoNet), others **highly reducible** (first component > 50%; e.g., chroma, sentiment).
27+
2. **Table 1** — the class × level feature-summary table.
28+
3. **1–3 figures** from `plotFactorScores` (`matlab/figures/`), placed **after the table**, each
29+
with a brief caption. Pick the ones that show the annotations most clearly and beautifully
30+
(defaults: `01_factor_timeseries`, `04_correlation_matrix`, `05_tsne`).
31+
32+
## How to generate it (mechanics)
33+
All headline numbers are pulled live so the prose stays correct as the corpus grows:
34+
- `schema/channel_template.json` → channel and variable counts.
35+
- `analysis/corpus_stats.json` → stimulus counts, modality split, total minutes.
36+
- `analysis/factor_reducibility.json` → per-category first-PC % (written by the MATLAB factor
37+
analysis; regenerate if the corpus changed — see below).
38+
39+
Steps:
40+
1. If the corpus or factors changed, refresh inputs first:
41+
- In MATLAB: `F = readAnnotationCorpusFull("annotations/corpus"); F = extractCategoryFactors(F, "Save","analysis/extracted_factors.mat");`
42+
then `plotFactorScores();` (writes the figures and, via the helper, the reducibility numbers).
43+
If `analysis/factor_reducibility.json` is stale, recompute the per-category first-PC % and
44+
rewrite it.
45+
2. **Review and, if needed, rewrite the narrative** in `tools/build_project_summary.py` so it is
46+
accurate and fair for the current state (this is the judgment step — do not just run the script
47+
blindly; update the prose, examples, and figure choices as the project evolves).
48+
3. Run: `python3 tools/build_project_summary.py` → writes `docs/feature_summary_table.docx`.
49+
4. Verify (optional): convert to PDF and eyeball that the narrative is ≤ ½ page, the table fits
50+
one page, and the figures + captions render.
51+
52+
The table itself is built by `tools/build_feature_summary.py` (reused by the summary builder).

docs/walkthrough.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@
8080
% Rows for one clip: pick them with the stimulus id, e.g.
8181
% r = C.stim == "BigBuckBunny"; plot(C.time_sec(r), C.X(r, 1));
8282

83+
%% 5.2 Read the FULL corpus
84+
85+
C = readAnnotationCorpusFull("annotations/corpus");
86+
fprintf("corpus: %d clips, %d scalar channels, %d timepoints\n", ...
87+
numel(C.ids), size(C.X, 2), size(C.X, 1));
88+
8389
%% 6. Cross-feature STRUCTURE: correlation, PCA, network graphs
8490
% Saves figures to analysis/figures/ and returns a results struct.
8591
res = analyzeCorpus(C);

matlab/extractCategoryFactors.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,27 @@
5858
vecLeaves = unique(info.Leaf(info.Dtype == "vector"), "stable");
5959
cats = struct("name",{}, "model",{}, "class",{}, "kind",{}, "cols",{});
6060
for i = 1:numel(vecLeaves)
61+
6162
idx = find(info.Leaf == vecLeaves(i));
6263
kind = "interpretable-vector";
6364
if info.IsEmbedding(idx(1)), kind = "embedding";
6465
elseif ismember(vecLeaves(i), ["audioset_tags","action_posteriors"]), kind = "taxonomy"; end
66+
6567
cats(end+1) = struct("name", vecLeaves(i), "model", info.Model(idx(1)), ...
6668
"class", info.Class(idx(1)), "kind", kind, "cols", idx(:)'); %#ok<AGROW>
6769
end
70+
6871
sIdx = find(info.Dtype ~= "vector" & info.Numeric);
72+
6973
cats(end+1) = struct("name","interpretable_scalars", "model","(mixed)", ...
7074
"class","(mixed)", "kind","interpretable-scalar", "cols", sIdx(:)');
7175

7276
% ---- run FA/PCA per category
7377
byCat = struct([]); allScores = []; FN=strings(0,1); Cat=strings(0,1);
7478
Mdl=strings(0,1); Cls=strings(0,1); Meth=strings(0,1); VE=zeros(0,1);
79+
7580
for i = 1:numel(cats)
81+
7682
ca = cats(i);
7783
cols = ca.cols;
7884
sub = X(:, cols);
@@ -83,6 +89,7 @@
8389
keep = sd > 0 & isfinite(sd);
8490
sub = sub(:, keep); vinfo = vinfo(keep,:); mu = mu(keep); sd = sd(keep);
8591
Z = (sub - mu) ./ sd;
92+
8693
d = size(Z,2);
8794
if d == 0, continue; end
8895

400 KB
Loading
438 KB
Loading
268 KB
Loading

0 commit comments

Comments
 (0)