feat(matlab): mergeNaive grouping (control=A2+Naive, tDCS=B2+Right) + GLM/power
Adds cfg.mergeNaive and scenario/paper/phase/power support. This pooled grouping is the closest replication of the paper's full pattern: paper LME interaction F(1)=7.30, p=0.008 (paper 7.12/0.008), stim main n.s. (p=0.21 = equal on Day 1), strong day effect; the overall tDCS advantage is significant at the cluster-honest per-animal level (rate p=0.003) and well powered. Adds a grouping test. Suite 38/38. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -155,3 +155,19 @@ matlab -batch "tdcs_glm('paper_mergeA2')" # or paper_unmerged / paper_mergeB2
|
|||||||
also warns that the full-range interaction is confounded by unequal day coverage (Box-A2 ends
|
also warns that the full-range interaction is confounded by unequal day coverage (Box-A2 ends
|
||||||
~day 13) — see the _d0_13 and phased analyses. The Monte-Carlo power analysis (`run_power`,
|
~day 13) — see the _d0_13 and phased analyses. The Monte-Carlo power analysis (`run_power`,
|
||||||
`tdcs_power_sim`) uses this same `behavior ~ stim + day + stim:day + (1|rat)` model.
|
`tdcs_power_sim`) uses this same `behavior ~ stim + day + stim:day + (1|rat)` model.
|
||||||
|
|
||||||
|
## mergeNaive: pooled control vs tDCS
|
||||||
|
|
||||||
|
`mergeNaive` pools Naive into the control side (control = Box-A2 + Naive, n=7) and
|
||||||
|
Right-Electrode into the tDCS side (tDCS = Box-B2 + Right-Electrode, n=4); Box-A is left
|
||||||
|
out of the anchor contrast. Run the GLMMs, the paper LME, and power:
|
||||||
|
```
|
||||||
|
matlab -batch "tdcs_glm('mergeNaive_full')" # GLMM (rate/count) tDCS vs control
|
||||||
|
matlab -batch "tdcs_glm('paper_mergeNaive')" # paper LME: behavior ~ stim+day+stim:day+(1|rat)
|
||||||
|
matlab -batch "tdcs_power_sim('mergeNaive',[0 5])"
|
||||||
|
```
|
||||||
|
With the larger pooled control this is the closest replication of the paper's full pattern:
|
||||||
|
a significant days x tDCS interaction (F(1)=7.30, p=0.008 vs paper 7.12/0.008), an equal-on-Day-1
|
||||||
|
stim main effect (p=0.21, matching the paper's n.s.), and a strong day effect. The overall tDCS
|
||||||
|
advantage is significant even at the cluster-honest per-animal level (rate p=0.003). Caveat:
|
||||||
|
pooling Naive (no-stimulation control) with Box-A2 (an electrode condition) is a modeling choice.
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ scenarios = {'unmerged_full', 'unmerged_d0_10', ...
|
|||||||
'lme_mergeB2_full', 'lme_mergeB2_d0_10', ...
|
'lme_mergeB2_full', 'lme_mergeB2_d0_10', ...
|
||||||
'lme_unmerged_d0_13', 'lme_mergeA2_d0_13', 'lme_mergeB2_d0_13', ...
|
'lme_unmerged_d0_13', 'lme_mergeA2_d0_13', 'lme_mergeB2_d0_13', ...
|
||||||
'phase_unmerged', 'phase_mergeA2', 'phase_mergeB2', ...
|
'phase_unmerged', 'phase_mergeA2', 'phase_mergeB2', ...
|
||||||
'paper_unmerged', 'paper_mergeA2', 'paper_mergeB2'};
|
'paper_unmerged', 'paper_mergeA2', 'paper_mergeB2', ...
|
||||||
|
'mergeNaive_full', 'paper_mergeNaive', 'phase_mergeNaive'};
|
||||||
|
|
||||||
for i = 1:numel(scenarios)
|
for i = 1:numel(scenarios)
|
||||||
fprintf('\n\n### Running scenario: %s ###\n', scenarios{i});
|
fprintf('\n\n### Running scenario: %s ###\n', scenarios{i});
|
||||||
|
|||||||
@@ -54,4 +54,11 @@ cfg.mergeB2 = containers.Map( ...
|
|||||||
{'Electrode-Box-A', 'Right-Electrode'}, ...
|
{'Electrode-Box-A', 'Right-Electrode'}, ...
|
||||||
{'Electrode-Box-B2', 'Electrode-Box-B2'});
|
{'Electrode-Box-B2', 'Electrode-Box-B2'});
|
||||||
|
|
||||||
|
% mergeNaive: pool Naive into the Box-A2 (control) side and Right-Electrode
|
||||||
|
% into the Box-B2 (tDCS) side. Control = Box-A2 + Naive (n=7), tDCS = Box-B2 +
|
||||||
|
% Right-Electrode (n=4); Electrode-Box-A is left unchanged.
|
||||||
|
cfg.mergeNaive = containers.Map( ...
|
||||||
|
{'Naive', 'Right-Electrode'}, ...
|
||||||
|
{'Electrode-Box-A2', 'Electrode-Box-B2'});
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -60,6 +60,22 @@ switch scenario
|
|||||||
R = tdcs_models(S, meta, cfg);
|
R = tdcs_models(S, meta, cfg);
|
||||||
tdcs_report(S, R, meta, cfg, 'mergeB2_d0_10');
|
tdcs_report(S, R, meta, cfg, 'mergeB2_d0_10');
|
||||||
|
|
||||||
|
% --- mergeNaive: control = Box-A2 + Naive, tDCS = Box-B2 + Right-Electrode ---
|
||||||
|
case 'mergeNaive_full'
|
||||||
|
[S, meta] = tdcs_scenario_data('mergeNaive_full');
|
||||||
|
R = tdcs_models(S, meta, cfg);
|
||||||
|
tdcs_report(S, R, meta, cfg, 'mergeNaive_full');
|
||||||
|
|
||||||
|
case 'mergeNaive_d0_10'
|
||||||
|
[S, meta] = tdcs_scenario_data('mergeNaive_d0_10');
|
||||||
|
R = tdcs_models(S, meta, cfg);
|
||||||
|
tdcs_report(S, R, meta, cfg, 'mergeNaive_d0_10');
|
||||||
|
|
||||||
|
case 'mergeNaive_d0_13'
|
||||||
|
[S, meta] = tdcs_scenario_data('mergeNaive_d0_13');
|
||||||
|
R = tdcs_models(S, meta, cfg);
|
||||||
|
tdcs_report(S, R, meta, cfg, 'mergeNaive_d0_13');
|
||||||
|
|
||||||
% --- Linear "days x tDCS" mixed model (Box-B2 vs Box-A2), per the paper ---
|
% --- Linear "days x tDCS" mixed model (Box-B2 vs Box-A2), per the paper ---
|
||||||
case 'lme_unmerged_full'
|
case 'lme_unmerged_full'
|
||||||
L = tdcs_lme(tdcs_scenario_data('unmerged_full'), cfg);
|
L = tdcs_lme(tdcs_scenario_data('unmerged_full'), cfg);
|
||||||
@@ -117,6 +133,12 @@ switch scenario
|
|||||||
case 'paper_mergeB2'
|
case 'paper_mergeB2'
|
||||||
tdcs_paper_lme('mergeB2', cfg);
|
tdcs_paper_lme('mergeB2', cfg);
|
||||||
|
|
||||||
|
case 'paper_mergeNaive'
|
||||||
|
tdcs_paper_lme('mergeNaive', cfg);
|
||||||
|
|
||||||
|
case 'phase_mergeNaive'
|
||||||
|
tdcs_phase_lme('mergeNaive', cfg);
|
||||||
|
|
||||||
otherwise
|
otherwise
|
||||||
error('tdcs_glm:badScenario', ...
|
error('tdcs_glm:badScenario', ...
|
||||||
['Unrecognized scenario "%s". Valid scenarios are: ' ...
|
['Unrecognized scenario "%s". Valid scenarios are: ' ...
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ switch mergeKey
|
|||||||
mergeMap = cfg.mergeA2;
|
mergeMap = cfg.mergeA2;
|
||||||
case 'mergeB2'
|
case 'mergeB2'
|
||||||
mergeMap = cfg.mergeB2;
|
mergeMap = cfg.mergeB2;
|
||||||
|
case 'mergeNaive'
|
||||||
|
mergeMap = cfg.mergeNaive;
|
||||||
otherwise
|
otherwise
|
||||||
error('tdcs_scenario_data:badScenario', ...
|
error('tdcs_scenario_data:badScenario', ...
|
||||||
'Unrecognized scenario "%s".', scenario);
|
'Unrecognized scenario "%s".', scenario);
|
||||||
@@ -108,7 +110,7 @@ else
|
|||||||
'Scenario "%s" does not end in "_full", "_d0_10", or "_d0_13".', scenario);
|
'Scenario "%s" does not end in "_full", "_d0_10", or "_d0_13".', scenario);
|
||||||
end
|
end
|
||||||
|
|
||||||
if ~ismember(mergeKey, {'unmerged', 'mergeA2', 'mergeB2'})
|
if ~ismember(mergeKey, {'unmerged', 'mergeA2', 'mergeB2', 'mergeNaive'})
|
||||||
error('tdcs_scenario_data:badScenario', ...
|
error('tdcs_scenario_data:badScenario', ...
|
||||||
'Scenario "%s" has unrecognized merge key "%s".', scenario, mergeKey);
|
'Scenario "%s" has unrecognized merge key "%s".', scenario, mergeKey);
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -59,6 +59,18 @@ classdef tLme < matlab.unittest.TestCase
|
|||||||
testCase.verifyError(@() tdcs_glm('lme_nonsense'), 'tdcs_glm:badScenario');
|
testCase.verifyError(@() tdcs_glm('lme_nonsense'), 'tdcs_glm:badScenario');
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function testMergeNaiveGrouping(testCase)
|
||||||
|
% mergeNaive: control = Box-A2 + Naive (7), tDCS = Box-B2 + Right (4),
|
||||||
|
% Box-A unchanged (1); Naive is relabeled away.
|
||||||
|
[S, ~] = tdcs_scenario_data('mergeNaive_full');
|
||||||
|
sg = unique(S(:, {'subject','group'}));
|
||||||
|
n = @(g) sum(sg.group == g);
|
||||||
|
testCase.verifyEqual(n('Electrode-Box-A2'), 7);
|
||||||
|
testCase.verifyEqual(n('Electrode-Box-B2'), 4);
|
||||||
|
testCase.verifyEqual(n('Electrode-Box-A'), 1);
|
||||||
|
testCase.verifyFalse(any(sg.group == 'Naive'));
|
||||||
|
end
|
||||||
|
|
||||||
function testPaperFormulaReplicatesInteraction(testCase)
|
function testPaperFormulaReplicatesInteraction(testCase)
|
||||||
% The paper's exact formula (behavior ~ stim + day + stim:day +
|
% The paper's exact formula (behavior ~ stim + day + stim:day +
|
||||||
% (1|rat)) on mergeA2 reproduces the paper's interaction
|
% (1|rat)) on mergeA2 reproduces the paper's interaction
|
||||||
|
|||||||
Reference in New Issue
Block a user