Chapter 3 R support

Base R for estimates and uncertainty

Use these short examples beside Chapter 3 and its lecture, or adapt them while working on an exercise, Computer Lab or homework. Computer Lab 3 uses one Special Practice comparison, a change from 95% to 90% confidence and a check against Welch’s procedure in the final classroom activity round. This page is a selectable reference menu, not a checklist: choose the example the assigned work needs. Choose one software route: R here, desktop R, or the lab’s SPSS instructions. The lab contains the activity and its worked answers; this page supplies reusable method support.

Every example on this site uses base R with the standard stats and graphics commands; no package is loaded or installed. Choose Run All to run a complete cell and Start Over to restore its starting code. Calculate at full precision and report statistical results rounded once to four decimal places, with trailing zeros dropped (3.1, not 3.1000); rounded inputs or different software rounding can change the last digits. For the buttons, the keyboard shortcuts and what to do when R reports an error, see how to run code.

Your task Useful starting point
New to R, or returning after a break Before you start
Normal areas, z scores and cutoffs Normal areas
Chapter 2: a Normal model against real grades Check a Normal model
The Special Practice interval from supplied values, or the same calculation for homework Question 6 Build an interval
One mean with a known or an estimated SD One-mean tests
Simple commands, such as those in homework 9’s printed code Numbers and a known SD
Computer Lab 3 Two groups, then one confidence change
Reading a t test printout Read the printout line by line
Core Exercise 4: the records re-assigned under no effect Re-assign the records
Core Exercise 2, item 3: the SE from three class means per format Supplied summary values
Two programmes with unequal spreads under both t tests Two programmes’ scores
What unequal spreads do to the two t tests Unequal spreads with unequal group sizes
Two measurements on the same students Paired scores, then plots that keep the pairs
A school-blocked design with twelve school differences Twelve school differences
Association between two columns Covariance and correlation
A selected data or display change Change the data or draw a base graph
Chapter 2: grades by higher-education intention Grades by intention
Core Exercise 7 and its doubled table The worked count table, then Exercise 7’s table
Section 6.4: a three-way table read within its layers Two-way tables within layers
Private course data or a complete supplied script Programming help and your workspace

Use the assigned task’s own data and settings. Each example includes its own setup and can start on its own; the cells and the workspace have separate R objects, so copy the whole cell when you move code. Fixed code and output remain readable if the browser R runtime cannot start.

Before you start

A command is one instruction for R, such as mean(score). An object is a named value R keeps for later, such as score. The assignment arrow <- creates one: score <- 60 stores 60 under the name score, and typing score on its own line prints it. c() combines several values into one vector, so c(50, 54, 48) holds three scores. $ takes one named column out of a table: dat$score is the score column of dat. A function is a command that does a job, written with its arguments inside the brackets. In t.test(special, usual, var.equal = TRUE) the first two arguments supply the data and var.equal = TRUE is a named setting; commas separate them, and text in quotes is a label rather than an object. Names must match exactly, so Mean is not mean. For the buttons, the keyboard shortcuts and what to do when R reports an error, see how to run code.

Normal areas and z scores

A z score reports how many SDs a value lies from its mean: z <- (score - mu) / sigma_known, where mu and sigma_known are the mean and SD of the Normal model. pnorm(z) returns the lower-tail area below z under the standard Normal curve, and pnorm(z, lower.tail = FALSE) returns the area above it. qnorm(p) reverses the question: it returns the cutoff whose lower-tail area is p. An area between two bounds is the difference of their cumulative probabilities, so pnorm(1) - pnorm(-1) gives the central area within one SD.

The z score is 2, with lower area .9772 and upper area .0228. The central areas are .6827, .9545 and .9973, and the central-95% bounds are -1.96 and 1.96. Changing score moves one value’s position; changing a confidence level changes a reference cutoff. Neither turns an individual SD into the SE of a mean.

Check a Normal model against real grades

Chapter 2 compares the 395 final mathematics grades of its secondary-school records with a Normal model of the same mean and SD. This cell repeats that check from the Student Performance extract, whose final_grade column holds the same grades in the same student order. grade_mean and grade_sd are the mean and SD of all 395 grades. The for loop runs the line inside its braces once for each j in k <- 1:3: mean(abs(grade - grade_mean) <= j * grade_sd) is the share of grades within j SDs of the mean, stored as entry j of observed, which numeric(3) sets up beforehand; 2 * pnorm(k) - 1 gives the share a Normal model predicts. pnorm(0.5, mean = grade_mean, sd = grade_sd) is the model’s probability at or below 0.5, so multiplying it by 395 gives model_zeros, the number of grades the model expects in its whole lower tail, which also contains negative values that no grade can take. The last lines compare the four summaries with and without the zero grades; grade[grade > 0] keeps the positive grades.

The observed shares within one, two and three SDs are .6987, .9013 and 1.0000, against the model’s .6827, .9545 and .9973. The shares near the centre are close; the tail is where the model fails. The records hold 38 zero grades, whereas the model expects 6.0136 in its whole lower tail at or below 0.5, of which only 2.6173 fall in the zero bar from -0.5 to 0.5. Without those 38 grades the mean rises from 10.4152 to 11.5238 and the SD falls from 4.5814 to 3.2278, while the median stays at 11 and the IQR moves from 6 to 5: the zeros pull the mean and inflate the SD far more than they move the resistant summaries. Read the tail rather than the third decimal of the shares, as the chapter does. Omitting the zeros is a diagnostic check, not a correction; the data register describes what the records can support.

Build an interval from supplied values

The estimate, SE and critical value below are the inputs of the book’s Special Practice interval (Chapter 3, Section 3.4). Use them to calculate its 95% interval; homework Question 6 applies the same calculation to its own supplied values. The critical value 1.98027 is the .975 quantile of the t distribution with 118 df, qt(.975, 118), rounded to five decimals: the reference of the equal-variance procedure that Chapter 3 uses for Special Practice. The book explains that procedure and how to interpret the interval.

<- stores a value under a name. c() joins values. Arithmetic works element by element, so c(-1, 1) subtracts and adds the margin to make the two endpoints. round(interval, 4) rounds each endpoint once to four decimal places, and format(..., digits = 15, scientific = FALSE, trim = TRUE) displays the rounded values without trailing zeros, with the same number of decimals for both endpoints. It returns text for reporting; the numeric object interval keeps its full precision for later calculations.

The interval is [0.8572, 5.3428] points. For a small programming experiment, change only estimate to 2.1 and rerun. Both endpoints move down by one point while the width stays the same. Restore estimate <- 3.1 before using the interval in a task.

One mean: known SD or estimated SD

The numbers below are the district example that Chapter 3 uses to introduce intervals and tests in Sections 3 and 4, before the Special Practice comparison: 25 students with sample mean 52 and SD 5, compared with a reference mean of 50. Section 5.1 names the two procedures, the one-sample z test and the one-sample t test.

These are two different assumptions about the SD, shown with the same numerical spread. A stipulated population SD of 5 gives a z calculation; a sample SD of 5 from 25 observations gives a t calculation with n - 1 degrees of freedom. pt() returns a t-distribution probability, so 2 * pt(-abs(t_value), df = n - 1) is a two-sided p-value. Choose the procedure from what is actually known about the SD. Do not switch to the other one because it crosses a significance threshold.

Both statistics are 2; the z p-value is .0455 and the t(24) p-value is .0569. These summaries do not reconstruct 25 unobserved raw scores. With an actual vector of scores, t.test(x, mu = 50) performs the same one-sample t test from the data themselves.

Numbers and a known SD

For a supplied vector of scores, length(), mean() and sd() return the count, average and sample SD. sqrt() takes a square root. Copy these commands into your workspace to try them:

scores <- c(12, 15, 17, 18, 20)
length(scores)
mean(scores)
sd(scores)
sigma_known <- 4
se <- sigma_known / sqrt(length(scores))
se

Here sigma_known is a separately stipulated population SD, while sd(scores) describes the entered sample. Homework 9 prints its own complete code, with its scores, known SD and critical value, and the output that code produces; read its interval from that output. Rerunning the printed code here or in desktop R is optional and gives the same result. Native R output can show a different number of digits; when you record a calculated result, round it once to four decimals and drop trailing zeros.

Sample size and the t reference

Hold the mean difference and the SD fixed and change only the sample size, so the SE and the degrees of freedom are the only moving parts. data.frame() places the results side by side in one small table. These are two stipulated summaries for two hypothetical samples; a larger real dataset would not keep exactly the same mean and SD, and a large sample must never be created by duplicating the same observations.

The SE falls from 1.5811 to 0.5000, the statistic rises from 1.2649 to 4.0000, and the reference distribution changes from t(9) to t(99). This comparison is about precision and the reference tail; it is not a rule for choosing a test at some fixed sample size.

Compare two groups

The Special Practice CSV has 120 fictional participants, 60 per assigned condition, with complete delayed assessment scores on a 0-40 scale. The data register gives the invented design and explains the CSV’s relationship to the book.

read.csv() reads the named file into a table. $ chooses a column; == matches a label, and [] selects the scores with that label. table() counts the groups and head(dat) shows the first six rows. Keep the original data in d0 and use dat as the working copy for every later calculation.

Each argument of the two t.test() calls records a decision from Chapter 3, so the calls write every one of them, including the settings that repeat R’s defaults. The order special, usual makes the comparison Special minus Usual (Section 1.2). var.equal = TRUE chooses the equal-variance model, the procedure Chapter 3 uses for Special Practice: it pools the two sample variances into one estimate of the common spread and uses a t reference with 60 + 60 - 2 = 118 df (Sections 2.2 and 5.3). var.equal = FALSE, R’s default, chooses Welch’s model, which keeps each group’s own variance and is the chapter’s last t procedure (Section 5.4). paired = FALSE, also a default, declares two independent groups of different students; paired = TRUE would analyse within-pair differences instead (Section 5.2). alternative = "two.sided", the default, counts departures from zero in both directions (Section 4.2), and conf.level = level sets the confidence level of the interval (Section 3.2). Each saved result contains its test and interval; $stderr selects its estimated SE and $conf.int its endpoints.

The difference is 3.1 points. The equal-variance procedure gives SE 1.1324, t(118) = 2.7376, p = .0071 and the 95% interval [0.8576, 5.3424]. Welch’s call reports the same SE and statistic, because with 60 students in each group the pooled SE and Welch’s SE coincide. Only its df differ: Welch estimates them from the two sample variances, giving 117.5118, just below 118 because the two SDs are close but not equal, and that shifts its p-value and endpoints slightly. The coincidence of the SEs comes from the equal group sizes and does not establish equal population variances. Chapter 3, Section 5.4, chooses between the two procedures from the design and subject knowledge, not from which p-value is smaller. The Computer Lab uses the equal-variance call with its confidence change below and compares Welch’s call as its final step.

The CSV’s group SDs, 5.9992 and 6.3992, are close to but not exactly the book’s 6.0 and 6.4, so these results can differ from the book’s in the last digits: from its summaries the book reports t(118) = 2.7372, p = .0072 and the interval [0.8572, 5.3428].

Read the printout line by line

Every t.test() result prints in the same fixed layout, so reading this one carefully makes the others familiar. This is the pooled output from the cell above, unchanged.

    Two Sample t-test

data:  special and usual
t = 2.7376, df = 118, p-value = 0.007148
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 0.8575514 5.3424486
sample estimates:
mean of x mean of y
     27.1      24.0
  • The first line names the procedure: Two Sample t-test is the equal-variance two-sample t test that var.equal = TRUE requested.
  • data: names the two vectors in the order you gave them, so the comparison is Special minus Usual.
  • On the fourth line, t = 2.7376 is the test statistic, df = 118 its degrees of freedom, 60 + 60 - 2, and p-value = 0.007148 the two-sided p-value, which is .0071 to four decimals.
  • The alternative hypothesis: line states what that p-value is calculated against: a true difference of zero, with both tails counted.
  • The two numbers under 95 percent confidence interval: are the interval endpoints, [0.8576, 5.3424] points. pooled$conf.int returns the same pair, and conf.level changes them.
  • Under sample estimates:, mean of x and mean of y are the two group means, 27.1 and 24.0. Their difference, 3.1 points, is the estimate the interval is built around.

The printout does not show the SE. pooled$stderr returns it: 1.1324 points. The interval is the estimate plus and minus the critical value for 118 df times that SE, which is how the interval and the test use the same quantities.

The welch printout has the same layout and changes four things: its first line reads Welch Two Sample t-test, its df are the fractional 117.51, its p-value is 0.007152 (.0072 to four decimals) and its interval is [0.8575, 5.3425] points. The statistic and the two group means are unchanged.

One confidence change

In the two-group example, level <- 0.95 sets the confidence level. Change only that line to level <- 0.90 and choose Run All. Read the new interval in pooled$conf.int; diff(pooled$conf.int) gives its width. Compare it with the original interval, then restore level <- 0.95. The same participants and scores supply the estimate, SE and two-sided p-value. A narrower interval at a lower confidence level reflects a different coverage requirement; it adds no observations or information.

Re-assign the records under no effect

Chapter 3, Section 4.4, also checks the p-value against the design itself: it states in two sentences what re-assigning the 120 records under no effect gives. This cell runs those re-assignments and draws them. Suppose no student’s score depends on format. Each of the 120 records would then keep its score whichever group it had been assigned to, so re-assigning the records at random to two groups of 60 repeats the design’s own randomization under that null.

sample(120, size = 60) chooses 60 of the row numbers 1 to 120 at random and labels those rows Special; -special_rows selects the other 60, which become Usual. numeric(10000) sets up a vector of 10,000 zeros, and the for loop runs the two lines inside its braces once for each b from 1 to 10,000: each pass makes one re-assignment and stores its Special-minus-Usual difference as entry b of reassigned. set.seed(12345) fixes the random choices, so every run gives the same count. hist() bins the differences in steps of one sixth of a point, and abline(v = c(-3.1, 3.1), lty = 2) marks the observed distance from zero in both directions. extreme marks the re-assignments at least 3.1 points from zero; the small tolerance 1e-9 counts a difference of exactly 3.1 even though stored decimals are rounded. The last line counts them and gives their share and the SD of all the re-assigned differences.

Of the 10,000 re-assignments, 77 give a difference at least 3.1 points from zero, 41 below and 36 above, a share of .0077; the SD of the re-assigned differences is 1.1671 points. The share estimates the p-value of this no-effect null on these records. It is close to, but need not equal, the equal-variance t test’s .0071 from the same records (.0072 from the chapter’s rounded summaries): the re-assignments take their reference from the random assignment itself, under the stronger null that no student’s score depends on format, while the t test takes its reference from a model for the average effect. Another seed moves the count by a few, the simulation’s own uncertainty, with a Monte Carlo SE of about .0009. This cell supports Exercise 4; it is not a Computer Lab task.

The row and display changes below are reference examples. Use the change specified in your task rather than completing every variation.

Use supplied summary values

The code below starts from the book’s Special Practice summaries: 60 students per format, with SDs 6.0 and 6.4. For core Exercise 2, item 3, replace them with the exercise’s class-level values: three classes per format, and the SDs of the three class means in place of the students’ SDs. ^2 squares each SD; division by its group count gives that group’s estimated mean variance. Add the two before taking the square root. The last two lines calculate the SE the way Chapter 3, Section 2.2, introduces it for the equal-variance model: sp is the pooled SD, the square root of the two variances averaged with weights equal to their df, n - 1, and sp * sqrt(1 / n1 + 1 / n0) is the pooled SE. Copy this short calculation into your workspace.

n1 <- 60
n0 <- 60
s1 <- 6.0
s0 <- 6.4
sqrt(s1^2 / n1 + s0^2 / n0)
sp <- sqrt(((n1 - 1) * s1^2 + (n0 - 1) * s0^2) / (n1 + n0 - 2))
sp * sqrt(1 / n1 + 1 / n0)

With 60 students in each group both routes give 1.1325 points, and the pooled SD is 6.2032. For an unbalanced reference case, change only one declared group count, such as n0 <- 40, and rerun: the direct sum gives 1.2744 and the pooled SE 1.2579. With unequal group sizes and unequal SDs the two forms differ: the direct sum is the SE of Welch’s test and the pooled SE that of the equal-variance test, and Chapter 3, Section 5.4, explains the choice. That is a reference step with stipulated summaries, not a new dataset.

Two programmes’ scores

Chapter 3, Section 5.4, compares two invented programmes from their summaries alone: 10 students from a small intensive-support programme, mean 29 and SD 8, and 40 from the usual programme, mean 24 and SD 4 points on the same 0-40 assessment. The cell below types one invented set of 50 scores with exactly these summaries, draws them and runs both t tests. Other sets with the same summaries give the same results, because both procedures use only the group sizes, means and SDs.

list(Usual = usual, Intensive = intensive) gathers the two named score vectors, and stripchart() draws each group as a row of dots; method = "stack" piles equal scores on top of one another so that every student stays visible, as in the section’s dot-plot figure. The two t.test() calls write their arguments in full, with the meanings of the two-group example: the order intensive, usual makes the comparison Intensive minus Usual, var.equal = TRUE pools the two variances and var.equal = FALSE keeps each group’s own, paired = FALSE declares different students in the two groups, and alternative and conf.level set the two-sided test and the 95% interval. The last three lines print the two SEs, Welch’s df and both intervals.

The means are 29 and 24 and the SDs 8 and 4. The equal-variance SE is 1.7678 points and Welch’s 2.6077, with Welch’s df 10.151. The equal-variance interval, [1.4457, 8.5543], excludes zero; Welch’s, [-0.7986, 10.7986], includes it. Typing pooled or welch on a line of its own prints the whole test: t(48) = 2.8284 with p = .0068, and Welch’s t = 1.9174 with p = .0837. These are the values of the chapter’s table for the two variance models. The pooled variance lies near the larger usual group’s, so applied to the intensive group’s mean it understates that mean’s variance; because the staff expected unequal spreads before seeing the scores, Welch’s procedure is the one this design calls for.

Unequal spreads with unequal group sizes

Chapter 3, Section 5.4, asks how much the variance model matters when a small group varies more than a large one, as in its invented comparison of 10 and 40 students with SDs 8 and 4, and answers with a simulation. This cell reruns it. Each simulated study draws two groups from Normal populations with equal means, so every rejection of a zero difference is a rejection of a true null.

set.seed(12345) fixes the random draws, so every run gives the same rates. B is the number of simulated studies. matrix(rnorm(...), nrow = n1) draws all the studies at once, one study per column of x1 and x0; colMeans() gives each study’s group mean and apply(x1, 2, var) each study’s sample variance. The pooled SE repeats the formula of Section 2.2 and Welch’s SE that of Section 5.4, and nu computes each study’s Welch–Satterthwaite df, which Section 5.4 describes in words. The last line gives the share of studies in which each two-sided test rejects at level .05. A run takes a few seconds.

R prints 0.18476 and 0.05070: the equal-variance test rejects the true null in .1848 of the 50,000 studies, and Welch’s test in .0507, the first row of the chapter’s simulation table. To reproduce the second row, change only the two SD lines, to s1 <- 4 and s0 <- 8, and rerun: 0.00546 and 0.05126. Restore them, then change only the two group sizes, to n1 <- 25 and n0 <- 25, for the third row, 0.05142 and 0.04934. When the smaller group varies more, the pooled SE is too small and the equal-variance test rejects far more often than .05; when the smaller group varies less it rejects too rarely; with equal group sizes it stays near .05. Welch’s test stays near .05 in all three designs. Another seed moves each rate by about .001 to .002, the simulation’s own uncertainty, while the contrast between .18 and .05 remains. This cell is a demonstration to rerun and vary, not a Computer Lab task.

Change the data and rerun

Replace only dat <- d0 in the two-group cell with the following line, then choose Run All to rerun the complete calculation:

dat <- d0[-(1:3), ]

1:3 means rows 1 through 3. A negative row index removes those rows; the empty place after the comma keeps all columns. Alternatively, use:

dat <- head(d0, -3)

head() with a negative count keeps all except the last three rows. The file begins with Special Practice and ends with Usual Practice, so the alternatives affect different groups. Keep both groups populated. Rerun the whole cell and compare group counts, means, the difference, SE and interval. With 57 records in one group and 60 in the other, pooled$stderr and welch$stderr no longer coincide exactly and the equal-variance df become 115. Restore dat <- d0 and rerun to recover the starting result. These file-order exclusions change the analysed records; they are programming experiments, not random sampling or a remedy for missingness.

For a task that reverses the comparison, reverse the inputs of both t.test() calls and the explicit subtraction in the line that prints difference. Multiplying both score vectors by the same positive number changes the measurement units. Use the complete script and settings supplied with that task.

Paired scores

These eight invented students were measured before and after one short practice session, on the same score scale at both times. They are not Special Practice records. Define the difference difference <- after - before before calculating anything, so the direction is after minus before.

The first t.test() call writes each of its decisions. paired = TRUE declares that each student supplies a pair, so t.test() analyses the eight within-student differences (Chapter 3, Section 5.2), and the order after, before keeps the after-minus-before direction. mu = 0 is the null mean change. alternative = "two.sided" and conf.level = 0.95 keep their meanings from the two-group example. The second call performs the same one-sample test on the differences themselves and returns the same statistic, df, p-value and interval. The line that stores unpaired_t repeats the analysis with paired = FALSE, the design error Section 5.2 demonstrates, and the last two lines compare the two SEs and print the unpaired interval.

The mean difference is 2 with SD 2 and SE 0.7071, giving t(7) = 2.8284, p = .0255 and the 95% interval [0.328, 3.672]. The paired SE uses the variance of the differences, which subtracts twice the covariance between the two measurements; the independent-samples formula omits that covariance because it describes a different design. Ignoring the pairing therefore makes the SE 2.7451 instead of 0.7071, with t(14) = 0.7286, p = .4783 and the interval [-3.8877, 7.8877], which includes zero. Because the pairing comes from how the scores were obtained, only the paired result describes their uncertainty, whichever p-value one might prefer. A before-and-after difference alone cannot separate the session from time trends, repeated testing or other concurrent changes.

Plots that keep the pairs

The figure in Chapter 3, Section 5.2, shows the eight students three ways: (a) the before and after scores as two separate sets, which is all an independent-groups analysis sees; (b) each student’s two scores joined; and (c) the eight differences. The two cells below draw panels (b) and (c), each with its own two score lines so that it runs on its own. The scatterplot of the same scores shows the pairs as points instead.

cbind() places the two measurement columns side by side, and t() transposes them into by_student, so each student becomes one column with two rows. matplot() then draws one line per student across the two measurement positions. type = "b" draws both points and lines, xaxt = "n" suppresses the default numeric axis, and axis() labels positions 1 and 2.

Each line joins the same student across measurements, as in panel (b). Most lines rise, one is flat and one falls, and they run nearly parallel: students who scored high before also scored high after. The paired test summarizes those eight within-student changes rather than two separate group averages.

The eight differences

after - before subtracts element by element, giving each student’s change. stripchart() draws the eight changes as dots on one axis, and method = "stack" piles equal values on top of one another, so the two students who each gained 2 points both stay visible. abline(v = 0, lty = 2) adds a dashed line at no change.

These are panel (c)’s dots: changes from -1 to 5 points, six above zero, one at zero and one below. The paired analysis works with exactly these eight values, mean 2 and SD 2, so the shared between-student variation that dominates panel (a) drops out of its SE.

Twelve school differences

Chapter 3, Section 2.3, describes an invented school-blocked design. Each of twelve schools contributed two classes of 20 students, and within every school one class was randomly assigned to explanatory feedback and the other to usual feedback, so 480 students took the assessment. The class is the unit of assignment, and each school supplies one explanatory-minus-usual difference of its two class means. Section 5.2 analyses the twelve differences with the paired t procedure.

The cell types the twelve differences. So that the paired call has two columns to pair, it also types a column of usual-class means, invented for this page, and adds the differences to give the explanatory-class means. Only the differences enter the paired analysis, so any other usual-class means would give the same result. t.test(..., paired = TRUE) has the meaning it has in the paired example, with its other arguments at their defaults: a null mean difference of zero, a two-sided test and 95% confidence. The last command compares the SE from the twelve school comparisons with the one that wrongly dividing by 480 students would give.

The twelve differences sum to 30, with mean 2.5 and SD 2.3932 points. The paired test gives t(11) = 3.6187, p = .004 and the 95% interval [0.9795, 4.0205] points. Its SE, 0.6908, comes from the twelve independent within-school comparisons. Dividing the same SD by the square root of 480, as if the 480 students rather than the twelve school comparisons were the independent units, would give 0.1092. That SE is too small because the calculation mixes levels: the SD describes class-mean differences, whereas the students were assigned in whole classes. The conclusion concerns the average within-school effect in these twelve participating schools; within-school assignment does not make them representative of a wider population.

Covariance and correlation

cov(x, y) reports how two measurements vary together in their original product units, so its size depends on the measurement scale. cor(x, y) divides by both SDs and is unit free, between -1 and 1. Multiplying x by 10 is a positive unit change: the covariance is multiplied by 10 while the Pearson correlation is unchanged. method = "spearman" correlates the ranks instead of the values and describes monotonic association, as introduced in Chapter 2.

The covariance 28.1429 becomes 281.4286 while Pearson’s r stays .9599; the Spearman rank correlation is .9222. These are the same eight paired scores used above. A correlation describes association in these records; it establishes no causal direction and is not a regression coefficient.

Base R graphs

Each graph below runs in its own cell with its own data-loading line, so you can run any one of them on its own. All of them use base graphics; no plotting package is needed. Read the axis labels and the statistic being displayed before interpreting a picture.

A histogram of all scores

hist() counts observations within score intervals. breaks = 10 is a suggestion for the number of intervals, main titles the plot and xlab names the horizontal axis. The bar heights are counts, not probabilities.

This histogram pools both practice groups, so a difference between them is not visible here.

A smoothed density curve

density() smooths the same scores into a continuous curve, and plot() draws it. The curve’s shape depends on the smoothing bandwidth reported in its output; it is an estimate, not the recorded data.

Boxplots by group

In score ~ group, ~ reads as “by”: one box per group label. A box shows the median and quartiles, the whiskers reach most of the remaining values, and separate points mark values far from the box.

The grouped boxplot makes the group structure visible that the pooled histogram hides. Its line is the median, not the mean estimated by the t comparison above.

Grades by higher-education intention

Chapter 2 compares final mathematics grades between the 20 students who did not report an intention to pursue higher education and the 375 who did. final_grade ~ higher draws one box per intention group, and horizontal = TRUE lays the boxes on their side. stripchart() with method = "jitter" and add = TRUE adds every student over the boxes, moved up or down by a small random amount so that equal grades stay visible, and abline(v = 9.5, lty = 2) marks the pass line between 9 and 10. tapply() applies one summary within each group; in dat$final_grade == 0 each comparison is TRUE or FALSE, so sum counts the zero grades. cbind() places the six group summaries side by side. The last line contrasts two ways of averaging the two group means: weighted by the group sizes in n, or with the two groups counted equally.

The 20 students without the intention have mean 6.8, SD 4.8297, median 8, IQR 10 and 6 zero grades; the 375 with it have mean 10.608, SD 4.4934, median 11, IQR 5 and 32 zero grades. The group sizes differ greatly, so the weighted and unweighted averages of the group means differ too: 10.4152 against 8.7040. The weighted average equals the mean of all 395 grades, because each group mean is multiplied by its group size before dividing by the total. Intention was reported, not assigned, so the difference describes these records rather than an effect of intending to study further.

A bar chart of two categories

These counts are one 50-person row of the count table below: the online participants who completed and who did not. barplot() draws one bar per named count, and ylab names the axis.

The same two categories as a pie

pie() shows the same two counts as shares of the 50 online participants. Bar lengths support more accurate comparison than pie angles, so prefer the bar chart when the exact sizes matter.

The denominator is the 50 online participants only. Do not draw the four cells of the two-by-two table as though they were four independent participation categories.

A scatterplot of two measurements

plot(x, y) draws one point per pair. abline(a = 0, b = 1, lty = 2) adds a dashed line with intercept 0 and slope 1, marking equal scores: points above it improved and points below it declined.

The points rise together, which is the association summarized by covariance and correlation; their distance from the dashed line is the within-student change tested in the paired example.

Run a supplied exercise script

The Computer Lab supplies the complete Special Practice script. Other exercises or their reference material supply any UCI or private HSB setup they use. If a script uses a bare CSV filename, open that CSV in the workspace first, or change its first file path to the public data/... path shown with the example. Paste all the setup and analysis into the workspace and run it together. Public CSVs listed on this page are already available there as data/...; on desktop R, keep that subfolder or change the path to your downloaded file. The code printed in homework 9 needs no CSV.

For variations in a supplied script, t.test(x, mu = ...) compares one mean with a stated value; paired = TRUE uses matched differences; var.equal = TRUE requests the equal-variance two-sample test, while leaving it out gives Welch’s test; and alternative states the test’s direction. Choose those settings from the study and the assigned question. pnorm() returns a Normal probability; qt() returns a t critical value. The chapter and task specify the model and any supplied critical value.

The worked count table

matrix(..., nrow = 2, byrow = TRUE) fills two rows in order, and dimnames names the rows and columns. Here the rows are the two participation formats and the columns are completion and its absence. prop.table(counts, 1) divides by each row’s total, so the two row proportions use the 50-person denominators. In chisq.test(counts, correct = FALSE), correct = FALSE requests the ordinary Pearson statistic that Chapter 3 calculates, X-squared 7.84 (Section 6); the default, correct = TRUE, applies a continuity correction to a two-by-two table and gives the different value 6.76 (p = .0093). $expected shows the counts predicted when format and completion are independent.

The completion proportions are .64 and .36, a difference of .28 in the Online-minus-In-person order. All four expected counts are 25, giving X-squared 7.84 with 1 df and p = .0051. These are invented records for an association question: participation format was not assigned, so the result supports no causal claim. Exercise 7 has its own table and counts; the cell below uses its numbers.

For a task using UCI data, run its complete CSV setup and coding. Its extract carries the final grades but no absences or second-period grades, so it can show the zero grades, as the Normal check does, but not the pattern behind them; the data register describes that limitation.

Exercise 7’s table and its doubled version

This cell enters the counts of Exercise 7’s 200 records in the same layout, then runs the same two commands on the table with every count doubled, the change that the exercise’s item 5 asks you to predict. 2 * exercise_7 multiplies each of the four counts by 2 and keeps the row and column names. correct = FALSE again requests the ordinary Pearson statistic that Chapter 3 calculates. Make your prediction before you run the cell.

The completion proportions, Online and In person, are .7000 and .7875 both times. Exercise 7’s table gives X-squared 1.8868 with 1 df and p = .1696; the doubled table gives 3.7736 and p = .0521, which R prints as 0.05207. Compare these results with your prediction and with the exercise’s question about reading X-squared or its p-value as the strength of an association.

Two-way tables within layers

Chapter 3, Section 6.4, arranges the same 100 invented learners by prior study, participation format and completion, a three-way table read as two-way tables within the two layers of prior study. The section describes the chi-square test within each layer without printing it; this cell runs those tests and the test of the added table, and Section 6.4 explains why the two layers’ p-values are not compared.

array() stores counts in more than two dimensions: dim = c(2, 2, 2) gives two formats by two completion outcomes by two layers. R fills the first dimension, then the second, then the layers, so the eight counts are the first layer’s completers, Online then In person, then its non-completers, then the same four counts for the second layer; change any of them and rerun to see every later line follow the new counts. dimnames names each dimension and its categories. ftable() prints the array as one flat table, with row.vars naming the variables that form its rows. Square brackets choose part of the array, one position per dimension, and a blank position keeps every category: layers[, , "Studied before"] is the first layer’s two-way table, and layers[, "Completed", ] holds the completers of each format in both layers. apply(layers, c(1, 3), sum) adds over the dimension it does not name, completion, giving each format’s total within each layer, so the division gives the completion proportions. apply(layers, c(1, 2), sum) adds over prior study instead and returns the two-way table of Section 6.1. Each chisq.test() call keeps correct = FALSE, the ordinary Pearson statistic of the worked count table.

The completion proportions, Online then In person, are .76 and .60 among the learners who had studied the subject before, and .52 and .12 among those new to it. For the two layers and the added table, X-squared is 1.4706, 9.1912 and 7.8400, each with 1 df, and p is .2253, .0024 and .0051; the added table repeats the result of the worked count table. One layer below .05 beside one above it does not show that the two layers’ associations differ; whether the format difference changes with prior study is a question about all three variables at once, which Section 6.4 leaves to a model (Chapter 11).

Open a private course CSV

In the workspace, choose Open a CSV and select the course-supplied hsb.csv. The file stays in the browser session. Opening it makes the file available; the first line of the supplied script then reads it:

hsb <- read.csv("hsb.csv")
names(hsb)

names() lists the columns. Keep the course’s numeric codes and run the task’s group/coding setup before the analysis. If the file has a different name, use that exact name in read.csv(). A saved R script does not contain the CSV records: reopen both files after reloading the page.

Function and software references

The R manuals give argument details for rounding, formatting results, t tests, CSV import, Normal probabilities, t critical values, random samples, for loops, random seeds, covariance and correlation, table proportions, Pearson tests, arrays, flat tables and apply(). For the graphs, see histograms, density estimates, boxplots, bar charts, pie charts, scatterplots, dot plots and matrix plots. Every command on this page belongs to base R, stats or graphics, so desktop R needs no installation step for them.

For the Computer Lab’s SPSS route: recoding, Crosstabs cells and independent-samples t tests. Use the task’s settings with either software route. The data register covers all public records and their limitations.

Your own code and the fixed output

Run a complete script, open a course CSV and save your work in the R workspace; the same page carries how to run code and the fixed code and output for this chapter, which stay readable when the browser runtime is unavailable.