Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/pages/SampleSize.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ describe('sample size page', () => {
test('walks a first-timer from the design to a plan: d = 0.30 from a raw difference', async () => {
const { pick, next, fill } = setup();
expect(question()).toBe('What will you test?');
// Each design names the model the course fits, not only the classic test.
expect(screen.getByRole('button', { name: /^Compare two separate groups/ }).textContent).toContain('In R: lm(outcome ~ group)');
await pick(/^Compare two separate groups/);
await pick(/How many people do I need/);
await pick(/I can guess the scores/);
Expand All @@ -56,6 +58,7 @@ describe('sample size page', () => {
// 176 / 0.9 rounds up to 196 per group.
expect(report()).toContain('we will recruit 392 participants');
expect(screen.getByText(/pwr.t.test\(d = 0.3, sig.level = 0.05, power = 0.8/)).toBeTruthy();
expect(screen.getByText(/is the independent-samples t-test \(Lesson 12-1\)/)).toBeTruthy();
expect(trail()).toEqual(['Compare two separate groups', 'Plan a new study', 'My own guess of the scores', 'd = 0.30', '80% power', 'α = .05', 'Two-sided', '10% dropout']);
});

Expand Down
8 changes: 6 additions & 2 deletions src/pages/SampleSize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,10 @@ export default function SampleSize() {
);
} else if (step === 'design') {
question = 'What will you test?';
help = <>Pick the comparison your research question makes. Not sure? <Link to="/which-model">Which model should I use?</Link> helps you choose. An A/B test is two separate groups: choose the yes/no comparison for clicks or sign-ups, and the first option for a score. Repeated measures, with or without several stimuli per condition, are under "Measure the same people in several conditions"; a before-and-after study with a control group is a 2 × 2 design. Logistic regression, mediation and models with more random factors are not here: plan those by simulation, as <Link to="/lesson/08-3">Lesson 8-3</Link> does.</>;
help = <>Pick the comparison your research question makes; each option shows the model from the course. Not sure? <Link to="/which-model">Which model should I use?</Link> helps you choose. An A/B test is two separate groups: a score, or a yes/no outcome such as clicks. A before-and-after study with a control group is a 2 × 2 design. Logistic regression with several predictors, mediation and models with more random factors are not here: plan those by simulation, as <Link to="/lesson/08-3">Lesson 8-3</Link> does.</>;
body = (
<Choice
options={DESIGNS.map((d) => ({ value: d.id, label: d.title, note: `e.g. ${d.example} Analysis: ${d.test}.` }))}
options={DESIGNS.map((d) => ({ value: d.id, label: d.title, note: <>e.g. {d.example}<br />In R: <code>{d.model}</code></> }))}
chosen={answers.design}
onPick={(value) => answer(value === answers.design ? {} : { design: value, route: undefined, effect: undefined, text: { ...answers.text, ...Object.fromEntries(['diff', 'sd', 'r', 'es', 'spread', 'eta2', 'r2', 'dr2', 'v', 'p2', 'eff1', 'eff2'].map((k) => [k, ''])), rpre: '.5' } })}
/>
Expand Down Expand Up @@ -807,6 +807,10 @@ export default function SampleSize() {
</>
)}

<h4>The model you will fit</h4>
<p><code>{design.model}</code></p>
<p>{design.modelNote}</p>

<h4>What this assumes</h4>
<p>{design.assumes}</p>
</div>
Expand Down
25 changes: 25 additions & 0 deletions src/pages/sampleSizeDesigns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export type Design = {
example: string;
/** The test the plan is for, as a student would name it. */
test: string;
/** The model the course fits for this question, and how its test relates to the planned one. */
model: string;
modelNote: string;
lessonId: string;
/** The effect size's symbol and name, and what it measures. */
symbol: string;
Expand All @@ -25,6 +28,8 @@ export const DESIGNS: Design[] = [
title: 'Compare two separate groups',
example: 'Do remote and office workers differ in wellbeing? Or an A/B test on a score, such as time on a page.',
test: 'independent-samples t-test',
model: 'lm(outcome ~ group)',
modelNote: 'The test of the group coefficient is the independent-samples t-test (Lesson 12-1).',
lessonId: '12-1',
symbol: 'd',
effectName: "Cohen's d",
Expand All @@ -39,6 +44,8 @@ export const DESIGNS: Design[] = [
title: 'Measure the same people twice',
example: 'Does engagement change between the first and second measurement?',
test: 'paired t-test',
model: 'lmer(outcome ~ time + (1 | person))',
modelNote: 'With two time points and nobody missing, the test of time is the paired t-test (Lesson 14-3).',
lessonId: '14-3',
symbol: 'dz',
effectName: 'Cohen\'s dz',
Expand All @@ -53,6 +60,8 @@ export const DESIGNS: Design[] = [
title: 'Measure the same people in several conditions',
example: 'Does reaction time differ between four conditions that every participant does?',
test: 'repeated-measures ANOVA (the overall F test)',
model: 'lmer(outcome ~ condition + (1 | person))',
modelNote: 'The F test for condition is the repeated-measures ANOVA (Lesson 14-4).',
lessonId: '14-4',
symbol: 'f',
effectName: "Cohen's f",
Expand All @@ -67,6 +76,8 @@ export const DESIGNS: Design[] = [
title: 'Two factors at once, like a 2 × 2 design',
example: 'Does a training raise wellbeing more than a waiting list, measured before and after? Or: do photo and text ads work differently for young and old viewers?',
test: '2 × 2 ANOVA',
model: 'lm(outcome ~ a * b)',
modelNote: 'Each effect is one term of this model: a * b gives both main effects and their interaction (Lesson 13-2). When a factor is within people, add a random intercept per person: lmer(outcome ~ a * b + (1 | person)) (Lesson 14-4).',
lessonId: '13-2',
symbol: 'f',
effectName: "Cohen's f",
Expand All @@ -81,6 +92,8 @@ export const DESIGNS: Design[] = [
title: 'Compare one group with a fixed value',
example: 'Is average wellbeing different from a benchmark score of 50?',
test: 'one-sample t-test',
model: 'lm(outcome - value ~ 1)',
modelNote: 'The test of the intercept is the one-sample t-test against the fixed value (Lesson 8-2 runs it as t.test()).',
lessonId: '08-2',
symbol: 'd',
effectName: "Cohen's d",
Expand All @@ -94,6 +107,8 @@ export const DESIGNS: Design[] = [
title: 'Compare three or more groups',
example: 'Does wellbeing differ between the four departments?',
test: 'one-way ANOVA (the overall F test)',
model: 'lm(outcome ~ group)',
modelNote: 'With group a factor of three or more categories, the overall F test of the model is the one-way ANOVA (Lesson 12-2).',
lessonId: '12-2',
symbol: 'f',
effectName: "Cohen's f",
Expand All @@ -108,6 +123,8 @@ export const DESIGNS: Design[] = [
title: 'Relate two numeric variables',
example: 'Is workload related to wellbeing?',
test: 'Pearson correlation test',
model: 'lm(outcome ~ predictor)',
modelNote: 'The test of the slope gives the same p-value as the test of the correlation (Lesson 10-2).',
lessonId: '10-1',
symbol: 'r',
effectName: 'the correlation r',
Expand All @@ -122,6 +139,8 @@ export const DESIGNS: Design[] = [
title: 'Predict an outcome from several predictors',
example: 'Do workload, autonomy and tenure together predict wellbeing?',
test: 'F test of R² in a multiple regression',
model: 'lm(outcome ~ x1 + x2 + x3)',
modelNote: 'The F test at the bottom of summary() tests the whole model (Lesson 11-3).',
lessonId: '11-3',
symbol: 'f²',
effectName: "Cohen's f²",
Expand All @@ -135,6 +154,8 @@ export const DESIGNS: Design[] = [
title: 'Test predictors on top of others',
example: 'Does autonomy predict wellbeing over and above workload and tenure?',
test: 'F test of the R² change (with one added predictor, the same as its t-test)',
model: 'anova(small_model, big_model)',
modelNote: 'The F test comparing lm(outcome ~ controls) with lm(outcome ~ controls + x) is the test of the R² change (Lesson 11-3).',
lessonId: '11-3',
symbol: 'f²',
effectName: "Cohen's f²",
Expand All @@ -149,6 +170,8 @@ export const DESIGNS: Design[] = [
title: 'Compare a yes/no outcome between two groups',
example: 'Do fewer mentored employees leave the company? Or an A/B test: do more visitors click with version B than with version A?',
test: 'test of two proportions (a chi-square test on a 2 × 2 table)',
model: 'glm(yes_no ~ group, family = binomial)',
modelNote: 'The page plans with the test of two proportions. The group coefficient in this logistic regression has almost the same power, at most a few percentage points less, so a few extra people cover it (Module 15).',
lessonId: '09-3',
symbol: 'p₂',
effectName: 'the two proportions',
Expand All @@ -162,6 +185,8 @@ export const DESIGNS: Design[] = [
title: 'Relate two categorical variables',
example: 'Is department related to working remotely?',
test: 'chi-square test of independence (or goodness of fit)',
model: 'chisq.test(table(a, b))',
modelNote: 'As a model: glm(count ~ a + b, family = poisson) on the table of counts gives the same chi-square test (Lesson 15-4).',
lessonId: '09-3',
symbol: 'w',
effectName: "Cohen's w",
Expand Down