Standard Paired Comparison
Executive Summary

Executive Summary

Did extra sleep drug2 change from extra sleep drug1?

Pairs Analysed
10
Mean Difference
1.58
Cohen's dz
1.285
Headline Test
Wilcoxon signed-rank
Headline p-value
0.004
Pairs Increased
90.00%
extra sleep drug2 genuinely increased from extra sleep drug1 (Wilcoxon signed-rank, p = 0.004). The mean change (extra sleep drug2 minus extra sleep drug1) is 1.58 (95% CI 0.70 to 2.46), a large effect (Cohen's dz = 1.28). Across the pairs, 90.00% increased and 0.00% decreased. Because the differences are not normally distributed, the rank-based Wilcoxon signed-rank test is the headline here.
What this means

Drug2 delivers genuinely more sleep than drug1: patients gained 1.58 extra hours on average (95% CI 0.70 to 2.46), a large effect (Cohen's dz = 1.28). The Wilcoxon signed-rank test confirms this difference is real at p = 0.004. Across the 10 pairs, 90% increased and 0% decreased, so the direction is consistent. The rank-based test is the recommended verdict because the paired differences fail the normality check.

Overview

Analysis Overview

Paired comparison of extra sleep drug1 and extra sleep drug2 across 10 matched pairs.

N Pairs10
Mean Difference1.58
Cohens Dz1.285
What this means

This analysis compares how much extra sleep patients gain from drug2 versus drug1 by measuring the change within each of the 10 matched pairs. Because both drugs were measured on the same subjects, the analysis works on the within-pair difference, which cancels out stable person-to-person variation and makes the test more powerful. The two measurements correlate at r = 0.80, so pairing removes a substantial share of individual variation and sharpens detection of a real drug effect.

Data Preparation

Data Quality

Pair completeness and the normality check on the differences.

Initial Rows10
Final Rows10
Rows Removed0
What this means

All 10 rows loaded formed complete pairs, so no data was dropped. A Shapiro-Wilk test on the paired differences rejects normality (p = 0.033), meaning the differences do not follow a normal distribution. This failure of the normality check makes the rank-based Wilcoxon signed-rank test the safer headline, even though the parametric t-test also reached significance.

Visualization

Before vs After

Every pair's extra sleep drug1 plotted against its extra sleep drug2.

What this means

Nine of the 10 pairs sit above the no-change diagonal, showing a tight upward pattern. The two measurements track closely within each subject (r = 0.80), confirming that pairing was effective. One pair sits exactly on the diagonal (no change), while the other nine all shifted upward, with the largest gain at 4.6 hours. This consistent upward movement supports a real drug effect.

Visualization

Distribution of Changes

How much each pair changed between extra sleep drug1 and extra sleep drug2.

What this means

The per-pair changes center at 1.58 with a standard deviation of 1.23, and the bulk of the distribution sits to the right of zero. Most changes cluster between 0.8 and 2.4 hours, with one outlier at 4.6 hours. The concentration to the right of zero confirms a real shift toward more sleep under drug2, though the spread shows variability in how much individual patients benefit.

Data Table

Statistical Tests

Paired t-test, Wilcoxon signed-rank, effect size, and the normality check.

TestStatisticP ValueEffect Or CIInterpretation
Paired t-test4.0620.003mean difference 1.58 (95% CI 0.70 to 2.46)Tests whether the mean change in extra sleep drug2 differs from zero: significant (p = 0.003).
Wilcoxon signed-rank540.004median difference 1.30Rank-based, no normality assumption: significant (p = 0.004).
Cohen's dz1.285n/alarge effectStandardized paired effect size: dz = 1.28.
Shapiro-Wilk (differences)0.033normality rejectedThe paired differences are not normally distributed — favor the Wilcoxon result.
What this means

Both the paired t-test (p = 0.003, mean difference 1.58, 95% CI 0.70 to 2.46) and the Wilcoxon signed-rank test (p = 0.004, median difference 1.30) reach significance and agree on direction. Cohen's dz = 1.285 marks a large standardized effect. The Shapiro-Wilk test rejects normality of the differences (p = 0.033), so the Wilcoxon signed-rank result is the headline. The agreement between both tests strengthens confidence that the difference is real and not an artifact of distributional assumptions.

Rate this report Was this the answer you needed?
The exact source that produced this report — yours to keep, read, and re-run.
Download PDF
How this was computed method · R source · citation
The code that did it

Paired Comparison — Before vs After

Did a paired or repeated measurement change? Each row is one subject measured twice — before vs after, treatment vs control on the same unit, two raters on the same items. The analysis works on the within-pair change (after minus before) and runs the whole standard paired toolkit at once: a paired t-test with a confidence interval on the mean change, a Wilcoxon signed-rank cross-check, Cohen's dz effect size, and a normality check on the differences that decides which result is the safer headline.

Why This Method?

When the same unit is measured twice, a two-sample test wastes power on the stable person-to-person differences. Working on each subject's own change cancels that variation out, so a paired test detects a real shift with far fewer observations. Running the parametric and rank-based versions together, with a normality check on the differences, lets the report itself say which result to trust.

What This Analysis Covers

  • Before against after per pair (scatter with a no-change diagonal)
  • The distribution of per-pair changes (histogram)
  • The paired t-test, Wilcoxon signed-rank, effect size, and normality check

Standard Library

Platform standard-library module (LAT-1441): runs on ANY dataset via the semantic mapping {before, after}. All narrative is derived from the user's own column names and computed values.

suppressPackageStartupMessages(library(DT))
suppressPackageStartupMessages(library(htmlwidgets))
suppressPackageStartupMessages(library(arrow))
suppressPackageStartupMessages(library(knitr))
suppressPackageStartupMessages(library(rmarkdown))
suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(tidyr))
suppressPackageStartupMessages(library(ggplot2))
suppressPackageStartupMessages(library(stringr))
suppressPackageStartupMessages(library(lubridate))
suppressPackageStartupMessages(library(broom))
suppressPackageStartupMessages(library(Matrix))
suppressPackageStartupMessages(library(cluster))
suppressPackageStartupMessages(library(data.table))

Core Analysis Pipeline

compute_shared <- function(df, params, col_map = list()) {
  # === SHARED EXPORTS ===
  #   initial_rows/final_rows/rows_removed  $ row accounting
  #   n / n_dropped        $ complete pairs used + incomplete rows dropped
  #   before_h / after_h   $ humanized user names for the two mapped columns
  #   mean_d / sd_d / median_d / ci_low / ci_high  $ change statistics
  #   paired_r             $ within-subject correlation of before vs after
  #   t_stat / t_p         $ paired t-test statistic + p
  #   w_V / w_p            $ Wilcoxon signed-rank statistic + p
  #   dz                   $ Cohen's dz (mean(d)/sd(d))
  #   shapiro_p / nonparam_preferred  $ normality of differences + headline choice
  #   pct_inc/pct_dec/pct_unch        $ share of pairs up/down/flat
  #   dir_word             $ direction language consistent with sign of mean_d
  #   primary_test/primary_p/primary_sig  $ the headline result
  #   test_results_df      $ test, statistic, p_value, effect_or_ci, interpretation
  #   paired_points_df     $ before_value, after_value (<= 1000 sampled)
  #   difference_dist_df   $ difference (<= 2000 sampled)
  #   metrics / json_output
  # === /SHARED EXPORTS ===

Step 1: Resolve mapped columns (humanized for all prose)

initial_rows <- nrow(df)
  before_h <- humanize_semantic("before", col_map)
  after_h  <- humanize_semantic("after", col_map)
  if (!("before" %in% names(df)) || !("after" %in% names(df))) {
    stop(sprintf("Paired comparison needs both &#x27;%s' (the before measurement) and '%s' (the after measurement) mapped.",
                 before_h, after_h))
  }

Step 2: Coerce both measurements to numeric (95% rule)

coerce_num <- function(v, label_h) {
    if (is.numeric(v)) return(v)
    ch <- as.character(v)
    non_blank <- !is.na(ch) & trimws(ch) != ""
    conv <- suppressWarnings(as.numeric(ch))
    if (sum(non_blank) == 0 ||
        sum(!is.na(conv[non_blank])) < 0.95 * sum(non_blank)) {
      stop(sprintf("The column &#x27;%s' does not look numeric — fewer than 95%% of its values parse as numbers. Pick a numeric column.",
                   label_h))
    }
    conv
  }
  b <- coerce_num(df$before, before_h)
  a <- coerce_num(df$after, after_h)

Step 3: Keep row-wise complete pairs; drop incomplete rows (reported)

keep <- !is.na(b) & !is.na(a)
  n_dropped <- sum(!keep)
  b <- b[keep]; a <- a[keep]
  n <- length(b)
  final_rows <- n
  rows_removed <- initial_rows - final_rows
  if (n < 6) {
    stop(sprintf("Only %d complete %s / %s pairs remained after dropping incomplete rows — at least 6 are needed for a paired comparison.",
                 n, before_h, after_h))
  }

Step 4: The within-pair change and its summary statistics

d <- a - b
  mean_d   <- mean(d)
  sd_d     <- stats::sd(d)
  median_d <- stats::median(d)
  paired_r <- suppressWarnings(tryCatch(stats::cor(b, a), error = function(e) NA_real_))
  if (is.na(sd_d)) sd_d <- 0

Step 5: Paired t-test (mean change + 95% CI)

tt <- tryCatch(stats::t.test(a, b, paired = TRUE), error = function(e) NULL)
  if (!is.null(tt)) {
    t_stat <- unname(tt$statistic)
    t_p    <- tt$p.value
    ci     <- as.numeric(tt$conf.int)
    ci_low <- ci[1]; ci_high <- ci[2]
  } else {
    t_stat <- NA_real_; t_p <- NA_real_; ci_low <- mean_d; ci_high <- mean_d
  }

Step 6: Wilcoxon signed-rank (rank-based, no normality assumption)

ww <- tryCatch(suppressWarnings(stats::wilcox.test(a, b, paired = TRUE)),
                 error = function(e) NULL)
  w_V <- if (!is.null(ww)) unname(ww$statistic) else NA_real_
  w_p <- if (!is.null(ww)) ww$p.value else NA_real_

Step 7: Cohen's dz + normality of the differences

dz <- if (!is.na(sd_d) && sd_d > 0) mean_d / sd_d else NA_real_
  shapiro_p <- NA_real_
  if (n >= 3 && n <= 5000) {
    shapiro_p <- tryCatch(stats::shapiro.test(d)$p.value, error = function(e) NA_real_)
  }
  nonparam_preferred <- !is.na(shapiro_p) && shapiro_p < 0.05

Step 8: Direction language + share of pairs moving each way

n_inc <- sum(d > 0); n_dec <- sum(d < 0); n_unch <- sum(d == 0)
  pct_inc  <- 100 * n_inc  / n
  pct_dec  <- 100 * n_dec  / n
  pct_unch <- 100 * n_unch / n
  dir_word <- if (mean_d > 0) "increased" else if (mean_d < 0) "decreased" else "stayed flat"

  primary_test <- if (nonparam_preferred) "Wilcoxon signed-rank" else "Paired t-test"
  primary_p    <- if (nonparam_preferred) w_p else t_p
  primary_sig  <- !is.na(primary_p) && primary_p < 0.05

Step 9: Test-results table (paired t, Wilcoxon, Cohen's dz, normality)

tr_rows <- list()
  add_test <- function(test, statistic, p_value, effect_or_ci, interpretation) {
    tr_rows[[length(tr_rows) + 1]] <<- data.frame(
      test = test,
      statistic = if (is.na(statistic)) NA_real_ else round(statistic, 3),
      p_value = fmt_p(p_value),
      effect_or_ci = effect_or_ci,
      interpretation = interpretation,
      stringsAsFactors = FALSE
    )
  }
  add_test("Paired t-test", t_stat, t_p,
           sprintf("mean difference %s(95%% CI %s to %s)", r2(mean_d), r2(ci_low), r2(ci_high)),
           sprintf("Tests whether the mean change in %s differs from zero: %s.", after_h, sig_phrase(t_p)))
  add_test("Wilcoxon signed-rank", w_V, w_p,
           sprintf("median difference %s", r2(median_d)),
           sprintf("Rank-based, no normality assumption: %s.", sig_phrase(w_p)))
  add_test("Cohen&#x27;s dz", dz, NA_real_,
           sprintf("%s effect", dz_word(dz)),
           sprintf("Standardized paired effect size: dz = %s.", r2(dz)))
  if (!is.na(shapiro_p)) {
    add_test("Shapiro-Wilk(differences)", NA_real_, shapiro_p,
             if (shapiro_p < 0.05) "normality rejected" else "normality plausible",
             if (shapiro_p < 0.05)
               "The paired differences are not normally distributed — favor the Wilcoxon result."
             else
               "No strong evidence against normal differences — the paired t-test is reliable.")
  }
  test_results_df <- do.call(rbind, tr_rows)
  rownames(test_results_df) <- NULL

Step 10: Chart datasets — sampled with a fixed seed

set.seed(42)
  sidx <- if (n > 1000) sample(n, 1000) else seq_len(n)
  paired_points_df <- data.frame(before_value = b[sidx], after_value = a[sidx],
                                 stringsAsFactors = FALSE)
  paired_points_df <- paired_points_df[order(paired_points_df$before_value), , drop = FALSE]
  rownames(paired_points_df) <- NULL

  set.seed(42)
  didx <- if (n > 2000) sample(n, 2000) else seq_len(n)
  difference_dist_df <- data.frame(difference = round(d[didx], 4), stringsAsFactors = FALSE)

  metrics <- list(
    `Pairs Analysed`   = n,
    `Mean Difference`  = round(mean_d, 3),
    `Cohen&#x27;s dz`       = if (is.na(dz)) NA_real_ else round(dz, 3),
    `Headline Test`    = primary_test,
    `Headline p-value` = fmt_p(primary_p),
    `Pairs Increased`  = paste0(r2(pct_inc), "%")
  )

  json_output <- list(
    answer = paste0(
      "Paired comparison of ", after_h, " versus ", before_h, " across ",
      format(n, big.mark = ","), " complete pairs: the mean change(", after_h,
      " minus ", before_h, ") is ", r2(mean_d), " (95% CI ", r2(ci_low), " to ",
      r2(ci_high), "). The ", primary_test, " finds this change ", sig_phrase(primary_p),
      " (Cohen&#x27;s dz = ", r2(dz), ", ", dz_word(dz), " effect); ",
      r2(pct_inc), "% of pairs increased and ", r2(pct_dec), "% decreased."
    ),
    cards = lapply(
      c("tldr", "overview", "preprocessing", "paired_change",
        "difference_distribution", "test_results"),
      function(cid) list(id = cid, metrics = metrics)
    )
  )

  list(
    initial_rows = initial_rows, final_rows = final_rows,
    rows_removed = rows_removed, n = n, n_dropped = n_dropped,
    before_h = before_h, after_h = after_h,
    mean_d = mean_d, sd_d = sd_d, median_d = median_d,
    ci_low = ci_low, ci_high = ci_high, paired_r = paired_r,
    t_stat = t_stat, t_p = t_p, w_V = w_V, w_p = w_p, dz = dz,
    shapiro_p = shapiro_p, nonparam_preferred = nonparam_preferred,
    pct_inc = pct_inc, pct_dec = pct_dec, pct_unch = pct_unch,
    dir_word = dir_word,
    primary_test = primary_test, primary_p = primary_p, primary_sig = primary_sig,
    test_results_df = test_results_df,
    paired_points_df = paired_points_df, difference_dist_df = difference_dist_df,
    metrics = metrics, json_output = json_output
  )
}
Your data has more stories to tell.Run any analysis on your own data — validated R modules, interactive reports, AI insights, and PDF export. 500 free credits on signup.
Try Free — No SignupSign Up Free

Cite this analysis

Report an Issue

Tell us what's wrong. You'll get a free re-run of this analysis so you can try again with different parameters. If the re-run still doesn't meet your expectations, we'll refund your credits.

Want to run this analysis on your own data? Upload CSV — Free Analysis See Pricing