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>
116 lines
4.3 KiB
Matlab
116 lines
4.3 KiB
Matlab
function make_variations()
|
|
%MAKE_VARIATIONS Generate one self-contained subfolder per grouping x window.
|
|
% MAKE_VARIATIONS() writes analysis/matlab/variations/<grouping>_<window>/
|
|
% for every combination below, each holding exactly three files:
|
|
% data.csv the curated data subset for that variation
|
|
% analyze.m a simple, standalone MATLAB script (copy of
|
|
% variation_analyze.m) that fits the paper's LME on data.csv
|
|
% result.txt the text result that analyze.m produces
|
|
% and a top-level variations/SUMMARY.csv indexing all variations.
|
|
%
|
|
% Groupings (stim = 1 treatment / stim = 0 control; all OTHER groups dropped):
|
|
% unmerge Box-B2 vs Box-A2
|
|
% right_only Box-B2 + Right-Elec. vs Box-A2 (Box-A dropped)
|
|
% naive_a2 Box-B2 vs Box-A2 + Naive (Right, Box-A dropped)
|
|
% naive_boxa Box-B2 vs Box-A2 + Naive + Box-A (Right dropped)
|
|
% Windows: 0-10, 0-13, 0-5, 6-10, 6-13.
|
|
%
|
|
% The model in each analyze.m is the paper's:
|
|
% behavior ~ stim + day + stim:day + (1|rat) (behavior = success COUNT).
|
|
|
|
thisDir = fileparts(mfilename('fullpath'));
|
|
templateScript = fullfile(thisDir, 'variation_analyze.m');
|
|
root = fullfile(thisDir, 'variations');
|
|
if ~exist(root, 'dir'); mkdir(root); end
|
|
|
|
B2 = 'Electrode-Box-B2'; A2 = 'Electrode-Box-A2'; NAIVE = 'Naive';
|
|
BOXA = 'Electrode-Box-A'; RIGHT = 'Right-Electrode';
|
|
|
|
% grouping name | treatment groups (stim=1) | control groups (stim=0)
|
|
groupings = {
|
|
'unmerge', {B2}, {A2}
|
|
'right_only', {B2, RIGHT}, {A2}
|
|
'naive_a2', {B2}, {A2, NAIVE}
|
|
'naive_boxa', {B2}, {A2, NAIVE, BOXA}
|
|
};
|
|
|
|
% window name | [loDay hiDay]
|
|
windows = {
|
|
'd0_10', [0 10]
|
|
'd0_13', [0 13]
|
|
'd0_5', [0 5]
|
|
'd6_10', [6 10]
|
|
'd6_13', [6 13]
|
|
};
|
|
|
|
T = tdcs_load_data();
|
|
allGroup = cellstr(T.group);
|
|
|
|
rows = {}; % accumulate SUMMARY rows
|
|
for gi = 1:size(groupings, 1)
|
|
gname = groupings{gi, 1};
|
|
treat = groupings{gi, 2};
|
|
ctrl = groupings{gi, 3};
|
|
inTreat = ismember(allGroup, treat);
|
|
inCtrl = ismember(allGroup, ctrl);
|
|
|
|
for wi = 1:size(windows, 1)
|
|
wname = windows{wi, 1};
|
|
w = windows{wi, 2};
|
|
sel = (inTreat | inCtrl) & T.day >= w(1) & T.day <= w(2);
|
|
|
|
D = T(sel, :);
|
|
stim = double(inTreat(sel));
|
|
Dout = table(string(D.subject), string(D.group), D.day, D.success, ...
|
|
D.total, stim, 'VariableNames', ...
|
|
{'subject', 'group', 'day', 'success', 'total', 'stim'});
|
|
|
|
vname = [gname '_' wname];
|
|
folder = fullfile(root, vname);
|
|
if ~exist(folder, 'dir'); mkdir(folder); end
|
|
writetable(Dout, fullfile(folder, 'data.csv'));
|
|
copyfile(templateScript, fullfile(folder, 'analyze.m'));
|
|
|
|
try
|
|
r = localRun(fullfile(folder, 'analyze.m'));
|
|
rows(end + 1, :) = {vname, gname, wname, r.nRats, r.nObs, ...
|
|
r.covEqual, r.interP, r.interPsatt, r.interPrs, r.interEst, ...
|
|
r.stimP, r.dayP}; %#ok<AGROW>
|
|
fprintf(' %-16s N=%2d obs=%4d interaction p=%.4g (%+.2f) %s\n', ...
|
|
vname, r.nRats, r.nObs, r.interP, r.interEst, ...
|
|
localTern(r.covEqual, 'equal-cov', 'UNEQUAL-cov'));
|
|
catch ME
|
|
localWriteError(folder, ME);
|
|
fprintf(2, ' %-16s ERROR: %s\n', vname, ME.message);
|
|
end
|
|
end
|
|
end
|
|
|
|
% Top-level index of all variations.
|
|
S = cell2table(rows, 'VariableNames', {'variation', 'grouping', 'window', ...
|
|
'nRats', 'nObs', 'covEqual', 'interaction_p', 'interaction_p_satt', ...
|
|
'interaction_p_rs', 'interaction_est', 'stim_p', 'day_p'});
|
|
writetable(S, fullfile(root, 'SUMMARY.csv'));
|
|
fprintf('\nWrote %d variations under %s\n(index: variations/SUMMARY.csv)\n', ...
|
|
size(rows, 1), root);
|
|
|
|
end
|
|
|
|
function r = localRun(scriptPath)
|
|
%LOCALRUN Execute one analyze.m in an isolated workspace and return its
|
|
% VARRESULT struct. Isolation prevents the script's variables (D, m, ...)
|
|
% from colliding with the generator's loop state.
|
|
run(scriptPath);
|
|
r = VARRESULT; %#ok<NODEF> (defined by the analyze.m script just run)
|
|
end
|
|
|
|
function localWriteError(folder, ME)
|
|
fid = fopen(fullfile(folder, 'result.txt'), 'w');
|
|
fprintf(fid, 'ERROR fitting this variation:\n%s\n', getReport(ME, 'basic'));
|
|
fclose(fid);
|
|
end
|
|
|
|
function out = localTern(cond, a, b)
|
|
if cond; out = a; else; out = b; end
|
|
end
|