e730aa0020
Adds tdcs_phase_lme (same LME refit within learning phases 0-5/6-10/6-13, with per-phase slopes + interaction 95% CI) as phase_* switch cases, and tdcs_power_sim (Monte-Carlo power for the early-phase interaction, scored by the cluster-honest per-animal test and the LME test) + run_power. Findings: the Box-B2 faster-acquisition signal is in the early phase; at n=3-5/group honest power is 0.3-0.7 even at the observed effect (need ~8/group if effect is as observed, ~20/group if half). Adds tPhasePower tests. Suite 35/35. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
82 lines
3.4 KiB
Matlab
82 lines
3.4 KiB
Matlab
function P = tdcs_phase_lme(mergeKey, cfg, phases)
|
|
%TDCS_PHASE_LME Refit the days x tDCS LME within learning phases (Box-B2 vs A2).
|
|
% P = TDCS_PHASE_LME(MERGEKEY, CFG) fits the same linear mixed model as the
|
|
% lme_* scenarios -- success ~ dayp*tDCS + (1|subject) -- SEPARATELY within
|
|
% each learning phase, for the Box-B2 (tDCS=1) vs Box-A2 (tDCS=0) subset of
|
|
% the MERGEKEY grouping ('unmerged' | 'mergeA2' | 'mergeB2'). `dayp` is `day`
|
|
% centered at each phase's start, so the tDCS main effect reads as the group
|
|
% difference on the phase's first day (the interaction and slopes are
|
|
% invariant to this centering). Prints a per-phase table and writes
|
|
% results/phase_<MERGEKEY>.txt.
|
|
%
|
|
% P = TDCS_PHASE_LME(MERGEKEY, CFG, PHASES) uses custom phase windows
|
|
% (default {[0 5],[6 10],[6 13]}), each a [loDay hiDay] pair.
|
|
%
|
|
% P.phases{k} has: .phase .nSub .a2Slope .b2Slope .dayP (learning) .tDCSlevelP
|
|
% (between-group level at phase start) .interP .interEst .interCI (the
|
|
% between-group learning-rate difference and its 95% CI).
|
|
|
|
if nargin < 3 || isempty(phases)
|
|
phases = {[0 5], [6 10], [6 13]};
|
|
end
|
|
|
|
Sfull = tdcs_scenario_data([mergeKey '_full']);
|
|
T = Sfull(ismember(Sfull.group, {cfg.anchorLow, cfg.anchorHigh}), :);
|
|
|
|
P = struct('mergeKey', mergeKey, 'phases', {cell(1, numel(phases))});
|
|
|
|
bar = repmat('=', 1, 78);
|
|
s = sprintf('%s\n', bar);
|
|
s = [s sprintf('PHASED days x tDCS LME -- %s (Box-B2 vs Box-A2)\n', mergeKey)];
|
|
s = [s sprintf('%s\n', bar)];
|
|
s = [s sprintf('model per phase: success ~ dayp*tDCS + (1|subject) [dayp = day - phaseStart]\n\n')];
|
|
s = [s sprintf('%-9s N A2slope B2slope day p tDCS(lvl) p interaction p slopeDiff [95%% CI]\n', 'phase')];
|
|
s = [s sprintf('%s\n', repmat('-', 1, 92))];
|
|
|
|
for k = 1:numel(phases)
|
|
ph = phases{k};
|
|
Tp = T(T.day >= ph(1) & T.day <= ph(2), :);
|
|
Tp.dayp = Tp.day - ph(1);
|
|
Tp.tDCS = double(Tp.group == cfg.anchorHigh);
|
|
|
|
lme = fitlme(Tp, 'success ~ dayp*tDCS + (1|subject)');
|
|
C = lme.Coefficients; A = anova(lme); ci = coefCI(lme);
|
|
ii = strcmp(C.Name, 'dayp:tDCS');
|
|
di = strcmp(C.Name, 'dayp');
|
|
|
|
e = struct();
|
|
e.phase = ph;
|
|
e.nSub = numel(unique(Tp.subject));
|
|
e.a2Slope = C.Estimate(di);
|
|
e.b2Slope = C.Estimate(di) + C.Estimate(ii);
|
|
e.dayP = A.pValue(strcmp(A.Term, 'dayp'));
|
|
e.tDCSlevelP = A.pValue(strcmp(A.Term, 'tDCS'));
|
|
e.interP = A.pValue(strcmp(A.Term, 'dayp:tDCS'));
|
|
e.interEst = C.Estimate(ii);
|
|
e.interCI = ci(ii, :);
|
|
P.phases{k} = e;
|
|
|
|
s = [s sprintf('%d-%-6d %d %6.2f %6.2f %-9.2g %-11.3f %-13.3f %+.2f [%+.2f, %+.2f]\n', ...
|
|
ph(1), ph(2), e.nSub, e.a2Slope, e.b2Slope, e.dayP, e.tDCSlevelP, ...
|
|
e.interP, e.interEst, e.interCI(1), e.interCI(2))]; %#ok<AGROW>
|
|
end
|
|
|
|
s = [s sprintf(['\nNote: the interaction p (and CI) use fitlme observation-level DF and are\n' ...
|
|
'ANTICONSERVATIVE at these small subject counts (see tdcs_power_sim). The early\n' ...
|
|
'phase carries the Box-B2 faster-acquisition signal; late phases converge.\n'])];
|
|
|
|
fprintf('%s', s);
|
|
localWrite(['phase_' mergeKey], s);
|
|
|
|
end
|
|
|
|
function localWrite(name, s)
|
|
thisDir = fileparts(mfilename('fullpath'));
|
|
resDir = fullfile(thisDir, 'results');
|
|
if ~exist(resDir, 'dir'); mkdir(resDir); end
|
|
fid = fopen(fullfile(resDir, [name '.txt']), 'w');
|
|
if fid < 0; error('tdcs_phase_lme:fopen', 'Cannot open results file for "%s".', name); end
|
|
cleanup = onCleanup(@() fclose(fid)); %#ok<NASGU>
|
|
fprintf(fid, '%s', s);
|
|
end
|