1A Kern County opener: did almond yield really change?¶
Kern County is one of the most productive farm counties in the United States, and almonds are one of its signature crops. Suppose a grower looks back at the last nine crop years and asks a simple question: “Is our typical almond yield really about one ton per acre, or is it drifting?”
We can put a number on the “typical” yield. Using the curated teaching dataset
kern_crops_sim, the mean almond yield per acre across the nine crop years
2015–2023 is 1.0359 tons per acre (dataset-derived from kern_crops_sim,
variable yield_per_acre; see the codebook data/codebooks/kern_crops_sim.md).
crops <- read.csv("data/processed/kern_crops_sim.csv")
almond <- subset(crops, commodity == "ALMONDS")
mean(~yield_per_acre, data = almond) # dataset-derived: 1.0359 tons/acreThat sample mean of 1.0359 is close to one ton per acre — but it is built from only nine years, and yields wobble from year to year with the weather. So the honest question is not “is 1.0359 exactly 1?” (it never will be), but: is the gap between 1.0359 and 1 large enough to be real, or is it the kind of wobble we would expect from nine noisy years even if the true long-run mean were exactly 1? That is a question about a population mean, and this chapter is the tool for it: the t-test family.
2Learning objectives¶
By the end of this chapter you will be able to:
(Apply) Conduct a one-sample -test and confidence interval for a mean, and state the conditions the -distribution requires.
(Apply) Conduct a paired -test by analyzing the differences, and explain when pairing is the right design.
(Apply) Conduct a two-sample -test and confidence interval for a difference of means.
(Analyze) Choose between a paired and a two-sample procedure for a given study design, and justify the choice.
(Evaluate) Assess the normality and independence conditions and judge how robust the procedures are when those conditions bend.
Durable skills this chapter builds: quantitative reasoning (QR) and critical thinking (CT).
Before this chapter you should be comfortable with confidence intervals (Chapter 7) and the logic of hypothesis testing — null/alternative hypotheses, p-values, and significance level (Chapter 8).
3Why a t and not a z? The intuition¶
3.1Intuition¶
In Chapter 6 you learned that a sample mean varies from sample to sample, and the size of that variation — the standard error — is the population standard deviation divided by . To turn into a test we standardize it: we measure how many standard errors sits away from the value we are testing.
There is one practical snag. The standard error needs the population standard deviation , and we almost never know it. We have to estimate it with the sample standard deviation . That substitution adds a second source of uncertainty: not only is noisy, but our ruler for measuring its noise is itself estimated from the same small sample.
The fix is to use a distribution that is a little more spread out than the normal — heavier in the tails — to honestly reflect that extra uncertainty. That distribution is Student’s . With few observations the is noticeably wider than the normal; as the sample grows, the estimate of settles down and the becomes indistinguishable from the normal. The amount of extra spread is controlled by one number, the degrees of freedom.
3.2Formula¶
The one-sample statistic is
where
= the sample mean (the average of your data),
(read “mu-naught”) = the hypothesized population mean — the value in the null hypothesis,
= the sample standard deviation (your estimate of the spread),
= the sample size (number of observations),
= the standard error of the mean, written ,
and we compare to a -distribution with degrees of freedom. (, “degrees of freedom,” is the single number that sets how heavy the tails are; here it is one less than the sample size.)
The two-sided p-value is the probability, if were true, of a at least as far from zero as the one we observed.
The confidence interval for inverts the same machinery:
where (read “t-star”) is the critical value: the point on the -distribution with degrees of freedom that puts the chosen confidence level (say 95%) in the middle.
3.3R¶
In R, the one-sample procedure is t.test(). With mosaic you can write it in the
formula form t.test(~variable, data = , mu = ), where mu is the value
you are testing against. Its output is compact — the statistic, degrees of freedom,
p-value, confidence interval, and sample mean — which we unpack line by line in
Worked Example 1:
# (Run for real in @ch10-sec-ex1 below; shown here only to introduce the function.)
t.test(~yield_per_acre, data = almond, mu = 1.0)4The one-sample t-test¶
4.1Intuition¶
Use a one-sample -test when you have one group of measurements and a specific number you want to compare its mean against — a standard, a target, a historical value, a claim. “Is the mean wait time under 15 minutes?” “Is the average fill weight 12 ounces?” “Is the typical almond yield one ton per acre?”
4.2Formula¶
Exactly Equation and Equation above. The hypotheses are
(or / for a one-sided test). Conditions for the procedure:
Independence — observations are independent (a random sample, or fewer than 10% of a finite population).
Normality / sample size — the data come from a roughly normal population, or the sample is large enough ( is the usual rule of thumb) for the Central Limit Theorem to make approximately normal. For small , check that the data are roughly symmetric with no extreme outliers.
4.3Worked Example 1 — almond yield vs. one ton per acre (Kern data)¶
Intuition. Back to the opener. We have nine yearly almond yields and one benchmark, ton per acre. One group, one number to compare against: a one-sample -test.
Formula. vs. , using Equation with .
Computation.
t.test(~yield_per_acre, data = almond, mu = 1.0)The pieces, recomputed and dataset-derived from kern_crops_sim:
tons/acre (R’s mean of x), , , so
and
R prints exactly this: t = 1.4082, df = 8, a two-sided p-value = 0.1967,
and a 95 percent confidence interval of 0.977 to 1.095 tons per acre.
Interpretation. Because , we fail to reject . There is not enough evidence to say the long-run mean almond yield differs from one ton per acre. The confidence interval tells the same story a richer way: one ton per acre sits comfortably inside (0.977, 1.095), so it remains a plausible value for the true mean. The 0.0359 gap we saw in the opener is exactly the kind of wobble nine noisy years can produce on their own.
4.4A picture of the data¶
A small sample earns a look before a test. Are the nine yields roughly symmetric with no wild outlier? Image shows they are.
xbar <- mean(~yield_per_acre, data = almond)
gf_histogram(~yield_per_acre, data = almond, bins = 6,
fill = okabe_ito[1], color = "white") %>%
gf_vline(xintercept = ~xbar, color = okabe_ito[4],
linetype = "dashed", linewidth = 0.8) %>%
gf_labs(
x = "Almond yield (tons per acre), 2015–2023 — SIMULATED",
y = "Number of crop years",
title = "Almond yield per acre is roughly symmetric (kern_crops_sim)",
subtitle = paste0("Dashed line = mean = ", signif(xbar, 4))
)
Nine almond yields cluster between about 0.9 and 1.2 tons per acre with no extreme outlier, so the t-procedure’s normality condition is reasonable for this small sample.
5The paired t-test¶
5.1Intuition¶
Sometimes two columns of numbers are naturally matched, one-to-one: a before-and-after measurement on the same person, the left and right hand of the same athlete, a 2015 and a 2023 value for the same crop. When the data come in matched pairs, the smart move is to subtract within each pair and analyze the single column of differences. Pairing strips away the variation between subjects (some crops are just bigger than others) so the variation within a pair (did this crop grow or shrink?) stands out on its own.
A paired -test is therefore not a new test at all — it is a one-sample -test run on the differences, testing whether their mean is zero.
5.2Formula¶
Let be the difference for pair . Then with
= the mean of the differences,
= the standard deviation of the differences,
= the number of pairs,
the statistic is
where is the hypothesized mean difference (almost always 0: “no change”). Everything else — p-value, confidence interval — is the one-sample machinery applied to the .
5.3Worked Example 2 — did Kern crop acreage shift from 2015 to 2023? (Kern data)¶
Intuition. For each of eight Kern commodities we have its harvested acres in 2015 and again in 2023. The two columns are matched by commodity — almonds to almonds, cotton to cotton — and they share the same unit (acres). That is a paired design. We ask: across these crops, did acreage change on average?
Formula. for each commodity; vs. , via Equation with .
Computation. First reshape the data so each commodity is one row with a 2015 column and a 2023 column (that plumbing is hidden below; the resulting paired table is shown):
Now the paired test. Subtract within each pair to get one column of differences, then run a one-sample -test on that column — that is the paired test:
paired_crops$diff <- paired_crops$acres_2023 - paired_crops$acres_2015
t.test(~diff, data = paired_crops, mu = 0)Dataset-derived from kern_crops_sim: the differences have mean
acres (R’s mean of x) and acres over
pairs, so . R prints t = 1.0495, df = 7, a
p-value = 0.3288, and a 95 percent confidence interval for the mean change
of (-10,266, 26,653) acres.
Interpretation. Some crops grew a lot (pistachios, almonds) and others shrank (cotton). On average, acreage rose by about 8,200 acres per crop. But with only eight crops and huge swings from one crop to the next, that average is swamped by noise. Since , we fail to reject : there is no average shift we can detect. The interval includes zero, which confirms that “no change” is still a plausible answer. Notice the payoff of the design: by pairing on commodity, we never had to pretend a carrot field and a cotton field were interchangeable — we only ever compared each crop to itself.
6The two-sample t-test¶
6.1Intuition¶
Now suppose the two groups are separate sets of subjects with no natural pairing: a treatment group and a control group, men and women, two different fields. We cannot subtract within pairs because there are no pairs. Instead we compare the two group means directly and ask whether their difference is bigger than sampling noise would explain.
6.2Formula¶
With sample means , sample standard deviations , and sample sizes , the Welch two-sample statistic (which does not assume the two groups have equal variance) is
where
= the two sample means,
= the two sample standard deviations,
= the two sample sizes,
= the hypothesized difference under (usually 0),
and the denominator is the standard error of the difference, .
The hypotheses are vs. . The
degrees of freedom for Welch’s come from a longer formula that R works out
for you, so you never have to compute it by hand; it is usually not a whole
number. R’s t.test() uses Welch by default because it is safer than assuming the
two groups have equal variance.
Going deeper (optional) — why we don’t pool the two variances
This box is enrichment; skip it without losing anything you need for the
problems. An older “pooled” two-sample -test combines and into a
single estimate of one shared variance, which buys you a clean whole-number
— but only if the two groups truly have equal spread. When
that assumption is wrong (different spreads, especially with unequal sample
sizes), the pooled test can quietly distort the error rate. Welch’s version
keeps the two variances separate, , and pays for
that honesty with a fractional, software-computed . Because we rarely know
the variances are equal, the safer default is to never pool — which is exactly
what R’s t.test() does. (You will meet pooling again in Section 6’s
ANOVA cousin in Chapter 12, where the within-group mean square is a pooled
variance — there it is justified by an explicit equal-variance condition.)
6.3Worked Example 3 — did the activity program raise daily steps? (randomized study)¶
Intuition. The fitness_tracker_sim dataset records a hypothetical
randomized study: 200 people were randomly assigned to a treatment group
(an 8-week activity-coaching program) or a control group, and their mean daily
steps were recorded. Random assignment makes the two groups independent and
comparable, so a two-sample -test is exactly right.
fit <- read.csv("data/processed/fitness_tracker_sim.csv")
# List treatment first so the reported difference reads treatment − control
# (otherwise R orders the groups alphabetically and would report control first).
fit$group <- factor(fit$group, levels = c("treatment", "control"))
tally(~group, data = fit) # 100 treatment, 100 controlFormula. vs. , via Equation.
Computation. Pass the numeric response and the grouping column:
t.test(steps ~ group, data = fit)Dataset-derived from fitness_tracker_sim: R lists mean in group treatment =
8112.9 steps/day and mean in group control = 7170.9, a difference of
942.0 steps/day, with , so
matching R’s t = 3.1169, df = 197.98, a p-value = 0.0021, and a
95 percent confidence interval for the difference of (346, 1538) steps per
day. (Because we put treatment first in the setup, R reports the difference as
treatment − control, so it comes out positive.)
Interpretation. Because , we reject : the program group walked significantly more. The confidence interval (346, 1538) excludes zero and is entirely positive, so we are 95% confident the program adds somewhere between about 350 and 1,500 daily steps on average — a difference that is both statistically detectable and, for a step count, practically meaningful.
Image shows the two groups side by side: the treatment box sits visibly higher, but with plenty of overlap — which is why we needed the test to confirm the shift is more than noise.
gf_boxplot(steps ~ group, data = fit, fill = ~group, width = 0.6) %>%
gf_refine(scale_fill_manual(
values = c(control = okabe_ito[1], treatment = okabe_ito[2]), guide = "none")) %>%
gf_labs(
x = "Group (randomized assignment)",
y = "Mean daily steps — SIMULATED",
title = "Daily steps by group (fitness_tracker_sim)"
)
Side-by-side boxplots of mean daily steps for the control and treatment groups in fitness_tracker_sim. The treatment median sits above the control median, with overlapping spreads — consistent with the significant but moderate difference the t-test found.
6.4Worked Example 4 — a “no difference” result, and reading it honestly (NHANES)¶
Intuition. Not every test ends in rejection, and a non-significant result is
information, not failure. The nhanes_subset dataset is a real CDC teaching
sample of body measurements. Adult female height in the United States is often
quoted as about 162 cm. Does this sample’s mean agree?
Formula. One group (adult women), one benchmark cm: vs. , via Equation.
Computation.
nhanes <- read.csv("data/processed/nhanes_subset.csv")
women <- subset(nhanes, sex == "female" & age >= 20)
t.test(~height_cm, data = women, mu = 162)Dataset-derived from nhanes_subset: R reports mean of x cm over
non-missing observations (the subset holds 3,683 rows, but t.test
drops the 25 with a missing height), t = 0.355, df = 3657, a
p-value = 0.7225, and a 95 percent confidence interval of
(161.81, 162.28) cm.
Interpretation. With , we fail to reject — the sample mean of 162.04 cm is fully consistent with a population mean of 162 cm. Notice two teaching points. First, the huge sample () makes the interval very narrow (less than half a centimeter wide), so this is a precise “no difference,” not a vague one. Second, “fail to reject” never means “we proved exactly”; it means 162 is among the many plausible values the data support.
7Choosing the right procedure¶
The hardest part of this chapter is not the arithmetic — R does that — it is matching the test to the design. Use this decision flow:
How many groups of measurements?
One group, compared to a fixed number one-sample (
t.test(~y, data = D, mu = )).Two groups go to step 2.
Are the two groups matched one-to-one?
Yes — each value in group 1 is paired with a specific value in group 2 (before/after, twins, same unit at two times) paired (
t.test(~diff, data = D)on the within-pair differences).No — the two groups are separate, independent sets of subjects two-sample (
t.test(y ~ g, data = D)).
For the full menu of every test in the course — not just the three in this chapter — see the book’s which-test decision guide in the appendix; it routes you from “what kind of data do I have?” to the right procedure.
Going deeper (optional) — a two-sample t-test is a two-group ANOVA
Enrichment, safe to skip. The two-sample -test you just learned is the same test you will meet in Chapter 12 as one-way ANOVA when there are exactly two groups. With groups, ANOVA’s -statistic is literally the square of the (equal-variance, pooled) two-sample : , and the two give identical p-values. ANOVA is the generalization to three or more groups, where running many pairwise -tests would inflate your false-alarm rate. So nothing in Chapter 12 replaces what you learned here — it extends it. (One caveat: ANOVA’s identity uses the pooled two-sample , not the Welch default; the two match closely when the group sizes and spreads are similar.)
8Try it¶
Shiny — Statistics Explorer, “One/Two-Sample Tests” module. Load
fitness_tracker_simorkern_crops_sim, pick a numeric variable and a group, and run the -test interactively. Every click shows the exact R code it ran, so the app and this chapter teach identical syntax. Launch the app fromshiny-explorer/(shiny::runApp("shiny-explorer")) and open the One/Two-Sample Tests tab.Jupyter lab —
labs/lab10-inference-for-means.ipynb. A guided, runnable notebook (R kernel) that walks through the one-sample and two-samplet.test()forms on these datasets, with starter-code exercises and a reflection prompt. On JupyterHub it loads the data by relative path; locally, open it from thelabs/folder.
9Practice problems¶
Problems marked [Kern] use a Kern-anchored dataset. Odd-numbered problems have short answers in the book’s Answers appendix; full worked solutions are in the instructor key. Unless stated otherwise, use and a two-sided alternative, and assume the conditions hold well enough to proceed.
[Kern] In your own words, explain why the almond-yield analysis in Section 4.3 used a one-sample -test rather than a two-sample test. What are the “one sample” and the “one number” here?
State the three members of the -test family covered in this chapter and, in one sentence each, the design that calls for each one.
A one-sample -test on observations gives , , and tests . Compute the standard error and the statistic by hand (show Equation plugged in). State the degrees of freedom.
Explain in plain language why the -distribution is wider than the normal distribution, and what happens to that extra width as grows.
[Kern] Using
kern_crops_sim, write the R call witht.test()that tests whether the mean pistachio yield per acre differs from 1.2 tons per acre. You do not need to run it — just write the correct one line of code.A paired study measures blood pressure before and after a treatment on the same 12 patients. Why is a paired -test more appropriate here than a two-sample -test? What does pairing “remove”?
For the steps analysis in Section 6.3, the 95% confidence interval for the difference was (346, 1538) steps. Interpret this interval in one sentence for a campus wellness coordinator who has never taken statistics.
True or false, and explain: “A non-significant result () proves the two means are equal.” Use Section 6.4 to ground your answer.
[Kern] A grower claims the mean table-grape yield in
kern_crops_simis 7 tons per acre. The nine yearly values have and . Compute the statistic for and state whether you would reject at (the critical value is about ).Decide paired vs. two-sample for each design, and say why: (a) left-hand vs. right-hand grip strength of 30 athletes; (b) test scores of 25 students taught online vs. 25 different students taught in person; (c) a city’s monthly rainfall this year vs. last year, month-matched.
[Kern] Using
fitness_tracker_sim, write thet.test()call that compares resting heart rate (resting_hr) between the treatment and control groups.A two-sample (Welch) -test reports . Explain why the degrees of freedom are not a whole number, and why we use Welch’s version by default.
Sketch (describe) what the side-by-side boxplots would look like for a two-sample comparison that is not significant. How does the overlap compare to Image?
A paired -test on pairs gives , . Test : compute , the statistic, and the .
[Kern] Explain why this chapter labels its
kern_crops_simnumbers as simulated and refuses to present them as facts about real Kern almond farming. What would change if a real USDA NASS key were registered?Explain the link between the test decision and the confidence interval: if a 95% CI for is , what is the result of the two-sided test of at ? Of ?
[Kern] Using
fitness_tracker_sim, the mean sleep (sleep_hours) was 7.11 (treatment) vs. 6.83 (control), with a two-sample , , . State the conclusion at , and explain why you should be cautious calling a result a big effect.A colleague runs a two-sample -test on data that are actually paired (before/after on the same people). Will ignoring the pairing tend to make the standard error too big or too small, and how could that change the conclusion?
[Kern] For the almond one-sample test in Section 4.3 (, , ), suppose a grower says “the p-value is above 0.05, so the mean yield is definitely exactly one ton per acre.” Correct this statement.
List the two conditions a one-sample -test requires, and for the nine-value almond sample, explain how Image helps you check the normality condition.
A study compares mean wait times at two clinics: clinic A (, min, ) and clinic B (, min, ). Set up and and compute the standard error of the difference using Equation’s denominator.
Why does a larger sample size make a confidence interval narrower? Tie your answer to the in the standard error.
[Kern] In Section 5.3 the paired test on crop acreage gave . Explain how it is possible that several individual crops changed acreage a lot, yet the average change was not statistically significant.
Give one real-world example from your own major or job of (a) a one-sample question, (b) a paired question, and (c) a two-sample question.
A one-sided one-sample test of vs. gives on . Without software, is the one-sided p-value larger or smaller than the two-sided p-value for the same ? By what factor?
10Chapter summary¶
Inference for a mean uses the -distribution, not the normal, because we estimate the standard deviation from the sample. The is wider in the tails (set by ) and converges to the normal as grows.
The one-sample -test (
t.test(~y, data = D, mu = )) compares one group’s mean to a fixed value via .The paired -test (
t.test(~diff, data = D)on the within-pair differences) is a one-sample test on the differences of matched pairs; use it when the two columns are matched one-to-one.The two-sample (Welch) -test (
t.test(y ~ g, data = D)) compares the means of two independent groups via Equation and does not assume equal variances.The design dictates the test. Decide paired vs. two-sample from how the data were collected, never from which p-value you prefer.
The decision and the confidence interval always agree, and the interval — reported in real units — is usually the more useful summary for a stakeholder.
11FAQ¶
Q1. When can I use a -test for a mean instead of a -test? In practice, almost never. A -test for a mean requires knowing the population standard deviation , which you essentially never do. Whenever you estimate the spread with the sample — i.e., always — use the -test. With large the two give nearly identical answers anyway, because the converges to the normal.
Q2. My sample is small (). Can I still use a -test? Yes, if the data look roughly symmetric with no extreme outliers (so the normality condition is believable). The almond example () is fine because Image shows a single rough mound. If a small sample is badly skewed or has outliers, the procedure is unreliable and you would turn to a randomization/bootstrap method (Chapters 7–8) instead.
Q3. What is the difference between the paired test and just running two
groups?
The paired test — t.test(~diff, data = D) on a column of within-pair differences
— needs the two columns to be the same length and matched row-by-row, and it
tests whether the mean difference is zero. The two-group form
t.test(y ~ g, data = D) treats the samples as independent. They answer different
questions and give different standard errors; pick based on the design
(Section 7).
Q4. Why is the Welch degrees-of-freedom not a whole number? Welch’s method adjusts the degrees of freedom to account for the two groups possibly having different spreads and sizes, and that adjustment usually lands on a fractional value rather than a whole number. R reports it exactly. You never compute it by hand — just read it from the output and trust it.
Q5. The confidence interval includes 0. What does that mean? For a difference of means, an interval that includes 0 means “no difference” is a plausible value — equivalent to failing to reject at the matching significance level. In Section 5.3 the acreage-change interval includes 0, so we could not conclude the average acreage changed.
Q6. Does a significant result () mean the effect is large or important? No. Significance means the effect is detectable given the sample size, not that it is big. Always read the confidence interval in real units to judge whether the effect matters practically. A tiny, useless difference can be “significant” with a large enough sample, and a meaningful difference can be non-significant in a small one.
Q7. Can I use these tests on the simulated Kern data and report the numbers as
real?
No — and this is a rule, not a preference. The _sim datasets are clearly labeled
synthetic; their numbers are for learning the method. The moment you have real
USDA NASS data (register a free key), rerun the identical code and report those
numbers instead. The code does not change; only the data behind it does.
12Glossary¶
New terms introduced in this chapter are collected in the book’s Glossary appendix. They include: Student’s -distribution, degrees of freedom, standard error of the mean, one-sample -test, paired -test, two-sample (Welch) -test, mean difference, and critical value .
13Resumen en español¶
Resumen del capítulo
En este capítulo aprendiste a comparar medias (means) usando la familia de pruebas t (t-tests). La idea central es simple: cuando quieres saber si la media (the mean) de una muestra es realmente diferente de un valor de referencia o de otra media, necesitas tener en cuenta dos fuentes de variación — la variabilidad natural de los datos y la incertidumbre de usar la desviación estándar muestral (sample standard deviation) en lugar de la desviación poblacional verdadera. Para eso existe la distribución t de Student (Student’s t-distribution), que es más ancha que la distribución normal y refleja esa incertidumbre adicional.
El capítulo cubre tres procedimientos. Primero, la prueba t de una muestra (one-sample t-test) te permite comparar la media de un grupo con un número específico. Por ejemplo, los rendimientos de almendra en el Condado de Kern — usando el conjunto de datos kern_crops_sim — se compararon con la referencia de una tonelada por acre. La estadística de prueba (test statistic) es
donde es el valor que se pone a prueba y es el error estándar (standard error). Con nueve años de datos obtuvimos y un valor p (p-value) de 0.197, lo que significa que no hubo evidencia suficiente para rechazar la hipótesis nula (null hypothesis).
Segundo, la prueba t pareada (paired t-test) se usa cuando cada observación del grupo 1 está emparejada con una observación específica del grupo 2 — como comparar el mismo cultivo en 2015 y en 2023. En ese caso simplemente se analizan las diferencias (differences) entre pares.
Tercero, la prueba t de dos muestras de Welch (two-sample Welch t-test) compara las medias de dos grupos independientes, como el grupo de tratamiento y el grupo de control (control group) en fitness_tracker_sim. Allí encontramos que el programa de actividad física aumentó los pasos diarios en aproximadamente 942 pasos, con un intervalo de confianza (confidence interval) del 95% de (346, 1538) pasos por día.
La regla más importante del capítulo: el diseño del estudio — no el valor p — es lo que determina qué prueba debes usar. En R, la función clave es t.test(): t.test(~y, data = D, mu = ) para una muestra, t.test(~diff, data = D) para la prueba pareada (sobre las diferencias), y t.test(y ~ g, data = D) para dos muestras.