Processing math: 100%

Lecture 3: Sampling and Confidence Intervals

Using R to generate random numbers, compute means and standard deviations, perform Student’s t-tests and calculate confidence intervals.

Published

April 2, 2020

DOI

This worksheet is based on the third lecture of the Statistical Modelling in Stata course, created by Dr Mark Lunt and offered by the Centre for Epidemiology Versus Arthritis at the University of Manchester. The original Stata exercises and solutions are here translated into their R equivalents.

Refer to the original slides and Stata worksheets here.

Generating random samples

In this part of the practical, you are going to repeatedly generate random samples of varying size from a population with known mean and standard deviation. You can then see for yourselves how changing the sample size affects the variability of the sample mean. Obviously, this is not something that you would ever do in data analysis, but since all of the frequentist statistics we cover in this course are based on the concept of repeated sampling, it is useful to see some repeated samples.

Start a new R session.

In RStudio: go to Session > New Session, or clear the current working environment with Session > Clear Workspace….

In the original RGui, use Misc > Remove all objects.

Or in any console just type rm(list = ls()).

You could also create a new R Markdown document.

Create a variable called n that is equal to 5.

n <- 5

Draw a sample vector, x, of length n from a standard normal distribution.

The standard normal is a normal distribution with mean 0 and standard deviation 1. The rnorm command draws random normal samples of a specified size:

x <- rnorm(n, mean = 0, sd = 1)

In fact, in R the default arguments for rnorm are mean = 0 and sd = 1, so we can implicitly draw from the standard normal distribution with the more concise syntax:

x <- rnorm(n)

Calculate the sample mean of the vector x.

mean(x)

Equivalently,

sum(x) / length(x)
sum(x) / n

Repeat steps 1–4 ten times and record the results as the first column of a data frame called sample_means.

You can save yourself from mashing the same command in multiple times, and in any case you should never enter data in manually if you can avoid it. You can repeat a non-deterministic function using the function replicate. So to draw the ten samples, we could write

samples <- replicate(10, rnorm(n))

This will produce all 10 samples for you with a single command. You can see the samples with

samples

You can now calculate the mean of each sample and save it in a new data frame as a column called n_5 with the command:

sample_means <- data.frame(n_5 = apply(samples, 2, mean))

The apply function tells R to apply the function mean to the columns (dimension 2, hence the 2) of the matrix samples.

Now generate a further ten samples, but each of size 25. Store the means of these samples.

n <- 25
samples <- replicate(10, rnorm(n))
sample_means$n_25 <- apply(samples, 2, mean)

Add a third column to your data frame corresponding to the means of ten vectors of sample size n = 100.

As before:

n <- 100
samples <- replicate(10, rnorm(n))
sample_means$n_100 <- apply(samples, 2, mean)

The resulting data frame might look something like:

sample_means

Now calculate the mean and standard deviation of the values in each column.

Everything is in a data frame, so finding summary statistics for each column is straightforward. For each column, we can call something like

mean(sample_means$n_5)
sd(sample_means$n_5)

But to save time typing that out three times, we can calculate for all the columns at once:

apply(sample_means, MARGIN = 2, FUN = mean)
apply(sample_means, MARGIN = 2, FUN = sd)

A shorthand for calculating the means is:

colMeans(sample_means)

and while there isn’t a function called colVars or colSDs, you can compute the variance–covariance matrix and read the numbers off the diagonal:

cov(sample_means)

# Just the diagonal entries
sqrt(diag(cov(sample_means)))

If you prefer dplyr:

library(dplyr)
summarise_all(sample_means, mean)
summarise_all(sample_means, sd)

And finally data.table:

library(data.table)
setDT(sample_means)
sample_means[, .(n = names(sample_means),
                 mean = sapply(.SD, mean),
                 sd = sapply(.SD, sd))]

Means

This section is bookwork and does not require R or Stata. See the Stata solution for the answers

If the standard deviation of the original distribution is σ, then the standard error of the sample mean is σ/n, where n is the sample size.

  1. If the standard deviation of measured heights is 9.31 cm, what will be the standard error of the mean in:
    1. a sample of size 49?
    2. a sample of size 100?
  2. Imagine we only had data on a sample of size 100, where the sample mean was 166.2 cm and the sample standard deviation was 10.1 cm.
    1. Calculate the standard error for this sample mean (using the sample standard deviation as an estimate of the population standard deviation).
    2. Calculate the interval ranging 1.96 standard errors either side of the sample mean.
  3. Imagine we only had data on a sample size of 36 where the sample mean height was 163.5 cm and the standard deviation was 10.5cm.
    1. Within what interval would you expect about 95% of heights from this population to lie (the reference range)?
    2. Calculate the 95% confidence interval for the sample mean.
  4. Figure ?? is a histogram of measured weight in a sample of 100 individuals.
    1. Would it be better to use the mean and standard deviation or the median and interquartile range to summarise these data?
    2. If the mean of the data is 69.69 kg with a standard deviation of 12.76 kg, calculate a 95% confidence interval for the mean.
    3. Why is it not sensible to calculate a 95% reference range for these data?

Proportions

This section is bookwork and does not require R or Stata. See the Stata solution for the answers

Again using our height and weight dataset of 412 individuals, 234 (56.8%) are women and 178 (43.2%) are men.

If we take a number of smaller samples from this population, the proportion of women will vary, although they will tend to be scattered around 57%. Figure ?? represents 50 samples, each of size 40.

  1. What would you expect to happen if the sample sizes were bigger, say n=100?

  2. In a sample of 40 individuals from a larger population, 25 are women. Calculate a 95% confidence interval for the proportion of women in the population.

Note. When sample sizes are small, the use of standard errors and the normal distribution does not work well for proportions. This is only really a problem if p (or (1p)) is less than 5/n (i.e. there are fewer than 5 subjects in one of the groups).

  1. From a random sample of 80 women who attend a general practice, 18 report a previous history of asthma.
    1. Estimate the proportion of women in this population with a previous history of asthma, along with a 95% confidence interval for this proportion.
    2. Is the use of the normal distribution valid in this instance?
  2. In a random sample of 150 Manchester adults it was found that 58 received or needed to receive treatment for defective vision. Estimate:
    1. the proportion of adults in Manchester who receive or need to receive treatment for defective vision,
    2. a 95% confidence interval for this proportion.

Confidence intervals in R

Download the bpwide.csv dataset.

Load the blood pressure data in its wide form into R with the command

bpwide <- read.csv('bpwide.csv')

This is fictional data concerning blood pressure before and after a particular intervention.

Use an appropriate visualisation to whether or not the variable bp_before is skewed. What do you think?

The original Stata solution calls for a histogram, for example:

with(bpwide, hist(bp_before, col = 'steelblue'))

However, a kernel density plot smooths out the histogram and may make it easier to visualise the distribution.

plot(density(bpwide$bp_before), main = 'Density plot')

There is a slight positive (right) skew.

Create a new variable to measure the change in blood pressure.

You can create a standalone variable:

bp_diff <- with(bpwide, bp_after - bp_before)

Or, since it is of the same length as bp_after and bp_before, you can store it as a new column in the bpwide data frame.

bpwide <- transform(bpwide, bp_diff = bp_after - bp_before)

What is the mean change in blood pressure?

mean(bp_diff)
mean(bpwide$bp_diff)

Compute a confidence interval for the change in blood pressure.

As we are looking at a difference in means, we compare the sample blood pressure difference to a Student’s t distribution with n1 degrees of freedom, where n is the number of patients.

By hand: the formula for a two-sided (1α) confidence interval for a sample of size n is Pr(ˉXntn1(α/2)Snn<μ<ˉX+tn1(α/2)Snn)=α

In R, the q family of functions returns quantiles of distributions (just as we have seen the r family draws random samples from those distributions). So qnorm finds the quantiles of the normal distribution, qt the quantiles of the Student’s t-distribution, and so on. We can use it to retrieve the 2.5% and 97.5% quantiles of the t distribution with n1 degrees of freedom and then plug them into the above formula, like so:

n <- length(bp_diff) # or nrow(bpwide)
mean(bp_diff) + qt(c(.025, .975), df = n - 1) * sd(bp_diff) / sqrt(n)

Or, using the built-in t.test function we get the same result:

with(bpwide, t.test(bp_before, bp_after, paired = TRUE))

As the 95% confidence interval does not contain zero, we may reject the hypothesis that, on average, there was no change in blood pressure.

Notice that the paired t.test did not require us to calculate bp_diff. But we can pass the same to t.test for a one-sample test and it will compute exactly the same test statistic, because testing whether two variables are equal is the same thing as testing whether their paired difference is zero.

t.test(bp_diff)

Look at the histogram of changes in blood pressure using the command hist.

hist(bp_diff, col = 'steelblue')

You could also try a kernel density plot, or even an old-fashioned stem and leaf diagram:

stem(bp_diff)

Does this confirm your answer to the previous question?

Create a new variable to indicate whether blood pressure went up or down in a given subject.

down <- with(bpwide, bp_after < bp_before)

Use the table command to see how many subjects showed a decrease in blood pressure.

table(down)

To get proportions, you can use prop.table, like so:

prop.table(table(down))

But it is just as quick to take the mean of the binary indicators (as TRUE == 1 and FALSE == 0):

mean(down)
mean(!down) # negation

Create a confidence interval for the proportion of subjects showing a decrease in blood pressure.

Note. The point of this is to create a confidence interval around a proportion. Making inference about dichotomised categorical variables is rarely a good idea, although clinically meaningful classifications can be very useful in describing your data. The problem with inference is that you lose power by discarding a lot of information, and you can reverse the effect: if most people show a slight decrease in blood pressure, but a few show a large increase, the mean change may be greater than 0 even though most subjects show a decrease.

Nonetheless, given that the purpose of the exercise is to create a binomial confidence interval, then we have:

binom.test(sum(down), n)