oneSampleTTestFlowchart = {
// Canvas setup
const width = 1300;
const height = 500;
const padding = 60;
// Create SVG with explicit viewBox
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 16px sans-serif;");
// Add title at the bottom
svg.append("text")
.attr("x", width / 2)
.attr("y", height - 20)
.attr("text-anchor", "middle")
.attr("font-size", "20px")
.attr("font-weight", "bold")
.text("One-Sample T-Test Procedure");
// Define nodes with horizontal layout
const nodes = [
// Input data and hypothesized value
{id: "A", label: "Sample Data", x: padding + 80, y: 150},
{id: "B", label: "Hypothesized\nPopulation Mean (μ₀)", x: padding + 80, y: 250},
// Initial calculations
{id: "C", label: "Calculate Sample\nMean (x̄)", x: padding + 280, y: 150},
{id: "D", label: "Calculate Sample\nStandard Deviation (s)", x: padding + 280, y: 250},
// Further calculations
{id: "E", label: "Calculate Standard\nError (s/√n)", x: padding + 480, y: 250},
// T-statistic
{id: "F", label: "Calculate t-statistic\n(x̄ - μ₀)/(s/√n)", x: padding + 680, y: 200},
// Final path
{id: "G", label: "Determine\np-value", x: padding + 880, y: 200},
{id: "H", label: "p-value < α?", x: padding + 1080, y: 200, isDecision: true},
{id: "I", label: "Reject null\nhypothesis", x: padding + 990, y: 330},
{id: "J", label: "Retain null\nhypothesis", x: padding + 1170, y: 330}
];
// Define edges with draw order
const edges = [
// Sample data path
{source: "A", target: "C", label: "", order: 1},
{source: "A", target: "D", label: "", order: 1},
// Hypothesized mean to t-statistic
{source: "B", target: "F", label: "", order: 1},
// Calculation path
{source: "C", target: "F", label: "", order: 1},
{source: "D", target: "E", label: "", order: 1},
{source: "E", target: "F", label: "", order: 1},
// Final path
{source: "F", target: "G", label: "", order: 1},
{source: "G", target: "H", label: "", order: 1},
{source: "H", target: "I", label: "Yes", order: 1},
{source: "H", target: "J", label: "No", order: 1}
];
// Sort edges by order to control draw sequence
edges.sort((a, b) => a.order - b.order);
// Define arrow marker
svg.append("defs").append("marker")
.attr("id", "arrowhead")
.attr("viewBox", "0 0 10 10")
.attr("refX", 8)
.attr("refY", 5)
.attr("markerWidth", 8)
.attr("markerHeight", 8)
.attr("orient", "auto")
.append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 z")
.attr("fill", "#666");
// Draw edges with improved path calculation
const edgeLines = svg.selectAll("path.edge")
.data(edges)
.join("path")
.attr("class", d => `edge order-${d.order}`)
.attr("d", d => {
const source = nodes.find(n => n.id === d.source);
const target = nodes.find(n => n.id === d.target);
// Calculate connector points
let sourceX, sourceY, targetX, targetY;
let path = "";
// Special case for decision diamond
if (source.isDecision) {
if (d.label === "Yes") {
// Going down-left to I
sourceX = source.x - 25;
sourceY = source.y + 20;
targetX = target.x;
targetY = target.y - 25;
// Return path with offset to avoid overlapping
path = `M${sourceX},${sourceY} L${sourceX},${(sourceY + targetY)/2} L${targetX},${(sourceY + targetY)/2} L${targetX},${targetY}`;
} else if (d.label === "No") {
// Going down-right to J
sourceX = source.x + 25;
sourceY = source.y + 20;
targetX = target.x;
targetY = target.y - 25;
// Return path with offset to avoid overlapping
path = `M${sourceX},${sourceY} L${sourceX},${(sourceY + targetY)/2} L${targetX},${(sourceY + targetY)/2} L${targetX},${targetY}`;
}
}
// Special case: Sample Data to Standard Deviation
else if (source.id === "A" && target.id === "D") {
// A to D (diagonal down)
sourceX = source.x + 30;
sourceY = source.y + 25;
targetX = target.x - 70;
targetY = target.y;
// Create angled path
path = `M${sourceX},${sourceY} L${(sourceX + targetX)/2},${(sourceY + targetY)/2} L${targetX},${targetY}`;
}
// Hypothesized mean to t-statistic
else if (source.id === "B" && target.id === "F") {
// B to F (diagonal up)
sourceX = source.x + 70;
sourceY = source.y;
targetX = target.x - 70;
targetY = target.y;
// Create a curved path that avoids other nodes
const midX1 = sourceX + 120;
const midY1 = sourceY;
const midX2 = midX1 + 100;
const midY2 = (sourceY + targetY) / 2;
const midX3 = targetX - 100;
const midY3 = targetY;
path = `M${sourceX},${sourceY} C${midX1},${midY1} ${midX2},${midY2} ${targetX},${targetY}`;
}
// Sample mean to t-statistic
else if (source.id === "C" && target.id === "F") {
// C to F (diagonal down)
sourceX = source.x + 70;
sourceY = source.y;
targetX = target.x - 70;
targetY = target.y;
// Create a curved path
const midX = (sourceX + targetX) / 2;
path = `M${sourceX},${sourceY} C${midX},${sourceY} ${midX},${targetY} ${targetX},${targetY}`;
}
// Standard error to t-statistic
else if (source.id === "E" && target.id === "F") {
// E to F (diagonal up)
sourceX = source.x + 70;
sourceY = source.y;
targetX = target.x - 70;
targetY = target.y;
// Create a curved path
const midX = (sourceX + targetX) / 2;
path = `M${sourceX},${sourceY} C${midX},${sourceY} ${midX},${targetY} ${targetX},${targetY}`;
}
else if (target.y > source.y + 30) {
// General case: Vertical flow down with significant gap
sourceX = source.x;
sourceY = source.y + 25;
targetX = target.x;
targetY = target.y - 25;
path = `M${sourceX},${sourceY} L${sourceX},${(sourceY + targetY)/2} L${targetX},${(sourceY + targetY)/2} L${targetX},${targetY}`;
}
else if (target.y < source.y - 30) {
// General case: Vertical flow up with significant gap
sourceX = source.x;
sourceY = source.y - 25;
targetX = target.x;
targetY = target.y + 25;
path = `M${sourceX},${sourceY} L${sourceX},${(sourceY + targetY)/2} L${targetX},${(sourceY + targetY)/2} L${targetX},${targetY}`;
}
else {
// Horizontal flow (default)
sourceX = source.x + 70;
sourceY = source.y;
targetX = target.x - 70;
targetY = target.y;
path = `M${sourceX},${sourceY} L${targetX},${targetY}`;
}
return path;
})
.attr("stroke", "#666")
.attr("stroke-width", 2)
.attr("fill", "none")
.attr("marker-end", "url(#arrowhead)");
// Add edge labels for Yes/No
svg.selectAll(".edgelabel")
.data(edges.filter(d => d.label !== ""))
.join("text")
.attr("class", "edgelabel")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.attr("x", d => {
const source = nodes.find(n => n.id === d.source);
const target = nodes.find(n => n.id === d.target);
if (d.label === "Yes") {
return (source.x + target.x) / 2 - 30;
} else if (d.label === "No") {
return (source.x + target.x) / 2 + 30;
} else {
return (source.x + target.x) / 2;
}
})
.attr("y", d => {
const source = nodes.find(n => n.id === d.source);
const target = nodes.find(n => n.id === d.target);
if (d.label === "Yes" || d.label === "No") {
return (source.y + target.y) / 2 - 10;
} else {
return source.y - 10;
}
})
.attr("font-size", "14px")
.attr("font-weight", "bold")
.attr("fill", d => d.label === "Yes" ? "#5a9bd5" : (d.label === "No" ? "#ff9052" : "#333"))
.text(d => d.label);
// Draw nodes with fixed box sizes - after drawing paths to ensure nodes appear on top
const node = svg.selectAll(".node")
.data(nodes)
.join("g")
.attr("class", "node")
.attr("transform", d => `translate(${d.x},${d.y})`);
// Add node shapes (rectangles or diamonds) with consistent sizing
node.each(function(d) {
const elem = d3.select(this);
if (d.isDecision) {
// Diamond for decision node
elem.append("polygon")
.attr("points", "0,-30 60,0 0,30 -60,0")
.attr("fill", "#f8d56f")
.attr("stroke", "#d4a82e")
.attr("stroke-width", 2);
} else {
// Rectangle for regular node with fixed width
const boxWidth = 140;
elem.append("rect")
.attr("x", -boxWidth/2)
.attr("y", -25)
.attr("width", boxWidth)
.attr("height", 50)
.attr("rx", 5)
.attr("ry", 5)
.attr("fill", d => {
if (d.id === "I" || d.id === "J") return "#f0f0f0";
if (d.id === "A" || d.id === "B") return "#e2f0d9"; // Light green for data inputs
return "#b3deff";
})
.attr("stroke", d => {
if (d.id === "I" || d.id === "J") return "#999";
if (d.id === "A" || d.id === "B") return "#70ad47"; // Green border for data inputs
return "#4a98e0";
})
.attr("stroke-width", 2);
}
});
// Add node labels with better text wrapping
node.append("text")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.attr("font-size", "14px")
.attr("font-weight", d => (d.id === "H" ? "bold" : "normal"))
.attr("fill", "#333")
.each(function(d) {
const lines = d.label.split('\n');
const elem = d3.select(this);
if (lines.length === 1) {
elem.text(d.label);
} else {
lines.forEach((line, i) => {
const lineHeight = 16;
const yOffset = (i - (lines.length - 1) / 2) * lineHeight;
elem.append("tspan")
.attr("x", 0)
.attr("y", yOffset)
.text(line);
});
}
});
// Add interactivity
node.on("mouseover", function(event, d) {
d3.select(this).select("rect, polygon")
.transition()
.duration(200)
.attr("fill", d => {
if (d.isDecision) return "#ffc107";
if (d.id === "A" || d.id === "B") return "#b8e986"; // Brighter green on hover
if (d.id === "I" || d.id === "J") return "#e6e6e6";
return "#7fc9ff";
});
})
.on("mouseout", function(event, d) {
d3.select(this).select("rect, polygon")
.transition()
.duration(200)
.attr("fill", d => {
if (d.isDecision) return "#f8d56f";
if (d.id === "I" || d.id === "J") return "#f0f0f0";
if (d.id === "A" || d.id === "B") return "#e2f0d9";
return "#b3deff";
});
});
return svg.node();
}
Key Takeaways: One-Sample t-Test
Tip
- Purpose: Compare a sample mean to a known or hypothesized population value
- When to use: When testing if a sample differs from a specified value
- Assumptions: Random sampling, normality (or large sample size)
- Null hypothesis: The population mean equals the specified value (\(H_0: \mu = \mu_0\))
- Interpretation: If p < 0.05, there is a significant difference between the sample mean and the hypothesized value
- Common applications: Quality control, testing against standards or benchmarks, validating claims
What is the One-Sample t-Test?
The one-sample t-test is a statistical method used to determine whether the mean of a sample differs significantly from a known or hypothesized population value. It’s one of the simplest yet most useful statistical tests for comparing observed data against a standard or theoretical expectation.
Tip
When to use the one-sample t-test:
- When comparing a sample mean to a known standard or reference value
- When testing if a sample comes from a population with a specific mean
- When validating claims about population means based on sample data
- When quality testing products against specifications
This online calculator allows you to quickly perform a one-sample t-test, visualize your data, and interpret the results with confidence.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 1300
library(shiny)
library(bslib)
library(ggplot2)
library(bsicons)
library(vroom)
library(shinyjs)
ui <- page_sidebar(
title = "One-Sample t-Test",
useShinyjs(), # Enable shinyjs for resetting inputs
sidebar = sidebar(
width = 400,
card(
card_header("Data Input"),
accordion(
accordion_panel(
"Manual Input",
textAreaInput("sample_input", "Sample Data [One value per row]", rows = 8,
placeholder = "Paste values here..."),
div(
actionLink("use_example", "Use example data", style = "color:#0275d8;"),
tags$span(bs_icon("file-earmark-text"), style = "margin-left: 5px; color: #0275d8;")
)
),
accordion_panel(
"File Upload",
fileInput("file_upload", "Upload CSV or TXT file:",
accept = c("text/csv", "text/plain", ".csv", ".txt")),
checkboxInput("header", "File has header", TRUE),
conditionalPanel(
condition = "output.file_uploaded",
div(
selectInput("sample_var", "Sample variable:", choices = NULL),
actionButton("clear_file", "Clear File", class = "btn-danger btn-sm")
)
)
),
id = "input_method",
open = 1
),
# Advanced Options accordion
accordion(
accordion_panel(
"Advanced Options",
card(
card_header("Null Hypothesis:"),
card_body(
tags$div(style = "margin-bottom: 10px;",
numericInput("null_hypothesis", "mu =", 0.0, width = "100%")
)
)
),
card(
card_header("Alternative Hypothesis:"),
card_body(
radioButtons("alternative", NULL,
choices = c("Population mean ≠ mu₀" = "two.sided",
"Population mean < mu₀" = "less",
"Population mean > mu₀" = "greater"),
selected = "two.sided")
)
),
card(
card_header("Confidence Level:"),
card_body(
sliderInput("conf_level", NULL, min = 0.8, max = 0.99, value = 0.95, step = 0.01)
)
)
),
open = FALSE
),
actionButton("run_test", "Run Test", class = "btn btn-primary")
),
hr(),
card(
card_header("Interpretation"),
card_body(
div(class = "alert alert-info",
tags$ul(
tags$li("The one-sample t-test compares the mean of a sample to a hypothesized value."),
tags$li(tags$b("Null hypothesis:"), " The population mean equals the specified value."),
tags$li(tags$b("Alternative:"), " The population mean differs from (or is less/greater than) the specified value."),
tags$li("If p-value < 0.05, there is a significant difference between the sample mean and the hypothesized value."),
tags$li("This test assumes the data is approximately normally distributed.")
)
)
)
)
),
layout_column_wrap(
width = 1,
card(
card_header("Test Results"),
card_body(
navset_tab(
nav_panel("Results", uiOutput("error_message"), verbatimTextOutput("test_results")),
nav_panel("Explanation", div(style = "font-size: 0.9rem;",
p("The One-Sample t-Test evaluates whether the population mean differs from a specified value:"),
tags$ul(
tags$li("It calculates the t-statistic by comparing the sample mean to the hypothesized value, considering the standard error."),
tags$li("The test assumes that the data follows a normal distribution."),
tags$li("Degrees of freedom = n-1, where n is the sample size."),
tags$li("A small p-value indicates the population mean likely differs from the hypothesized value.")
)
))
)
)
),
card(
card_header("Visual Assessment"),
card_body(
navset_tab(
nav_panel("Histogram",
navset_tab(
nav_panel("Plot", plotOutput("histogram")),
nav_panel("Explanation", div(style = "font-size: 0.9rem;",
p("The histogram shows the distribution of your sample:"),
tags$ul(
tags$li("The vertical dashed line shows the sample mean."),
tags$li("The vertical dotted line shows the hypothesized population mean value."),
tags$li("The curve represents the normal distribution with the same mean and standard deviation as your sample."),
tags$li("Comparing these lines helps you visualize the difference being tested.")
)
))
)
),
nav_panel("QQ Plot",
navset_tab(
nav_panel("Plot", plotOutput("qqplot")),
nav_panel("Explanation", div(style = "font-size: 0.9rem;",
p("The Q-Q plot helps assess if your data follows a normal distribution:"),
tags$ul(
tags$li("Points should follow the diagonal line if the data is normally distributed."),
tags$li("Deviations from the line suggest departures from normality."),
tags$li("The t-test assumes normality, so substantial deviations may affect the reliability of the test results.")
)
))
)
),
nav_panel("Confidence Interval",
navset_tab(
nav_panel("Plot", plotOutput("ci_plot")),
nav_panel("Explanation", div(style = "font-size: 0.9rem;",
p("The confidence interval plot shows:"),
tags$ul(
tags$li("The sample mean (point) and confidence interval (horizontal line)."),
tags$li("The vertical dotted line represents the hypothesized mean value."),
tags$li("If the confidence interval does not include the hypothesized value, the result is statistically significant."),
tags$li("The width of the interval reflects the precision of the estimate.")
)
))
)
)
)
)
)
)
)
server <- function(input, output, session) {
# Example data
example_data <- "8.5\n7.2\n12.4\n10.8\n9.3\n6.7\n11.5\n8.9\n10.2\n7.8"
# Track input method
input_method <- reactiveVal("manual")
# Function to clear file inputs
clear_file_inputs <- function() {
updateSelectInput(session, "sample_var", choices = NULL)
reset("file_upload")
}
# Function to clear text inputs
clear_text_inputs <- function() {
updateTextAreaInput(session, "sample_input", value = "")
}
# When example data is used, clear file inputs and set text inputs
observeEvent(input$use_example, {
input_method("manual")
clear_file_inputs()
updateTextAreaInput(session, "sample_input", value = example_data)
})
# When file is uploaded, clear text inputs and set file method
observeEvent(input$file_upload, {
if (!is.null(input$file_upload)) {
input_method("file")
clear_text_inputs()
}
})
# When clear file button is clicked, clear file and set manual method
observeEvent(input$clear_file, {
input_method("manual")
clear_file_inputs()
})
# When text input changes, clear file inputs if it has content
observeEvent(input$sample_input, {
if (!is.null(input$sample_input) && nchar(input$sample_input) > 0) {
input_method("manual")
clear_file_inputs()
}
}, ignoreInit = TRUE)
file_data <- reactive({
req(input$file_upload)
tryCatch({
vroom::vroom(input$file_upload$datapath, delim = NULL, col_names = input$header, show_col_types = FALSE)
}, error = function(e) {
showNotification(paste("File read error:", e$message), type = "error")
NULL
})
})
observe({
df <- file_data()
if (!is.null(df)) {
num_vars <- names(df)[sapply(df, is.numeric)]
updateSelectInput(session, "sample_var", choices = num_vars)
}
})
output$file_uploaded <- reactive({
!is.null(input$file_upload)
})
outputOptions(output, "file_uploaded", suspendWhenHidden = FALSE)
# Function to parse text input
parse_text_input <- function(text) {
if (is.null(text) || text == "") return(NULL)
input_lines <- strsplit(text, "\\r?\\n")[[1]]
input_lines <- input_lines[input_lines != ""]
numeric_values <- suppressWarnings(as.numeric(input_lines))
if (all(is.na(numeric_values))) return(NULL)
return(na.omit(numeric_values))
}
# Get sample values
sample_values <- reactive({
if (input_method() == "file" && !is.null(file_data()) && !is.null(input$sample_var)) {
df <- file_data()
return(na.omit(df[[input$sample_var]]))
} else {
return(parse_text_input(input$sample_input))
}
})
# Get mu value
mu_value <- reactive({
return(input$null_hypothesis)
})
# Validate input data
validate_data <- reactive({
sample <- sample_values()
if (is.null(sample)) {
return("Error: Please check your input. Make sure all values are numeric.")
}
if (length(sample) < 2) {
return("Error: At least 2 observations are required for the t-test.")
}
# Check if all values are identical (no variance)
if (var(sample) == 0) {
return("Error: All values are identical. The t-test requires some variability in the data.")
}
# Check for normality using Shapiro-Wilk test (just a warning)
if (length(sample) <= 50) { # Shapiro-Wilk limited to 50 observations
sw_test <- shapiro.test(sample)
if (sw_test$p.value < 0.05) {
return(paste0("Warning: Your data may not be normally distributed (Shapiro-Wilk p=",
round(sw_test$p.value, 4),
"). The t-test assumes normality."))
}
}
return(NULL)
})
output$error_message <- renderUI({
error <- validate_data()
if (!is.null(error) && input$run_test > 0) {
if (startsWith(error, "Warning")) {
div(class = "alert alert-warning", error)
} else {
div(class = "alert alert-danger", error)
}
}
})
# Run the t-test
test_result <- eventReactive(input$run_test, {
error <- validate_data()
if (!is.null(error) && startsWith(error, "Error")) return(NULL)
# Run the t.test with the specified parameters
t.test(
sample_values(),
mu = mu_value(),
alternative = input$alternative,
conf.level = input$conf_level
)
})
# Display test results
output$test_results <- renderPrint({
if (is.null(test_result())) return(NULL)
result <- test_result()
sample <- sample_values()
# Use alternative input for interpretation
alt_text <- switch(
input$alternative,
"two.sided" = "different from",
"less" = "less than",
"greater" = "greater than"
)
cat("One-Sample t-Test Results:\n")
cat("==========================\n\n")
cat("t-statistic:", round(result$statistic, 4), "\n")
cat("Degrees of freedom:", round(result$parameter, 0), "\n")
cat("p-value:", round(result$p.value, 6), "\n\n")
conf_pct <- paste0(input$conf_level * 100, "%")
cat(conf_pct, " Confidence Interval: [",
round(result$conf.int[1], 4), ", ",
round(result$conf.int[2], 4), "]\n\n", sep = "")
cat("Sample Summary:\n")
cat("---------------\n")
cat("Sample size:", length(sample), "\n")
cat("Sample mean:", round(result$estimate, 4), "\n")
cat("Sample standard deviation:", round(sd(sample), 4), "\n")
cat("Standard error of the mean:", round(sd(sample)/sqrt(length(sample)), 4), "\n\n")
cat("Test Information:\n")
cat("-----------------\n")
cat("Hypothesized mean:", mu_value(), "\n")
cat("Alternative hypothesis: true mean is", alt_text, mu_value(), "\n")
cat("Confidence level:", conf_pct, "\n")
# Effect size (Cohen's d)
d <- (mean(sample) - mu_value()) / sd(sample)
cat("\nEffect Size:\n")
cat("--------------\n")
cat("Cohen's d:", round(d, 4), "\n")
effect_size_interpretation <- ""
if (abs(d) < 0.2) {
effect_size_interpretation <- "negligible"
} else if (abs(d) < 0.5) {
effect_size_interpretation <- "small"
} else if (abs(d) < 0.8) {
effect_size_interpretation <- "medium"
} else {
effect_size_interpretation <- "large"
}
cat("Interpretation: The effect size is", effect_size_interpretation, "\n\n")
# Conclusion
cat("Conclusion:\n")
cat("-----------\n")
if (result$p.value < 0.05) {
cat("Interpretation: There is a significant difference (p < 0.05).\n")
cat("The sample mean is significantly", alt_text, "the hypothesized value of", mu_value(), ".\n")
} else {
cat("Interpretation: No significant difference detected (p ≥ 0.05).\n")
cat("We cannot conclude that the population mean is", alt_text, "the hypothesized value of", mu_value(), ".\n")
}
})
# Generate histogram
output$histogram <- renderPlot({
req(input$run_test > 0, !is.null(sample_values()))
sample <- sample_values()
# Calculate mean and standard deviation for normal curve
sample_mean <- mean(sample)
sample_sd <- sd(sample)
# Create data for the normal curve
x_range <- seq(min(sample) - 2 * sample_sd, max(sample) + 2 * sample_sd, length.out = 100)
normal_data <- data.frame(
x = x_range,
y = dnorm(x_range, mean = sample_mean, sd = sample_sd)
)
# Scale the normal curve to match histogram height
hist_data <- hist(sample, plot = FALSE)
max_height <- max(hist_data$density)
max_norm <- max(normal_data$y)
scale_factor <- max_height / max_norm
normal_data$y <- normal_data$y * scale_factor
ggplot() +
geom_histogram(data = data.frame(value = sample), aes(x = value, y = after_stat(density)),
bins = min(30, max(10, length(sample)/3)),
fill = "#5dade2", color = "#2874a6", alpha = 0.7) +
geom_line(data = normal_data, aes(x = x, y = y),
color = "#e74c3c", size = 1.2, linetype = "solid") +
geom_vline(xintercept = sample_mean, linetype = "dashed",
color = "#c0392b", size = 1.5) +
geom_vline(xintercept = mu_value(), linetype = "dotted",
color = "#8e44ad", size = 1.5) +
annotate("text", x = sample_mean, y = 0,
label = paste("Mean =", round(sample_mean, 2)),
vjust = -1, hjust = ifelse(sample_mean > mu_value(), 1.1, -0.1),
color = "#c0392b", fontface = "bold") +
annotate("text", x = mu_value(), y = 0,
label = paste("H₀ =", mu_value()),
vjust = -3, hjust = ifelse(mu_value() > sample_mean, 1.1, -0.1),
color = "#8e44ad", fontface = "bold") +
labs(title = "Distribution of Sample Values with Normal Curve",
subtitle = "With sample mean and hypothesized value",
x = "Value",
y = "Density") +
theme_minimal(base_size = 14)
})
# Generate QQ plot
output$qqplot <- renderPlot({
req(input$run_test > 0, !is.null(sample_values()))
sample <- sample_values()
ggplot(data.frame(sample = sample), aes(sample = sample)) +
stat_qq(color = "#3498db", size = 3) +
stat_qq_line(color = "#e74c3c", size = 1.2) +
labs(title = "Normal Q-Q Plot",
subtitle = "Points should follow the line if normally distributed",
x = "Theoretical Quantiles",
y = "Sample Quantiles") +
theme_minimal(base_size = 14)
})
# Generate confidence interval plot
output$ci_plot <- renderPlot({
req(input$run_test > 0, !is.null(test_result()))
result <- test_result()
sample <- sample_values()
mu <- mu_value()
# Extract confidence interval
ci_lower <- result$conf.int[1]
ci_upper <- result$conf.int[2]
sample_mean <- mean(sample)
# Create data frame for CI plot
ci_data <- data.frame(
parameter = "Mean",
estimate = sample_mean,
ci_lower = ci_lower,
ci_upper = ci_upper
)
# Determine if CI includes hypothesized mean
includes_mu <- ci_lower <= mu && mu <= ci_upper
ci_color <- ifelse(includes_mu, "#3498db", "#e74c3c")
ggplot(ci_data, aes(x = estimate, y = parameter)) +
geom_vline(xintercept = mu, linetype = "dotted",
color = "#8e44ad", size = 1.2) +
geom_pointrange(aes(xmin = ci_lower, xmax = ci_upper),
color = ci_color, size = 1.5, linewidth = 1.2) +
annotate("text", x = mu, y = 0.8,
label = paste("H₀ =", mu),
vjust = -1, color = "#8e44ad", fontface = "bold") +
annotate("text", x = sample_mean, y = 1.2,
label = paste("Mean =", round(sample_mean, 2)),
vjust = 1.5, color = ci_color, fontface = "bold") +
labs(title = paste0(input$conf_level * 100, "% Confidence Interval for the Mean"),
subtitle = ifelse(includes_mu,
"CI includes the hypothesized value (not significant)",
"CI excludes the hypothesized value (significant)"),
x = "Value",
y = "") +
theme_minimal(base_size = 14) +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank())
})
}
shinyApp(ui = ui, server = server)
How the One-Sample t-Test Works
The one-sample t-test evaluates whether the mean of a sample is statistically different from a specified value by comparing the observed mean difference to the variation within the sample.
Mathematical Procedure
State hypotheses:
- Null hypothesis \(H_0: \mu = \mu_0\) (the population mean equals the hypothesized value)
- Alternative hypothesis \(H_a: \mu \neq \mu_0\) (two-tailed), \(H_a: \mu > \mu_0\) (right-tailed), or \(H_a: \mu < \mu_0\) (left-tailed)
Calculate the t-statistic:
\[t = \frac{\bar{x} - \mu_0}{s/\sqrt{n}}\]
Where:
- \(\bar{x}\) is the sample mean
- \(\mu_0\) is the hypothesized population mean
- \(s\) is the sample standard deviation
- \(n\) is the sample size
Determine degrees of freedom:
\[df = n - 1\]
Calculate p-value by comparing the t-statistic to the t-distribution with \(n - 1\) degrees of freedom
Make a decision:
- If p < \(\alpha\) (typically 0.05): Reject the null hypothesis
- If p ≥ \(\alpha\): Fail to reject the null hypothesis
Effect Size (Cohen’s d)
For a one-sample t-test, Cohen’s d is calculated as:
\[d = \frac{|\bar{x} - \mu_0|}{s}\]
Interpretation: - d ≈ 0.2: Small effect - d ≈ 0.5: Medium effect - d ≈ 0.8: Large effect
Assumptions of the One-Sample t-Test
- Random sampling: The sample should be randomly selected from the population
- Normality: The data should follow an approximately normal distribution
- For large samples (n > 30), the t-test is robust to violations of normality due to the Central Limit Theorem
- Measurement level: The dependent variable should be measured on a continuous scale
Statistical Power Considerations
Important
Statistical Power Note: The power of a one-sample t-test depends on: - Sample size - Effect size (difference between sample mean and hypothesized value) - Significance level (α) - Variability within the sample
To achieve 80% power (standard convention) for detecting: - Small effect (d = 0.2): Need approximately 199 observations - Medium effect (d = 0.5): Need approximately 34 observations - Large effect (d = 0.8): Need approximately 15 observations
These calculations assume α = 0.05 for a two-tailed test.
Example 1: Testing Battery Life Claims
A manufacturer claims their new batteries last 40 hours on average. A consumer group tests 20 batteries and wants to verify this claim.
Data (battery life in hours): 38.6, 42.4, 39.8, 43.1, 40.2, 41.6, 39.4, 40.9, 37.8, 42.3, 41.8, 38.5, 40.4, 39.2, 41.5, 40.7, 39.6, 42.8, 40.1, 41.3
Analysis Steps:
- State hypotheses:
- \(H_0: \mu = 40\) (the mean battery life equals 40 hours)
- \(H_a: \mu \neq 40\) (the mean battery life differs from 40 hours)
- Calculate descriptive statistics:
- Sample mean: \(\bar{x} = 40.60\) hours
- Sample standard deviation: \(s = 1.52\) hours
- Sample size: \(n = 20\)
- Calculate the t-statistic:
- \(t = \frac{40.60 - 40}{1.52/\sqrt{20}} = \frac{0.60}{0.34} = 1.76\)
- Determine degrees of freedom:
- \(df = 20 - 1 = 19\)
- Calculate p-value:
- For a two-tailed test with df = 19, t = 1.76: p = 0.094
- Calculate effect size:
- \(d = \frac{|40.60 - 40|}{1.52} = 0.39\) (small to medium effect)
Results: - t(19) = 1.76, p = 0.094, d = 0.39 - 95% CI for mean: [39.96, 41.24] - Interpretation: There is no statistically significant evidence that the actual mean battery life differs from the claimed 40 hours (p > 0.05).
How to Report: “A one-sample t-test was conducted to determine whether the average battery life differed from the manufacturer’s claim of 40 hours. The mean battery life of the tested batteries (M = 40.60, SD = 1.52) was not significantly different from the claimed 40 hours, t(19) = 1.76, p = 0.094, d = 0.39, 95% CI [39.96, 41.24]. These results suggest that the manufacturer’s claim about battery life is plausible.”
Example 2: Testing Student Performance Against Standard
A teacher wants to determine if her class performs better than the national average score of 75 on a standardized test.
Data Summary: - National average (hypothesized value): 75 - Class average (sample mean): 78.6 - Standard deviation of class scores: 8.4 - Sample size: 25 students - Alternative hypothesis: Class mean > national average (one-tailed test)
Results: - t(24) = 2.14, p = 0.021, d = 0.43 - 95% CI for mean: [75.4, 81.8] - Interpretation: There is statistically significant evidence that the class performs better than the national average (p < 0.05).
How to Report: “The class’s mean test score (M = 78.6, SD = 8.4) was significantly higher than the national average of 75, t(24) = 2.14, p = 0.021, d = 0.43, 95% CI [75.4, 81.8]. This indicates that the class is performing above the national standard.”
How to Report One-Sample t-Test Results
When reporting the results of a one-sample t-test in academic papers or research reports, include the following elements:
[value], SD = [value]) was significantly [higher/lower/different from]
"The sample mean (M = [μ₀], t([df]) = [t-value], p = [p-value], d = [effect size],
the hypothesized value of [lower bound, upper bound]." 95% CI
For example:
"The sample mean (M = 78.6, SD = 8.4) was significantly higher than [75.4, 81.8]." the hypothesized value of 75, t(24) = 2.14, p = 0.021, d = 0.43, 95% CI
Additional information to consider including: - Direction of the effect (higher or lower than hypothesized value) - Whether assumptions were met (e.g., normality) - Whether the test was one-tailed or two-tailed
APA Style Reporting
For APA style papers (7th edition), report the one-sample t-test results as follows:
[variable] differed from [hypothesized value].
We conducted a one-sample t-test to examine whether [value], SD = [value]) was significantly
Results indicated that the sample mean (M = [higher/lower than/different from] the hypothesized value of [μ₀],
[df]) = [t-value], p = [p-value], d = [effect size], 95% CI [lower, upper]. t(
Reporting in Tables
When reporting multiple one-sample t-test results in a table, include these columns: - Variable tested - Hypothesized value (μ₀) - Sample mean and standard deviation - t-value - Degrees of freedom - p-value - Effect size (Cohen’s d) - 95% confidence interval
Test Your Understanding
- What does the one-sample t-test primarily compare?
- Two sample means to each other
- A sample mean to a hypothesized population value
- A sample median to a hypothesized value
- Two population variances
- What is the formula for the degrees of freedom in a one-sample t-test?
- n, where n is the sample size
- n - 1, where n is the sample size
- n - 2, where n is the sample size
- n + 1, where n is the sample size
- A researcher finds t(18) = 2.65, p = 0.016 when testing if a sample differs from a hypothesized value. What can they conclude?
- There is no significant difference from the hypothesized value
- There is a significant difference from the hypothesized value
- The test is invalid
- More data is needed
- What sample size would you need to detect a medium effect size (d = 0.5) with 80% power in a one-sample t-test?
- Approximately 15
- Approximately 34
- Approximately 64
- Approximately 200
- Which assumption becomes less critical for the one-sample t-test when the sample size is large (n > 30)?
- Random sampling
- Independent observations
- Normality of the data
- Continuous measurement scale
Answers: 1-B, 2-B, 3-B, 4-B, 5-C
Common Questions About the One-Sample t-Test
When should I use a one-sample t-test versus a two-sample t-test?
Use a one-sample t-test when comparing a single sample to a known or hypothesized value. Use a two-sample t-test (independent or paired) when comparing two separate samples to each other.
What if my data isn’t normally distributed?
If your sample size is large (n > 30), the t-test is generally robust to violations of normality due to the Central Limit Theorem. For smaller samples with non-normal data, consider using the non-parametric alternative, the one-sample Wilcoxon signed-rank test.
How do I report one-sample t-test results in research papers?
Include: t-value, degrees of freedom, p-value, sample mean and standard deviation, effect size (Cohen’s d), and the 95% confidence interval. For example: “The sample (M = 25.3, SD = 4.2) was significantly higher than the test value of 20, t(29) = 6.94, p < .001, d = 1.27, 95% CI [23.7, 26.9].”
What’s the difference between one-tailed and two-tailed tests?
A two-tailed test examines whether the sample mean differs from the hypothesized value in either direction (higher or lower). A one-tailed test examines only one direction (specifically higher than or lower than). Two-tailed tests are more conservative and generally recommended unless there’s a strong theoretical reason for testing only one direction.
How do I choose the hypothesized value to test against?
The hypothesized value (μ₀) should be based on: - Theoretical expectations or previous research findings - Industry standards or regulatory thresholds - Claimed values that need verification - Historical averages or baseline measurements - Meaningful reference points for your specific context
Can I use the one-sample t-test with very small samples?
Yes, the one-sample t-test can be used with small samples, but its reliability depends on how closely your data follows a normal distribution. With very small samples (n < 10), examine the data carefully for normality, or consider non-parametric alternatives if you have concerns about the distribution.
Examples of When to Use the One-Sample t-Test
- Quality control: Testing if product measurements meet specifications
- Educational assessment: Comparing class performance to national standards
- Medical research: Testing if a treatment changes metrics from baseline values
- Manufacturing: Validating if production output meets target levels
- Environmental science: Comparing pollution levels to regulatory thresholds
- Psychology: Testing if a group’s scores differ from normative values
- Consumer research: Verifying if customer ratings meet expected satisfaction levels
- Finance: Testing if investment returns differ from market benchmarks
- Sports science: Comparing athletic performance to established standards
- Agriculture: Testing if crop yields meet expected production levels
Step-by-Step Guide to the One-Sample t-Test
1. Check Assumptions
Before interpreting the results, verify these assumptions:
- Random sampling: The data should represent a random sample from the population
- Normality: The data should follow an approximately normal distribution
- Check using the Q-Q plot in the ‘Assumptions’ tab
- With larger samples (n > 30), the t-test is robust to normality violations
- Independence: The observations should be independent of each other
2. Interpret the Results
- Check the p-value:
- If p < 0.05, there is a statistically significant difference between the sample mean and the hypothesized value
- If p ≥ 0.05, there is not enough evidence to conclude the sample mean differs from the hypothesized value
- Examine the confidence interval:
- If it doesn’t include the hypothesized value, the difference is statistically significant
- The width indicates precision of the estimated mean
- Assess the effect size:
- Cohen’s d indicates the practical significance of the difference
- Consider whether the magnitude of the effect is meaningful in your context
References
- Student. (1908). The probable error of a mean. Biometrika, 6(1), 1-25.
- Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd ed.). Lawrence Erlbaum Associates.
- Cumming, G. (2014). The new statistics: Why and how. Psychological Science, 25(1), 7-29.
- Sawilowsky, S. S. (2009). New effect size rules of thumb. Journal of Modern Applied Statistical Methods, 8(2), 597-599.
- Lakens, D. (2013). Calculating and reporting effect sizes to facilitate cumulative science: a practical primer for t-tests and ANOVAs. Frontiers in Psychology, 4, 863.
- Lumley, T., Diehr, P., Emerson, S., & Chen, L. (2002). The importance of the normality assumption in large public health data sets. Annual Review of Public Health, 23(1), 151-169.
Reuse
Citation
BibTeX citation:
@online{kassambara2025,
author = {Kassambara, Alboukadel},
title = {One-Sample {t-Test} {Calculator} \textbar{} {Compare}
{Sample} {Mean} to {Known} {Value}},
date = {2025-04-07},
url = {https://www.datanovia.com/apps/statfusion/analysis/inferential/mean-comparisons/one-sample/one-sample-t-test.html},
langid = {en}
}
For attribution, please cite this work as:
Kassambara, Alboukadel. 2025. “One-Sample t-Test Calculator |
Compare Sample Mean to Known Value.” April 7, 2025. https://www.datanovia.com/apps/statfusion/analysis/inferential/mean-comparisons/one-sample/one-sample-t-test.html.