How to one-sample t-test in R

statistics
one-sample t-test
The one-sample t-test is a statistical hypothesis test used to determine whether a sample mean significantly differs from a known population mean. Unlike the…
Published

February 21, 2026

Introduction

The one-sample t-test is a statistical hypothesis test used to determine whether a sample mean significantly differs from a known population mean. Unlike the z-test, which requires knowing the population standard deviation, the t-test uses the sample standard deviation, making it much more practical for real-world applications.

You would use a one-sample t-test when you have a single sample and want to compare its mean to a theoretical value, industry standard, or historical benchmark. Common scenarios include testing whether a new manufacturing process produces items with a target weight, checking if students’ test scores differ from a national average, or verifying if a medication achieves its intended effect.

The one-sample t-test requires several assumptions: the data should be approximately normally distributed (especially important for small samples), observations must be independent of each other, and the data should be measured at the interval or ratio level. The test is relatively robust to minor violations of normality, particularly with larger sample sizes (n > 30), thanks to the Central Limit Theorem.

The Math

The one-sample t-test uses this formula:

t = (sample_mean - population_mean) / (sample_standard_deviation / sqrt(sample_size))

Breaking this down: - t: the t-statistic we calculate - sample_mean: the average of your observed data - population_mean: the value you’re testing against (your null hypothesis) - sample_standard_deviation: the standard deviation of your sample - sample_size: the number of observations in your sample

The denominator (sample_standard_deviation / sqrt(sample_size)) is called the standard error of the mean. The t-statistic follows a t-distribution with (n-1) degrees of freedom, where n is your sample size.

R Implementation

Let’s start by loading the necessary packages and exploring our data:

library(tidyverse)
library(palmerpenguins)

# Look at the penguins dataset
glimpse(penguins)
Rows: 344
Columns: 8
$ species           <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adel…
$ island            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgerse…
$ bill_length_mm    <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1…
$ bill_depth_mm     <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1…
$ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 18…
$ body_mass_g       <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475,…
$ sex               <fct> male, female, female, NA, female, male, female, male…
$ year              <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 200…

The basic R function for a one-sample t-test is t.test():

# Basic syntax
# t.test(x, mu = population_mean, alternative = "two.sided")

Full Worked Example

Let’s test whether the mean body mass of penguins in our sample differs from 4000 grams. Perhaps 4000g represents a historical average or a value from literature.

Step 1: Prepare the data

# Remove missing values and examine the data
body_mass <- penguins$body_mass_g[!is.na(penguins$body_mass_g)]

# Basic descriptive statistics
summary(body_mass)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   2700    3550    4050    4202    4750    6300 

Step 2: State hypotheses - Null hypothesis (H₀): The mean body mass equals 4000g - Alternative hypothesis (H₁): The mean body mass does not equal 4000g

Step 3: Perform the t-test

# One-sample t-test
result <- t.test(body_mass, mu = 4000, alternative = "two.sided")
print(result)
    One Sample t-test

data:  body_mass
t = 4.7803, df = 341, p-value = 2.515e-06
alternative hypothesis: true mean is not equal to 4000
95 percent confidence interval:
 4118.203 4285.797
sample estimates:
mean of x 
     4202 

Step 4: Interpret the results

The t-statistic is 4.78 with 341 degrees of freedom. The p-value is 2.515e-06 (0.000002515), which is much smaller than the typical significance level of 0.05. This provides strong evidence against the null hypothesis.

We can conclude that the mean body mass of penguins in our sample (4202g) is significantly different from 4000g. The 95% confidence interval (4118g to 4286g) doesn’t include 4000g, confirming our conclusion.

Visualization

Let’s create a visualization to illustrate our findings:

library(ggplot2)

# Create a histogram with our test value marked
ggplot(data = data.frame(body_mass = body_mass), aes(x = body_mass)) +
  geom_histogram(aes(y = after_stat(density)), bins = 30,
                 fill = "skyblue", alpha = 0.7, color = "black") +
  geom_density(color = "blue", linewidth = 1) +
  geom_vline(xintercept = mean(body_mass), color = "red",
             linetype = "solid", linewidth = 1.2) +
  geom_vline(xintercept = 4000, color = "orange",
             linetype = "dashed", linewidth = 1.2) +
  labs(title = "Distribution of Penguin Body Mass",
       subtitle = "Red line: Sample mean (4202g), Orange line: Test value (4000g)",
       x = "Body Mass (g)",
       y = "Density") +
  theme_minimal()

Histogram in R with ggplot2 showing penguin body mass distribution with sample mean and test-value reference lines for a one-sample t-test

This plot shows the distribution of penguin body masses with both our sample mean (red solid line) and the test value of 4000g (orange dashed line). The clear separation between these lines visually confirms why our t-test found a significant difference.

Assumptions & Limitations

When NOT to use a one-sample t-test:

  1. Non-normal data with small samples: If your data is heavily skewed or has extreme outliers and n < 30, consider non-parametric alternatives like the Wilcoxon signed-rank test
  2. Dependent observations: If your data points aren’t independent (like repeated measures from the same subjects), use paired t-tests or mixed-effects models
  3. Multiple comparisons: Testing many hypotheses simultaneously inflates Type I error rates

What to do when assumptions are violated:

# Check normality with Shapiro-Wilk test (for small samples)
shapiro.test(body_mass[1:50])  # Only works for n ≤ 5000

# Visual normality check
ggplot(data.frame(body_mass), aes(sample = body_mass)) +
  geom_qq() + geom_qq_line() + theme_minimal()

# If normality is violated, use Wilcoxon test
wilcox.test(body_mass, mu = 4000, alternative = "two.sided")

Common Mistakes

1. Confusing one-sample with two-sample tests New users often use t.test(group1, group2) when they mean to test one group against a fixed value. Remember: one-sample compares to a number, two-sample compares two groups.

2. Ignoring the alternative hypothesis direction Always specify whether you expect the mean to be greater than, less than, or simply different from your test value:

# Two-sided (default): mean ≠ 4000
t.test(body_mass, mu = 4000, alternative = "two.sided")

# One-sided: mean > 4000
t.test(body_mass, mu = 4000, alternative = "greater")

3. Misinterpreting p-values A p-value of 0.03 doesn’t mean there’s a 3% chance your hypothesis is wrong. It means that if the null hypothesis were true, you’d see results this extreme or more extreme 3% of the time.