8a18c894dd
Every fitlme-based report (lme_*, paper_*, phase_*, and the variations' analyze.m) now shows, per effect: residual-DF p, Satterthwaite-DF p, and -- for the interaction -- an HONEST test from a per-animal random-SLOPE model (day|rat), whose Satterthwaite DF collapses toward the animal count. New: tdcs_random_slope_interaction.m (shared helper). Wired into tdcs_lme, tdcs_paper_lme, tdcs_phase_lme, variation_analyze; SUMMARY.csv gains interaction_p_satt / interaction_p_rs. Regenerated all results/, variations/, matched-effort outputs. Key point this surfaces: Satterthwaite ~= residual on the random-INTERCEPT model (the slope's error is at session level), so it does NOT fix pseudoreplication; the random-slope model does. Effect: full-range mergeA2 interaction 0.009 -> 0.75 (collapses); unmerge_d0_5 0.015 -> 0.13 (n.s.); the pooled-control early windows survive honestly (naive_a2_d0_5 0.001 -> 0.028; naive_boxa_d0_5 0.003 -> 0.036). Suite 42/42. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
34 lines
1.6 KiB
Matlab
34 lines
1.6 KiB
Matlab
function rs = tdcs_random_slope_interaction(T, formula, term)
|
|
%TDCS_RANDOM_SLOPE_INTERACTION Honest LME test of a slope-interaction TERM.
|
|
% RS = TDCS_RANDOM_SLOPE_INTERACTION(T, FORMULA, TERM) refits the linear mixed
|
|
% model FORMULA (which must include a per-subject random SLOPE, e.g.
|
|
% 'success ~ day*tDCS + (day|subject)') on table T and returns the
|
|
% Satterthwaite-DF marginal F-test of TERM (e.g. 'day:tDCS').
|
|
%
|
|
% Why this is the honest test. The paper's model has only a random INTERCEPT,
|
|
% so it assumes every animal shares one true slope and estimates the slope /
|
|
% interaction with session-level precision -- Satterthwaite DF on that model
|
|
% stays ~= residual DF and does NOT fix the pseudoreplication. Giving each
|
|
% animal its OWN slope (a random slope) lets the between-animal slope variance
|
|
% enter the standard error, and the Satterthwaite denominator DF then collapses
|
|
% toward the number of animals -- matching the per-animal (cluster-honest)
|
|
% test. RS fields: .ok (false if the richer model failed to fit), .F, .df1,
|
|
% .df2 (Satterthwaite), .p.
|
|
|
|
rs = struct('ok', false, 'F', NaN, 'df1', NaN, 'df2', NaN, 'p', NaN);
|
|
w = warning('off', 'all'); cleanup = onCleanup(@() warning(w)); %#ok<NASGU>
|
|
try
|
|
lme = fitlme(T, formula);
|
|
A = anova(lme, 'DFMethod', 'satterthwaite');
|
|
i = strcmp(A.Term, term);
|
|
if any(i)
|
|
rs = struct('ok', true, 'F', A.FStat(i), 'df1', A.DF1(i), ...
|
|
'df2', A.DF2(i), 'p', A.pValue(i));
|
|
end
|
|
catch
|
|
% Random-slope model unidentifiable / non-convergent (common in short
|
|
% windows or with very few animals): leave rs.ok = false.
|
|
end
|
|
|
|
end
|