← The Analysis Clinic
Analysis Clinic · Case 009 · Diagnosed
We can't reproduce our own results

My Coauthor Reran the Code and Got Different Results

Trace the first point of divergence in inputs, analysis rows, coding, and software before comparing final p-values.

ReproducibilityResearch workflowR

Symptoms

What this usually means

“The same analysis” includes the input data, selected rows, formula, coding, defaults, random-number configuration, and environment. Compare these intermediate objects before comparing the final table. An opposite sign can represent an opposite reference contrast rather than different predictions.

Common causes

Run these checks

  1. Match input checksums and record the data version without sharing private records unnecessarily.
  2. Compare row counts, stable identifiers, filters, joins, and model-frame exclusions.
  3. Compare formulas, model matrices, contrasts, reference levels, weights, and transformations.
  4. Record sessionInfo(), package versions, and random-number settings. Start from a clean session.
  5. Locate the earliest differing object, then rerun the agreed pipeline from the raw input.

What not to do

Do not average conflicting results or copy the preferred output into the manuscript. A seed alone does not freeze data or software, and identical displayed p-values are weak evidence that the workflows agree.

Treatment options

Create an explicit input-to-report script with saved provenance. Set reference levels and exclusions in code, make data validation failures visible, and lock relevant dependencies. Compare numerical outputs using justified tolerances rather than expecting every platform to produce byte-identical floating-point results.

Worked example

Eight synthetic rows contain one missing covariate. The script separates the sample effect from adjustment and reference coding:

PipelineRowsReported contrast
y ~ arm on all rows8Treatment − control = 3.000
y ~ arm on complete covariate rows7Treatment − control = 1.500
y ~ arm + z on those same rows7Conditional treatment − control = 2.000
Original model with treatment as reference8Control − treatment = −3.000

Changing the reference gives identical fitted values: their maximum absolute difference is 0.00000000 at the displayed precision. The example also prints an input checksum, included IDs, the formula, contrast defaults, RNG settings, and session information. The checksum is for this generated CSV; do not treat it as a platform-independent data identity standard.

What to tell the reviewers

We reconciled input versions, included observations, model formulas, and reference coding. The discrepancy arose from a changed analysis sample and contrast direction. We regenerated the report from the agreed pipeline and recorded the data and software versions used.

See it in R and Python

Both languages construct the same deterministic observations and reproduce changes in sample size, adjustment, and contrast direction. Each prints language-appropriate provenance information.

Python dependencies: NumPy and SciPy. Install with python -m pip install numpy scipy.

# Synthetic workflow divergence; base R only. No external data.
d <- data.frame(id=1:8,arm=factor(rep(c('control','treatment'),each=4)),y=c(1,2,3,4,3,4,5,10),z=c(1,2,3,4,1,2,3,NA))
a <- lm(y~arm,d)
b <- lm(y~arm+z,d)
same <- d[complete.cases(d[c('y','arm','z')]),]
c <- lm(y~arm,same)
reverse <- transform(d,arm=relevel(arm,ref='treatment'))
r <- lm(y~arm,reverse)
cat(sprintf('Original model: n=%d, treatment-control=%.3f\n',nobs(a),coef(a)[2]))
cat(sprintf('Same formula on complete rows: n=%d, treatment-control=%.3f\n',nobs(c),coef(c)[2]))
cat(sprintf('Added z: n=%d, conditional treatment-control=%.3f\n',nobs(b),coef(b)[2]))
cat(sprintf('Reversed reference: control-treatment=%.3f; max fitted-value difference=%.8f\n',coef(r)[2],max(abs(fitted(a)-fitted(r)))))
# Stable audit manifest: record input checksum, identifiers, formula and session.
f <- tempfile(fileext='.csv'); write.csv(d,f,row.names=FALSE,quote=FALSE)
cat('Input MD5:',unname(tools::md5sum(f)),'\n'); unlink(f)
cat('Included IDs:',paste(d$id[as.integer(rownames(model.frame(a)))],collapse=','),'\n')
print(formula(a)); print(options('contrasts','na.action')); print(RNGkind()); sessionInfo()
stopifnot(nobs(a)==8,nobs(b)==7,max(abs(fitted(a)-fitted(r)))<1e-8)
# DASS Analysis Clinic Case 009; Python dependencies: numpy, scipy.
from pathlib import Path
import numpy as np
from scipy import stats, optimize
HERE = Path(__file__).resolve().parent

def ols(X, y):
    b = np.linalg.lstsq(X, y, rcond=None)[0]
    residual = y - X @ b
    df = len(y) - X.shape[1]
    cov = (residual @ residual / df) * np.linalg.inv(X.T @ X)
    se = np.sqrt(np.diag(cov))
    ci = np.column_stack((b-stats.t.ppf(.975, df)*se,b+stats.t.ppf(.975,df)*se))
    return b, cov, ci

def power(n, d):
    # Matches R power.t.test(strict=FALSE): rejection tail in effect direction.
    return stats.nct.sf(stats.t.ppf(.975,2*n-2),2*n-2,d*np.sqrt(n/2))

import platform, sys, hashlib
arm=np.repeat([0.,1.],4); y=np.array([1.,2.,3.,4.,3.,4.,5.,10.]); z=np.array([1.,2.,3.,4.,1.,2.,3.,np.nan])
X=np.column_stack((np.ones(8),arm)); full=ols(X,y); keep=~np.isnan(z)
same=ols(X[keep],y[keep]); adjusted=ols(np.column_stack((X[keep],z[keep])),y[keep]); reverse=ols(np.column_stack((np.ones(8),1-arm)),y)
print('All rows treatment-control:',full[0][1]); print('Complete rows treatment-control:',same[0][1]); print('Adjusted complete rows:',adjusted[0][1]); print('Reverse contrast:',reverse[0][1])
print('Included IDs:',np.arange(1,9)); print('Python:',sys.version,'platform:',platform.platform(),'NumPy:',np.__version__)
print('SHA256 of script:',hashlib.sha256(Path(__file__).read_bytes()).hexdigest())
assert np.allclose([full[0][1],same[0][1],adjusted[0][1],reverse[0][1]],[3,1.5,2,-3])
assert np.allclose(X@full[0],np.column_stack((np.ones(8),1-arm))@reverse[0])

Download Python script

Reproduce this Case

Every number above comes from one base-R script, with no packages to install.

Download case-009-reproduce-results.R →

Sources

← Case 008: Missing Data Across Waves: What Will Reviewers Accept?
All Cases →

Still stuck after the first checks?

Some problems turn on the details of your design, data, or the exact reviewer comment. A free 30-minute consult can identify the next defensible step and what it would take.

Book a free consult