Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

1Objectives

By the end of this lesson you will be able to:

  1. Run and read a one-proportion and two-proportion prop.test().

  2. Run and read a one-sample t.test(~x, data=) and a two-sample t.test(y ~ g, data=).

  3. Run and read BSDA’s summary-statistics tests, zsum.test() and tsum.test(), when a problem gives you xˉ\bar{x}, ss, and nn instead of raw data.

  4. Extract and interpret the pieces of an htest object: the test statistic, degrees of freedom, p-value, confidence interval, and point estimate.

  5. Explain why prop.test()'s default confidence interval is not the same as the Wald interval taught by hand, and why that’s expected.

2From “how much does it vary” to “what can I conclude”

L09 showed you, by simulation, that a sample statistic bounces around from sample to sample — and that the size of that bounce has a formula, SE(xˉ)=σ/nSE(\bar{x}) = \sigma/\sqrt{n}. Confidence intervals and hypothesis tests are what you do with that fact: they turn “here’s one sample’s statistic, and here’s how much it typically varies” into “here’s a plausible range for the population value” (a CI) or “here’s how surprising this sample would be if a specific claim were true” (a test). Every function in this lesson returns the same kind of object — an htest — with the same handful of pieces, once you know where to look. Reload the toolkit and data if you’re starting fresh:

suppressMessages({library(mosaic); library(BSDA)})
survey <- read.csv("data/survey_sim.csv")
survey$class_year <- factor(survey$class_year,
                             levels = c("Freshman", "Sophomore", "Junior", "Senior"))

3One-proportion inference: prop.test()

The sample proportion p^=x/n\hat{p} = x/n estimates an unknown population proportion pp from xx successes in nn trials. L07 already counted pet for us: 55 of 120 surveyed students are “Dog person.” prop.test(x, n, p = ) tests whether the true rate could plausibly be some claimed value — say, H0:p=0.5H_0: p = 0.5 (a coin-flip split) — and returns a confidence interval for pp either way:

prop.test(x = 55, n = 120, p = 0.5)

	1-sample proportions test with continuity correction

data:  55 out of 120
X-squared = 0.675, df = 1, p-value = 0.4113
alternative hypothesis: true p is not equal to 0.5
95 percent confidence interval:
 0.3678863 0.5514842
sample estimates:
        p 
0.4583333 

Every htest object prints the same shape, and it’s worth being able to name each piece: the test name (a 1-sample proportions test), data: (what was tested), the test statistic (X-squared = 0.675, a chi-square statistic here — later lessons use t or z), df (degrees of freedom, 1), the p-value, a plain-language alternative hypothesis sentence, the confidence interval, and the sample estimate (p^=0.4583\hat{p} = 0.4583). Read it as: 45.8% of this sample are dog people; that’s not far enough from 50% to reject H0:p=0.5H_0: p = 0.5 (p-value 0.41, well above any usual 0.05 threshold), and the true rate is plausibly anywhere from about 37% to 55%.

Every one of those pieces is also a named piece of the returned object — useful the moment you need one number without the whole printout, e.g. to report just a p-value in a sentence:

pt1 <- prop.test(x = 55, n = 120, p = 0.5)
pt1$statistic
pt1$parameter
pt1$p.value
pt1$conf.int
pt1$estimate
X-squared 
    0.675 
df 
        1 
[1] 0.4113138
[1] 0.3678863 0.5514842
attr(,"conf.level")
[1] 0.95
        p 
0.4583333 

Two small, honest details worth noticing: $statistic and $parameter print with their names (X-squared, df) because they’re named numbers, not bare ones — and $conf.int prints an extra attr(,"conf.level") line, because R attaches the confidence level (0.95) as metadata on the interval itself, not just in the printout above. Neither is an error; that’s simply what a named/attributed R object looks like when auto-printed.

4Wald vs. Wilson: prop.test()'s CI is not the by-hand formula

Many intro textbooks (and calculators) teach a Wald confidence interval for a proportion by hand:

p^±zp^(1p^)n\hat{p} \pm z^\ast \sqrt{\frac{\hat{p}(1-\hat{p})}{n}}
phat <- 55/120
zst <- qnorm(0.975)
phat - zst * sqrt(phat * (1 - phat) / 120)
phat + zst * sqrt(phat * (1 - phat) / 120)
[1] 0.3691848
[1] 0.5474819

That gives (0.3692, 0.5475) — close to, but not identical to, prop.test()'s (0.3679, 0.5515) above. This is not a bug and not rounding error: R’s prop.test() does not compute the Wald interval at all. By default it computes a Wilson score interval with a continuity correction — a different (and generally more accurate, especially for small nn or p^\hat{p} near 0 or 1) method for the same job. Turning the continuity correction off gets you closer, but still not to a plain Wald interval, because the underlying method is still score-based, not Wald:

prop.test(x = 55, n = 120, p = 0.5, correct = FALSE)

	1-sample proportions test without continuity correction

data:  55 out of 120
X-squared = 0.83333, df = 1, p-value = 0.3613
alternative hypothesis: true p is not equal to 0.5
95 percent confidence interval:
 0.3718613 0.5473903
sample estimates:
        p 
0.4583333 

5Two-proportion inference: prop.test() with two counts

prop.test(x = c(x1, x2), n = c(n1, n2)) compares two proportions directly. L07’s two-way tally() already has the counts we need — the rate of “Cat person” among Freshmen vs. Sophomores:

tally(pet ~ class_year, data = survey)
            class_year
pet          Freshman Sophomore Junior Senior
  Cat person        9        14     11      6
  Dog person       16        11     16     12
  Neither           5        11      6      3

9 of 30 Freshmen and 14 of 36 Sophomores are cat people:

prop.test(x = c(9, 14), n = c(30, 36))

	2-sample test for equality of proportions with continuity correction

data:  c out of c9 out of 3014 out of 36
X-squared = 0.24525, df = 1, p-value = 0.6204
alternative hypothesis: two.sided
95 percent confidence interval:
 -0.3480262  0.1702485
sample estimates:
   prop 1    prop 2 
0.3000000 0.3888889 

6One-sample and two-sample t-tests: t.test()

t.test() is mosaic’s masked, formula-aware version from L05 — same function name as base R, now also accepting ~x, data= and y ~ g, data=. A one-sample test asks whether a population mean could plausibly equal some claimed value, e.g. H0:μ=7H_0: \mu = 7 hours of sleep:

t.test(~ sleep_hours, data = survey, mu = 7)

	One Sample t-test

data:  sleep_hours
t = -2.2997, df = 119, p-value = 0.02321
alternative hypothesis: true mean is not equal to 7
95 percent confidence interval:
 6.60453 6.97047
sample estimates:
mean of x 
   6.7875 

Same anatomy as prop.test(), different statistic: t = -2.30 (a t statistic instead of chi-square), df = 119 (n1n - 1), p-value 0.023 — below 0.05, so this sample’s mean (6.7875 hours) is far enough from 7 to reject H0:μ=7H_0: \mu = 7 at the usual 5% level, and the 95% CI (6.60, 6.97) does not contain 7, telling the same story.

A two-sample test compares two group means; the formula’s right side takes the grouping variable. Restricting to two pet levels (t.test() needs exactly two groups per call) compares sleep hours for cat people vs. dog people:

sub <- subset(survey, pet != "Neither")
t.test(sleep_hours ~ pet, data = sub)

	Welch Two Sample t-test

data:  sleep_hours by pet
t = -0.3511, df = 85.958, p-value = 0.7264
alternative hypothesis: true difference in means between group Cat person and group Dog person is not equal to 0
95 percent confidence interval:
 -0.5072222  0.3549495
sample estimates:
mean in group Cat person mean in group Dog person 
                6.687500                 6.763636 

A p-value of 0.73 and a CI comfortably straddling 0 both say the same thing: no real evidence that cat people and dog people sleep differently in this sample. Notice the title: “Welch Two Sample t-test” — R’s default does not assume the two groups share the same variance (df = 85.958 is a fractional, Welch-adjusted value, not the simpler n1+n22n_1+n_2-2 you may see in a hand-calculation). Add var.equal = TRUE if your course wants the classic pooled-variance version to match a by-hand formula exactly — the same “R’s smart default may not match the simplest hand formula” lesson as prop.test()'s Wald-vs-Wilson point above.

7Summary-statistics tests: zsum.test() and tsum.test() (BSDA)

L05 already ran zsum.test() once — a one-sample z-test of H0:μ=7H_0: \mu = 7 using only favstats()'s summary numbers, with a promise that reading htest output in full was coming later. This is that lesson. tsum.test() is zsum.test()'s tt-distribution sibling, for when σ\sigma (the population SD) is not known and you only have xˉ\bar{x}, ss, and nn — the everyday case for real data. Pull the exact group numbers from favstats():

favstats(sleep_hours ~ pet, data = sub)
         pet min    Q1 median   Q3 max     mean       sd  n missing
1 Cat person 4.2 6.075   6.65 7.20 9.1 6.687500 1.026867 40       0
2 Dog person 3.9 6.050   6.80 7.55 8.8 6.763636 1.066035 55       0

Feed those six numbers — no raw data, no data = argument at all — into tsum.test():

tsum.test(mean.x = 6.687500, s.x = 1.026867, n.x = 40,
          mean.y = 6.763636, s.y = 1.066035, n.y = 55)

	Welch Modified Two-Sample t-Test

data:  Summarized x and y
t = -0.3511, df = 85.958, p-value = 0.7264
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -0.5072222  0.3549495
sample estimates:
mean of x mean of y 
 6.687500  6.763636 
t, df, p-value, and the confidence interval all match `t.test(sleep_hours
pet, data = sub)above **exactly** — the raw-data version and the summary-statistics version are the same test, computed two different ways, just as [L05](./L05.md) showed for the one-sample z-test. Whenever a problem hands you $\bar{x}$, $s$, and $n$ instead of a data column,tsum.test()(two groups) orzsum.test()/one-sample tsum.test()` (one group, L05’s example) is the tool — never re-derive raw data that wasn’t given to you.

8Reading htest output: a quick reference

PieceWhere it appearsWhat it means
Test nameFirst printed lineWhich test ran — read it, don’t assume
data:Second lineWhat was tested (watch for the prop.test() two-vector quirk above)
StatisticX-squared, t, or zThe standardized distance between data and H0H_0
dfDegrees of freedomNot always n1n-1 — Welch’s t.test() uses a fractional value
p-valueP(data this extreme or more, if H0H_0 were true)
alternative hypothesisA full sentenceConfirms exactly what’s being tested — read it every time
confidence intervalA rangePlausible values for the population parameter
sample estimatesOne or two numbersThe actual p^\hat{p}, xˉ\bar{x}, or xˉ1,xˉ2\bar{x}_1, \bar{x}_2 observed

Every piece is also a named field on the saved object (result$statistic, result$p.value, result$conf.int, ...) — save any test to a variable when you need to pull out one number for a written answer, rather than re-reading the printout by eye.

9Summary