Celeus

Writing a results section: structure and wording

How to write the results section of a research paper paragraph by paragraph: sample, primary result, secondary and sensitivity analyses, tables versus text.

On this page

A results section reports what your planned analyses found, in the order the plan gave them, without explaining why. In a quantitative paper it usually runs in four steps: who or what was analysed, the primary result, the secondary results, and any sensitivity analyses or supplementary checks, with the numbers that are too many for a sentence moved into a table.

The sections below write each of those paragraphs from three engine runs in R on one public dataset (a Welch t-test, a two-way ANOVA and a Mann-Whitney test), so every statistic can be traced to engine output. The primary result in the example is not statistically significant while a secondary one is, the situation in which a results section is most tempted to change its emphasis. For the formatting of individual statistics, see the guide to reporting statistics in APA style.

What a results section does, and what it leaves out

The results section of a research paper is the record of what the analyses produced. APA’s reporting standards for quantitative research (JARS-Quant, Appelbaum and colleagues 2018) ask it to cover the flow and number of participants or units, descriptive statistics, every inferential analysis with its effect size and interval, and any analyses that were not planned, labelled as such. The CONSORT 2010 statement asks the same of a randomised trial, item by item, and adds that each primary and secondary outcome needs its estimated effect and its precision; CONSORT was updated in 2025, so check the current checklist for a trial.

Three things stay out:

  • Interpretation. “Orange juice may be absorbed more efficiently” is a discussion sentence. The results section says what was observed; the discussion says what it might mean.
  • Methods detail. The test and its rationale belong in the methods section. The results section may name the test, but does not justify it.
  • Literature. Comparisons with earlier studies belong in the discussion.

The order follows the analysis plan, not the size of the effects. Primary first, then secondary, then sensitivity analyses and supplementary checks, and an exploratory analysis last, marked as exploratory. Putting the significant findings first, whatever their status in the plan, is one of the quietest forms of spin.

The example and its analysis plan

The data are ToothGrowth, which ships with R. It comes from a 1947 experiment by Crampton in which 60 guinea pigs received vitamin C by one of two delivery methods, orange juice or ascorbic acid, at one of three doses (0.5, 1 or 2 mg a day). Each animal gives one measurement of odontoblast length, a measure of tooth growth, called “length” below; the R documentation gives no unit for it. The design is a 2 x 3 factorial with 10 animals per cell, and every animal sits in one cell only, so the groups are independent.

Suppose the analysis plan, written before the data were seen, said:

  1. Primary analysis. Mean length compared between delivery methods with Welch’s t-test, pooled over dose.
  2. Secondary analysis. A two-way ANOVA of length on delivery method, dose and their interaction.
  3. Sensitivity analysis. A Mann-Whitney test of the primary comparison, in case length is far from normally distributed. (Paragraph 4 explains why the results report it as a supplementary check.)

A t-test pooled over dose is a weaker primary choice than the two-way model, because it ignores a factor the experiment deliberately varied. Plans like that are common, and the point of the example is what to do once the plan is fixed: report it as planned, and put the critique in the discussion.

ToothGrowth records only one outcome, so the example has no secondary outcome; its secondary paragraph reports a secondary analysis of the same outcome. With true secondary outcomes, say a second measurement on each animal, the same rules apply, and the section below says how.

Paragraph 1: the sample

The first paragraph tells the reader what was analysed: how many units entered, how many were excluded and why, and how they were distributed across groups. Descriptive statistics follow, usually in a table.

All 60 guinea pigs were analysed, 30 per delivery method and 10 in each combination of delivery method and dose; no values were missing or excluded. Mean odontoblast length was 20.66 (SD = [SD]) in the orange juice group and 16.96 (SD = [SD]) in the ascorbic acid group. Table 1 gives the mean for each delivery method and dose.

The standard deviations are slots: the Welch run reports the group means but not the standard deviations, and every statistic on this page comes from one of the three engine runs. In your own paper they come from your descriptive table. The sentence “no values were missing or excluded” matters even when it is true; a reader cannot tell otherwise.

Paragraph 2: the primary result

The primary result gets the fullest report in the section: the estimate in the outcome’s own units, its confidence interval, the test statistic with its degrees of freedom, the exact p-value and an effect size with its interval. The engine’s Welch run, rounded to APA style (the t-test reporting guide walks through each number), gives:

In the primary analysis, mean length did not differ significantly between delivery methods, Welch’s t(55.31) = 1.92, p = .061: it was 3.70 higher with orange juice than with ascorbic acid, 95% CI [-0.17, 7.57], Cohen’s d = 0.49, 95% CI [-0.02, 1.01]. The interval includes zero and extends to a difference about twice the estimate.

The second sentence is reporting, not interpretation: it states what the interval contains. What the paragraph does not say is “there was no difference between delivery methods” or “orange juice tended to increase length”. The first claims more than a p-value can show: Greenland and colleagues list “a non-significant result shows no effect” among the common misreadings of p-values. The second highlights a benefit the test did not show, which fits the definition of spin Boutron and colleagues used (see below).

A design sensitivity figure (here, 80% power only for d = 0.74 or larger) is optional in the results; if you include it, label it as a property of the design, never as “observed power”.

Paragraph 3: secondary analyses and secondary outcomes

Secondary results are reported with the same statistics as the primary one and labelled secondary. A model with several terms is easier to read as a table, so the text gives the pattern and points to it. The engine’s two-way ANOVA, rounded to APA style:

Table 2. Secondary analysis: two-way ANOVA of odontoblast length (N = 60)

Term F df p Partial η², 95% CI
Delivery method 15.57 1, 54 < .001 .22 [.06, .40]
Dose 92.00 2, 54 < .001 .77 [.66, .84]
Delivery method x dose 4.11 2, 54 .022 .13 [.001, .29]

Note. The p-values are not adjusted for multiplicity.

In the secondary two-way analysis, length differed by dose and by delivery method, and the delivery-method difference depended on dose (Table 2). Mean length was higher with orange juice at 0.5 and 1 mg and similar at 2 mg (Table 1).

The delivery-method term is significant here and was not in the primary analysis, for a reason the reader can see: the two-way model removes the large dose effect from the error, so the same mean difference is estimated against less noise. That explanation belongs in the discussion. The results section reports both numbers, each under its own label, and does not let the secondary result replace the primary one.

Two more rules apply. With a significant interaction, the main effect of delivery method averages over doses where the difference is large and one where it is close to zero, so the paragraph describes the pattern by cell. And comparing the delivery methods at each dose separately would be three more tests; if you run them, adjust for multiplicity and say how. The two-way ANOVA guide covers both.

True secondary outcomes follow the same pattern. Name each one in the plan, report each in the planned order with its estimate and interval whether or not it is significant, and keep the label “secondary”. When the primary outcome is not significant and a secondary one is, the abstract and the first sentence of the discussion still lead with the primary result.

Paragraph 4: the supplementary check

A sensitivity analysis asks whether the primary conclusion survives a different but defensible assumption. Morris, Kahan and White set out the principle that a sensitivity analysis should answer the same question as the primary analysis, or a disagreement between them means nothing.

The plan’s Mann-Whitney test only partly meets that standard. It drops the normality assumption; the engine’s Shapiro-Wilk check found evidence of non-normality in the orange juice group (W = 0.9178, p = 0.0236) and suggested Mann-Whitney. But its estimate (Hodges-Lehmann, the median of all orange-juice-minus-ascorbic-acid differences) is not the difference in means, and it equals a shift between the groups only if the two distributions have the same shape. By Morris and colleagues’ principle it is therefore a supplementary check rather than a true sensitivity analysis, so the paragraph labels it that way and names what it estimates:

In a supplementary check, a Mann-Whitney test gave a similar result, U = 575.5, p = .064, with a Hodges-Lehmann estimate of the difference in location of 4.0 (95.09% CI [-0.1, 8.5]) and a rank-biserial correlation of r = .28, 95% CI [-.01, .52].

The interval level is unusual on purpose. R (version 4.6.0 and later) computes this interval from the exact distribution of the rank statistic, even with tied values, and that distribution moves in steps, so the interval cannot always hit 95% exactly. R reports the level it achieved, 0.9509 here (95.09%), and the engine passes that on rather than rounding it to 95%. Report the achieved level. R also labels the statistic W; for two groups it is the Mann-Whitney U of the first group. If a supplementary check or a sensitivity analysis disagrees with the primary result, report both and say so in plain words; never present the more favourable one as the main result.

Tables versus text

The rule of thumb: a few numbers go in a sentence, many go in a table, and no number appears in both. APA’s guidance is not to repeat the same statistics in the text and in a table: the text points to the table and comments on the key numbers, without retyping every cell. Keeping every number in one place also stops the two from drifting apart, and it explains the choices in the example.

  • In the text: the primary result in full, because it is the main finding and fits in one sentence; the supplementary check in one sentence; the secondary results as a pattern, pointing to their table.
  • In a table: anything a reader needs to scan or compare, here the three model terms (Table 2) and the six cell means (Table 1). A sentence listing six means and twelve interval limits is unreadable.
  • In neither: output lines no reader needs, such as every assumption statistic. Assumption checks go in the methods or a supplement, unless one is the reason for a planned analysis, as the normality flag is for the supplementary check here.

The table the paragraphs refer to, built from the two-way run:

Table 1. Mean odontoblast length by delivery method and dose, with 95% confidence intervals from the two-way model (n = 10 per cell; length in the dataset’s units, which the R documentation does not state)

Dose (mg/day) Orange juice Ascorbic acid
0.5 13.23 [10.93, 15.53] 7.98 [5.68, 10.28]
1 22.70 [20.40, 25.00] 16.77 [14.47, 19.07]
2 26.06 [23.76, 28.36] 26.14 [23.84, 28.44]

A table should stand alone: a title, units, the sample size, what the brackets mean, and notes for anything unusual, such as these intervals sharing one pooled standard error from the model. The text then points to the table instead of repeating it, as paragraph 3 does with “(Table 1)” and “(Table 2)”.

Wording that keeps the section honest

Boutron and colleagues reviewed reports of randomised trials whose primary outcome was not statistically significant, looking for spin: reporting strategies that highlight a benefit of the experimental treatment despite the non-significant primary result, or that distract the reader from it. They found it in the main-text results of some reports, more often in the conclusions, and often in more than one section of the same report. The table below pairs wording to avoid with what to write instead.

Avoid Write instead
“There was no difference between groups.” “The difference was not statistically significant”, with the interval.
“showed a trend toward significance (p = .061)” The exact p and the interval, with no adjective.
“As hypothesised, delivery method affected length.” Report the primary result first; label the secondary one as secondary.
“proves”, “confirms” “was associated with”, “differed by”, with the estimate.
“p = .000” “p < .001”.
“n.s.” The exact p-value, the estimate and its interval.

Write what was observed in the past tense (“mean length was 22.70”) and what a table shows in the present (“Table 1 gives the cell means”). Keep the units and the comparison direction in every sentence: “3.70 higher with orange juice” can be checked against the table; “a difference of 3.70” cannot.

A checklist before you submit

  • Every analysis in the plan is reported, in the plan’s order, with the primary result first.
  • Each result has an estimate in the outcome’s units, an interval and an effect size, not a p-value alone.
  • Secondary analyses, sensitivity analyses, supplementary checks and exploratory analyses are labelled as such, and multiplicity is stated.
  • No number appears in both a table and the text, and every table can be read without the text.
  • Missing data and exclusions are reported, even when there are none.

Rerunning the example

The worked example below shows the engine’s output for the primary Welch run, then its R script in a collapsed panel. The output includes Cohen’s d and the assumption checks; the script recomputes only the Welch test and the power figure. The other two runs are not shown. In R, aov(len ~ supp * factor(dose), data = ToothGrowth) gives the two-way model (its sequential sums of squares match because every cell has 10 animals) and, in R 4.6.0 or later, wilcox.test(len ~ supp, data = ToothGrowth, conf.int = TRUE) the supplementary check.

The methods are documented on the reference pages for Welch’s t-test, the two-way ANOVA and the Mann-Whitney test. The t-test or Mann-Whitney guide covers the choice between the primary test and the supplementary check, the multiple comparisons guide covers adjusting secondary tests, and the guide to reproducible statistical analysis explains what a reviewer needs to rerun a results section like this one.

Reproduce this

Worked example

Data: Crampton EW (1947). The growth of the odontoblasts of the incisor tooth as a criterion of the vitamin C intake of the guinea pig. Journal of Nutrition 33(5):491-504. doi:10.1093/jn/33.5.491. Seed: 20261101.

Run on Celeus engine 0.41.7, seed 20261101, public data.

Output

# Results

- Test: Welch's t-test
- n: 60
- statistic: 1.915
- p-value: 0.0606
- effect size: Cohen's d = 0.4945 (95% CI -0.02151 to 1.006)
- 95% CI (mean in group OJ minus mean in group VC): [-0.171, 7.571]
- seed: 20261101

## Assumption checks
- normality by group (Shapiro-Wilk): passed=FALSE
- variance homogeneity (Levene): passed=TRUE (no evidence of unequal variances)
- sample size: passed=TRUE (adequate n)

## Power (sensitivity, not observed power)
- minimum detectable effect at n=60: Cohen's d = 0.7356
- to detect a conventional medium effect: 64 per group to detect d=0.50
- alpha = 0.05, target power = 0.8
R script
# Celeus - reproduce.R  (run from this package folder; bundled data in data/)
# Test: Welch's t-test  |  engine 0.41.7  |  catalog c40-dc08
# Research context: not provided for this run.
# Reproduces the recorded result deterministically.

set.seed(20261101)
.data_path <- "data/results-section.csv"
if (!file.exists(.data_path)) stop("Run reproduce.R from the package folder (the bundled data is in data/).")
data <- readr::read_csv(.data_path, show_col_types = FALSE)  # needs the readr package
clean_cells <- function (df) 
{
    ctrl_collapse <- "[\r\n\t\v\f]+"
    zero_width_chars <- intToUtf8(c(8203L, 65279L), multiple = TRUE)
    uni_space_chars <- intToUtf8(c(160L, 8239L, 8199L), multiple = TRUE)
    clean_text <- function(x) {
        out <- x
        for (ch in zero_width_chars) out <- gsub(ch, "", out, 
            fixed = TRUE)
        out <- gsub(ctrl_collapse, " ", out)
        for (ch in uni_space_chars) out <- gsub(ch, " ", out, 
            fixed = TRUE)
        trimws(gsub(" +", " ", out))
    }
    changed_cols <- character(0)
    cells_cleaned <- 0L
    retyped <- character(0)
    for (j in seq_along(df)) {
        col <- df[[j]]
        if (!is.character(col)) 
            next
        cleaned <- clean_text(col)
        n_changed <- sum(!is.na(col) & !is.na(cleaned) & col != 
            cleaned, na.rm = TRUE)
        if (n_changed > 0L) {
            changed_cols <- c(changed_cols, names(df)[j])
            cells_cleaned <- cells_cleaned + n_changed
        }
        df[[j]] <- cleaned
        non_blank <- cleaned[!is.na(cleaned) & nzchar(cleaned)]
        if (length(non_blank) && all(grepl("^[+-]?(0|[1-9][0-9]*)([.][0-9]+)?$", 
            non_blank))) {
            parsed <- suppressWarnings(as.numeric(cleaned))
            was_blank <- is.na(cleaned) | !nzchar(cleaned)
            lost <- (is.na(parsed) | is.infinite(parsed)) & !was_blank
            if (!any(lost)) {
                finite <- parsed[!is.na(parsed)]
                all_whole <- all(finite == floor(finite))
                in_range <- all(abs(finite) <= .Machine$integer.max)
                df[[j]] <- if (all_whole && in_range) 
                  as.integer(parsed)
                else parsed
                retyped <- c(retyped, names(df)[j])
            }
        }
    }
    names(df) <- clean_text(names(df))
    if (length(changed_cols)) {
        attr(df, "ingest_clean_columns") <- changed_cols
        attr(df, "ingest_clean_cells_cleaned") <- cells_cleaned
    }
    if (length(retyped)) 
        attr(df, "ingest_clean_retyped") <- retyped
    df
}
data <- clean_cells(data)
# Complete-case analysis: drop rows missing any analysis value.
.acols <- c("len", "supp")
data <- data[stats::complete.cases(data[, .acols, drop = FALSE]), , drop = FALSE]
result <- t.test(`len` ~ `supp`, data = data, var.equal = FALSE)
print(result)

# Sensitivity: minimum detectable effect at n=60 (power=0.8, alpha=0.05). NOT observed power.
power <- pwr::pwr.t2n.test(n1 = 30, n2 = 30, sig.level = 0.05, power = 0.8)
print(power)

Download the files

Common questions

What is the difference between the results and the discussion section?

The results section reports what the analyses found: the numbers, their intervals and the direction of each effect, in the order of the analysis plan. The discussion says what those findings mean, compares them with earlier studies, weighs limitations and draws conclusions. A sentence that explains why something happened belongs in the discussion.

Should I report results that were not statistically significant?

Yes. Every analysis named in the plan is reported in the same format whatever its p-value, with the estimate, its confidence interval and an effect size. Leaving out non-significant results misleads readers and biases any later meta-analysis, and a non-significant primary result stays the primary result.

What tense should a results section be written in?

Use the past tense for what was observed in your study, such as 'mean length was 20.66 in the orange juice group'. Use the present tense for what a table or figure shows now, such as 'Table 1 gives the cell means', and for statements that remain true beyond the study.

How long should the results section be?

As long as the analyses in the plan require and no longer. A short paper with one primary analysis may need four paragraphs and one table. Length should come from the number of planned analyses, not from repeating table values in the text or narrating every output line.

Should results go in a table or in the text?

Put a few numbers in a sentence and many numbers in a table. A single comparison reads best in the text; cell means, several outcomes or many model terms belong in a table, with the text giving only the main finding and pointing to the table. Do not give the same numbers in both places.

Sources

  1. Appelbaum M, Cooper H, Kline RB, Mayo-Wilson E, Nezu AM, Rao SM (2018). Journal article reporting standards for quantitative research in psychology: the APA Publications and Communications Board task force report. American Psychologist 73(1):3-25. doi:10.1037/amp0000191
  2. American Psychological Association (2024). APA Style numbers and statistics guide (7th ed. handout, last updated September 11, 2024). apastyle.apa.org
  3. Schulz KF, Altman DG, Moher D, for the CONSORT Group (2010). CONSORT 2010 Statement: updated guidelines for reporting parallel group randomised trials. BMJ 340:c332. doi:10.1136/bmj.c332
  4. Hopewell S, Chan AW, Collins GS, et al. (2025). CONSORT 2025 statement: updated guideline for reporting randomised trials. BMJ 389:e081123. doi:10.1136/bmj-2024-081123
  5. Boutron I, Dutton S, Ravaud P, Altman DG (2010). Reporting and interpretation of randomized controlled trials with statistically nonsignificant results for primary outcomes. JAMA 303(20):2058-2064. doi:10.1001/jama.2010.651
  6. Morris TP, Kahan BC, White IR (2014). Choosing sensitivity analyses for randomised trials: principles. BMC Medical Research Methodology 14:11. doi:10.1186/1471-2288-14-11
  7. Greenland S, Senn SJ, Rothman KJ, Carlin JB, Poole C, Goodman SN, Altman DG (2016). Statistical tests, P values, confidence intervals, and power: a guide to misinterpretations. European Journal of Epidemiology 31(4):337-350. doi:10.1007/s10654-016-0149-3

Methods discussed

Try it on your own data

Upload a dataset and Celeus suggests a method, checks its assumptions, runs it in R, and gives you the script to rerun it.

Try it in Celeus