analysis(matlab): matched-effort comparison (prev _f full vs current 0-3)

Add make_matched_effort.m: pick the current-study day cutoff whose per-animal
cumulative attempts best match the previous (_f) study's full-span total
(~405 attempts/animal -> current days 0-3), then fit the paper LME on both.

Two variation folders (data.csv + analyze.m + result.txt):
  variations/prev_f_full/            b2_f vs a2_f, days 0-9  (24 rats)
  variations/matched_current_d0_3/   Box-B2 vs Box-A2, 0-3   (6 rats)

At matched cumulative effort both show a significant positive stim x day
interaction (prev p=0.008 +0.56/day; current-0-3 p=0.004 +10.2/day) -- the
tDCS acceleration replicates at equal practice, though the count slopes are
not directly comparable across datasets given differing attempts/session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Experiments DB Dev
2026-07-22 14:39:15 -04:00
parent d9b65a3279
commit 9af33e747e
7 changed files with 634 additions and 0 deletions
@@ -0,0 +1,74 @@
% Variation analysis -- the paper's linear mixed model on the successful-reach
% COUNT, fit on this folder's curated data subset.
%
% model: behavior ~ stim + day + stim:day + (1|rat)
% behavior = successful reaches (count per session)
% stim = 1 for the treatment group(s), 0 for the control group(s)
% day = training day within this window (0 = first analyzed day)
% rat = subject (random intercept)
%
% Self-contained: reads data.csv beside this script and writes result.txt.
% Run headless from this folder with: matlab -batch "analyze"
% (This is a copy of analysis/matlab/variation_analyze.m; see make_variations.m.)
here = fileparts(mfilename('fullpath'));
if isempty(here); here = pwd; end
vname = regexprep(here, '.*[/\\]', ''); % folder name = variation id
D = readtable(fullfile(here, 'data.csv'), 'TextType', 'string');
tbl = table(D.success, D.day - min(D.day), double(D.stim), categorical(D.subject), ...
'VariableNames', {'behavior', 'day', 'stim', 'rat'});
m = fitlme(tbl, 'behavior ~ stim + day + stim:day + (1|rat)');
C = m.Coefficients; A = anova(m); ci = coefCI(m);
gi = @(t) find(strcmp(C.Name, t), 1);
ga = @(t) find(strcmp(A.Term, t), 1);
row = @(nm, t) sprintf('%-26s t(%d)=%6.2f F(%d)=%8.3f p=%.4g\n', nm, ...
C.DF(gi(t)), C.tStat(gi(t)), A.DF1(ga(t)), A.FStat(ga(t)), C.pValue(gi(t)));
maxT = max(D.day(D.stim == 1)); minT = min(D.day(D.stim == 1));
maxC = max(D.day(D.stim == 0)); minC = min(D.day(D.stim == 0));
if abs(maxT - maxC) > 2
cov = '** WARNING: unequal day coverage -- interaction may be confounded. **';
else
cov = '(equal day coverage over this window)';
end
ii = gi('day:stim'); pI = C.pValue(ii); eI = C.Estimate(ii);
if pI >= 0.05
verdict = 'n.s. -- slopes parallel (no differential learning rate)';
elseif eI > 0
verdict = 'SIGNIFICANT positive -- treatment improves FASTER (benefit accumulates)';
else
verdict = 'SIGNIFICANT negative -- treatment improves SLOWER (groups converge)';
end
bar = repmat('=', 1, 78);
raw = regexprep(evalc('disp(m)'), '</?strong>', '');
s = sprintf('%s\nVARIATION: %s\n%s\n', bar, vname, bar);
s = [s sprintf('model: behavior ~ stim + day + stim:day + (1|rat) (behavior = success COUNT)\n')];
s = [s sprintf('day = training day within window (0 = first analyzed day)\n')];
s = [s sprintf('treatment (stim=1): %s\n', strjoin(cellstr(unique(D.group(D.stim == 1))), ', '))];
s = [s sprintf('control (stim=0): %s\n', strjoin(cellstr(unique(D.group(D.stim == 0))), ', '))];
s = [s sprintf('N = %d rats, %d sessions raw day coverage: treat %d..%d, control %d..%d\n', ...
numel(unique(D.subject)), height(D), minT, maxT, minC, maxC)];
s = [s sprintf('%s\n\n%s\nFULL MODEL SUMMARY -- fitlme\n%s\n%s\n', cov, bar, bar, raw)];
s = [s sprintf('%-26s %-13s %-13s %s\n%s\n', 'effect', 't (df)', 'F (df1)', 'p', repmat('-', 1, 66))];
s = [s row('stim x day (interaction)', 'day:stim')];
s = [s row('day (learning)', 'day')];
s = [s row('stim (main, window start)', 'stim')];
s = [s sprintf('interaction 95%% CI: [%+.2f, %+.2f]\n', ci(ii, 1), ci(ii, 2))];
s = [s sprintf('INTERPRETATION: stim x day interaction %s (p=%.4g, slope diff=%+.2f)\n', verdict, pI, eI)];
s = [s sprintf('Paper (N=24): interaction t(227)=2.68, F(1)=7.12, p=0.008.\n')];
fprintf('%s', s);
fid = fopen(fullfile(here, 'result.txt'), 'w');
fprintf(fid, '%s', s);
fclose(fid);
% Machine-readable handoff for the summary table (see make_variations.m).
VARRESULT = struct('name', vname, 'nRats', numel(unique(D.subject)), ...
'nObs', height(D), 'interP', pI, 'interEst', eI, ...
'stimP', C.pValue(gi('stim')), 'dayP', C.pValue(gi('day')), ...
'covEqual', abs(maxT - maxC) <= 2);
@@ -0,0 +1,25 @@
subject,group,day,success,total,stim
Banh-mi-1,Electrode-Box-B2,0,13,84,1
Banh-mi-1,Electrode-Box-B2,1,35,86,1
Banh-mi-1,Electrode-Box-B2,2,47,110,1
Banh-mi-1,Electrode-Box-B2,3,65,140,1
Banh-mi-2,Electrode-Box-A2,0,25,97,0
Banh-mi-2,Electrode-Box-A2,1,22,101,0
Banh-mi-2,Electrode-Box-A2,2,22,119,0
Banh-mi-2,Electrode-Box-A2,3,29,118,0
Egg-tart-1,Electrode-Box-B2,0,7,56,1
Egg-tart-1,Electrode-Box-B2,1,16,78,1
Egg-tart-1,Electrode-Box-B2,2,23,103,1
Egg-tart-1,Electrode-Box-B2,3,63,120,1
Egg-tart-2,Electrode-Box-A2,0,9,32,0
Egg-tart-2,Electrode-Box-A2,1,2,38,0
Egg-tart-2,Electrode-Box-A2,2,31,93,0
Egg-tart-2,Electrode-Box-A2,3,44,101,0
Root-beer-1,Electrode-Box-B2,0,11,85,1
Root-beer-1,Electrode-Box-B2,1,18,76,1
Root-beer-1,Electrode-Box-B2,2,40,105,1
Root-beer-1,Electrode-Box-B2,3,55,134,1
Root-beer-2,Electrode-Box-A2,0,22,74,0
Root-beer-2,Electrode-Box-A2,1,31,87,0
Root-beer-2,Electrode-Box-A2,2,49,134,0
Root-beer-2,Electrode-Box-A2,3,31,89,0
1 subject group day success total stim
2 Banh-mi-1 Electrode-Box-B2 0 13 84 1
3 Banh-mi-1 Electrode-Box-B2 1 35 86 1
4 Banh-mi-1 Electrode-Box-B2 2 47 110 1
5 Banh-mi-1 Electrode-Box-B2 3 65 140 1
6 Banh-mi-2 Electrode-Box-A2 0 25 97 0
7 Banh-mi-2 Electrode-Box-A2 1 22 101 0
8 Banh-mi-2 Electrode-Box-A2 2 22 119 0
9 Banh-mi-2 Electrode-Box-A2 3 29 118 0
10 Egg-tart-1 Electrode-Box-B2 0 7 56 1
11 Egg-tart-1 Electrode-Box-B2 1 16 78 1
12 Egg-tart-1 Electrode-Box-B2 2 23 103 1
13 Egg-tart-1 Electrode-Box-B2 3 63 120 1
14 Egg-tart-2 Electrode-Box-A2 0 9 32 0
15 Egg-tart-2 Electrode-Box-A2 1 2 38 0
16 Egg-tart-2 Electrode-Box-A2 2 31 93 0
17 Egg-tart-2 Electrode-Box-A2 3 44 101 0
18 Root-beer-1 Electrode-Box-B2 0 11 85 1
19 Root-beer-1 Electrode-Box-B2 1 18 76 1
20 Root-beer-1 Electrode-Box-B2 2 40 105 1
21 Root-beer-1 Electrode-Box-B2 3 55 134 1
22 Root-beer-2 Electrode-Box-A2 0 22 74 0
23 Root-beer-2 Electrode-Box-A2 1 31 87 0
24 Root-beer-2 Electrode-Box-A2 2 49 134 0
25 Root-beer-2 Electrode-Box-A2 3 31 89 0
@@ -0,0 +1,65 @@
==============================================================================
VARIATION: matched_current_d0_3
==============================================================================
model: behavior ~ stim + day + stim:day + (1|rat) (behavior = success COUNT)
day = training day within window (0 = first analyzed day)
treatment (stim=1): Electrode-Box-B2
control (stim=0): Electrode-Box-A2
N = 6 rats, 24 sessions raw day coverage: treat 0..3, control 0..3
(equal day coverage over this window)
==============================================================================
FULL MODEL SUMMARY -- fitlme
==============================================================================
Linear mixed-effects model fit by ML
Model information:
Number of observations 24
Fixed effects coefficients 4
Random effects coefficients 6
Covariance parameters 2
Formula:
behavior ~ 1 + day*stim + (1 | rat)
Model fit statistics:
AIC BIC LogLikelihood Deviance
185.35 192.42 -86.676 173.35
Fixed effects coefficients (95% CIs):
Name Estimate SE tStat DF pValue
{'(Intercept)'} 16.867 4.4554 3.7856 20 0.0011608
{'day' } 6.3667 2.2049 2.8875 20 0.0091051
{'stim' } -8.9667 6.3009 -1.4231 20 0.17013
{'day:stim' } 10.2 3.1182 3.2711 20 0.0038217
Lower Upper
7.5728 26.161
1.7673 10.966
-22.11 4.1769
3.6955 16.705
Random effects covariance parameters (95% CIs):
Group: rat (6 Levels)
Name1 Name2 Type Estimate
{'(Intercept)'} {'(Intercept)'} {'std'} 2.9163
Lower Upper
0.43116 19.725
Group: Error
Name Estimate Lower Upper
{'Res Std'} 8.5397 6.1599 11.839
effect t (df) F (df1) p
------------------------------------------------------------------
stim x day (interaction) t(20)= 3.27 F(1)= 10.700 p=0.003822
day (learning) t(20)= 2.89 F(1)= 8.337 p=0.009105
stim (main, window start) t(20)= -1.42 F(1)= 2.025 p=0.1701
interaction 95% CI: [+3.70, +16.70]
INTERPRETATION: stim x day interaction SIGNIFICANT positive -- treatment improves FASTER (benefit accumulates) (p=0.003822, slope diff=+10.20)
Paper (N=24): interaction t(227)=2.68, F(1)=7.12, p=0.008.