analysis(matlab): rate + count outputs, variation batch 5

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Experiments DB Dev
2026-07-24 02:32:53 -04:00
parent 1606eb698b
commit fd40b6fb54
48 changed files with 1247 additions and 595 deletions
@@ -1,45 +1,49 @@
% Variation power simulation -- Monte-Carlo power for the paper's stim x day
% interaction, using THIS folder's data as the ground truth.
% interaction, using THIS folder's data as the ground truth, for BOTH metrics:
% metric = count : behavior = # successes -> power_result.txt
% metric = rate : behavior = success / attempts -> power_result_rate.txt
%
% Ground truth: fitlme(behavior ~ stim + day + stim:day + (1|rat)) on data.csv
% (success COUNT; day within-window). Its fixed effects, per-rat intercept SD,
% and residual SD generate NREP synthetic datasets at each rats-per-group N and
% each true-effect multiplier (1 = observed slope, 0.5 = half). Each dataset is
% scored at alpha = 0.05 two ways:
% per-animal : Welch t on per-rat behavior~day slopes (cluster-honest -- the
% honest power, matching the random-slope / per-animal inference)
% (day within-window). Its fixed effects, per-rat intercept SD, and residual SD
% generate NREP synthetic datasets at each rats-per-group N and each true-effect
% multiplier (1 = observed, 0.5 = half). Each is scored at alpha=0.05 by:
% per-animal : Welch t on per-rat behavior~day slopes (cluster-honest power)
% LME : the fitlme stim:day p (observation-level DF -- optimistic)
% Writes power_result.txt beside this script. Run: matlab -batch "powersim"
% Writes power_result[_rate].txt. Run: matlab -batch "powersim"
% (Copy of analysis/matlab/variation_power.m; see make_variation_power.m.)
here = fileparts(mfilename('fullpath'));
if isempty(here); here = pwd; end
vname = regexprep(here, '.*[/\\]', '');
D = readtable(fullfile(here, 'data.csv'), 'TextType', 'string');
localPower(D, 'count', here, vname);
localPower(D, 'rate', here, vname);
% ---------------------------------------------------------------- per metric
function localPower(D, metric, here, vname)
NS = [3 5 8 12 16 24]; EFFMULS = [1 0.5]; NREP = 120;
FORMULA = 'behavior ~ stim + day + stim:day + (1|rat)';
NS = [3 5 8 12 16 24];
EFFMULS = [1 0.5];
NREP = 120;
warnState = warning('off', 'all');
rng(1);
if strcmp(metric, 'rate')
D = D(D.total > 0, :); beh = D.success ./ D.total; mlabel = 'success RATE'; suffix = '_rate';
else
beh = D.success; mlabel = '# successes (count)'; suffix = '';
end
warnState = warning('off', 'all'); rng(1);
day0 = min(D.day);
tbl0 = table(D.success, D.day - day0, double(D.stim), categorical(D.subject), ...
tbl0 = table(beh, D.day - day0, double(D.stim), categorical(D.subject), ...
'VariableNames', {'behavior', 'day', 'stim', 'rat'});
nStim = numel(unique(D.subject(D.stim == 1)));
nCtrl = numel(unique(D.subject(D.stim == 0)));
bar = repmat('=', 1, 78);
s = sprintf('%s\nPOWER SIMULATION -- %s\n%s\n', bar, vname, bar);
s = [s sprintf('model: %s (success COUNT; day within-window)\n', FORMULA)];
s = sprintf('%s\nPOWER SIMULATION -- %s [metric: %s]\n%s\n', bar, vname, mlabel, bar);
s = [s sprintf('model: %s (behavior = %s; day within-window)\n', FORMULA, mlabel)];
s = [s sprintf('observed groups: stim n=%d, control n=%d nrep=%d, alpha=0.05\n', nStim, nCtrl, NREP)];
if nStim < 2 || nCtrl < 2 || numel(unique(tbl0.day)) < 2
s = [s sprintf('\nInsufficient data for a power simulation (need >=2 animals/group and >=2 days).\n')];
localFinish(s, here); warning(warnState); return
s = [s sprintf('\nInsufficient data for a power simulation.\n')];
localFinish(s, here, suffix); warning(warnState); return
end
lme = fitlme(tbl0, FORMULA);
@@ -49,14 +53,13 @@ bDay = be(strcmp(cn, 'day')); bInt = be(strcmp(cn, 'day:stim'));
psi = covarianceParameters(lme); sRat = sqrt(psi{1}); sRes = sqrt(lme.MSE);
days = (0:max(tbl0.day))';
s = [s sprintf('ground truth: stim:day=%+.2f/day, rat SD=%.2f, residual SD=%.2f, days=%d\n', ...
s = [s sprintf('ground truth: stim:day=%+.4g/day, rat SD=%.4g, residual SD=%.4g, days=%d\n', ...
bInt, sRat, sRes, numel(days))];
for eMul = EFFMULS
bI = bInt * eMul;
s = [s sprintf('\n true stim:day interaction = %+.2f (%.0f%% of observed)\n', bI, eMul * 100)]; %#ok<AGROW>
s = [s sprintf(' %-8s | per-animal power | LME power\n', 'N/group')]; %#ok<AGROW>
s = [s sprintf(' %s\n', repmat('-', 1, 42))]; %#ok<AGROW>
s = [s sprintf('\n true stim:day interaction = %+.4g (%.0f%% of observed)\n', bI, eMul * 100)];
s = [s sprintf(' %-8s | per-animal power | LME power\n %s\n', 'N/group', repmat('-', 1, 42))];
for N = NS
sigPA = 0; sigL = 0;
for r = 1:NREP
@@ -70,20 +73,18 @@ for eMul = EFFMULS
end
star = '';
if N == nStim || N == nCtrl; star = ' <- observed'; end
s = [s sprintf(' %-8d | %5.2f | %5.2f%s\n', N, sigPA / NREP, sigL / NREP, star)]; %#ok<AGROW>
s = [s sprintf(' %-8d | %5.2f | %5.2f%s\n', N, sigPA / NREP, sigL / NREP, star)];
end
end
s = [s sprintf(['\nRead the per-animal column as the honest power. At the observed N this study\n' ...
'is typically underpowered; per-animal power reaches ~0.8 only at larger N.\n'])];
localFinish(s, here);
s = [s sprintf('\nRead the per-animal column as the honest power; LME is optimistic (obs-level DF).\n')];
localFinish(s, here, suffix);
warning(warnState);
end
% ---------------------------------------------------------------- helpers
function localFinish(s, here)
function localFinish(s, here, suffix)
fprintf('%s', s);
fid = fopen(fullfile(here, 'power_result.txt'), 'w');
fid = fopen(fullfile(here, ['power_result' suffix '.txt']), 'w');
fprintf(fid, '%s', s); fclose(fid);
end