A hypothesis test for a population mean when the population standard deviation, \(\sigma\), is unknown is conducted in the same way as if the population standard deviation is known. The only difference is, that the t-distribution is invoked, instead of the standard normal distribution (\(z\)-distribution).

For a test with the null hypothesis \(H_0:\; \mu = \mu_0\), the test statistic, \(t\), is calculated as

\[t = \frac{\bar x - \mu_0}{s/\sqrt{n}}\]

This hypothesis testing procedure is called one-mean t-test or simply t-test. Recall, that hypothesis tests follow a step wise procedure, which is summarized as follows:

\[ \begin{array}{l} \hline \ \text{Step 1} & \text{State the null hypothesis } H_0 \text{ and alternative hypothesis } H_A \text{.}\\ \ \text{Step 2} & \text{Decide on the significance level, } \alpha\text{.} \\ \ \text{Step 3} & \text{Compute the value of the test statistic.} \\ \ \text{Step 4a} & \text{Critical value approach: Determine the critical value.} \\ \ \text{Step 4b} &\text{P-value approach: Determine the p-value.} \\ \ \text{Step 5a} & \text{Critical value approach: If the value of the test statistic falls in the rejection region, reject} H_0 \text{; otherwise, do not reject} H_0 \text{.} \\ \ \text{Step 5b} & \text{P-value approach: If } p \le \alpha \text{, reject }H_0 \text{; otherwise, do not reject } H_0 \text{.} \\ \ \text{Step 6} &\text{Interpret the result of the hypothesis test.} \\ \hline \end{array} \]

Similar to the preceding section we showcase the critical value approach first and then, in a second step, we repeat the analysis using the p-value approach. However, this time we wrap the critical value approach up within a self-built function. For the p-value approach we will make use of the powerful machinery of R and apply the in-built function t.test().


Hypothesis testing: The critical value approach

Let us implement the critical value approach by building a function called simple.ttest(). The function takes as input arguments the sample data (a vector x), \(\mu_0\) (mu0), the population average to test against, the specified significance level \(\alpha\) (alpha) and the method of the t-test (left, right or the default value two-sided) with the function argument named method. The output of the function is a Boolean (TRUE or FALSE). If TRUE, \(H_0\) is rejected , if FALSE, \(H_0\) is not rejected.

simple.ttest <- function(x, mu0, alpha, method = "two-sided") {
  n <- length(x)
  xbar <- mean(x)
  s <- sd(x)

  # calculate test statistic
  tstat <- (xbar - mu0) / (s / sqrt(n))

  # calculate critical value
  df <- n - 1
  # for left-tailed test
  if (method == "left") {
    crit_val <- qt(p = 0.05, df = df, lower.tail = TRUE)
    # evaluate rejection region
    if (tstat < crit_val) {
      reject <- TRUE
    } else {
      reject <- FALSE
    }
  }
  # for right-tailed test
  else if (method == "right") {
    crit_val <- qt(p = alpha, df = df, lower.tail = FALSE)
    # evaluate rejection region
    if (tstat > crit_val) {
      reject <- TRUE
    } else {
      reject <- FALSE
    }
  }
  # for two-sided test (default)
  else {
    crit_val <- qt(p = alpha / 2, df = df, lower.tail = FALSE)
    # evaluate rejection region
    if (abs(tstat) > abs(crit_val) | -abs(tstat) < -abs(crit_val)) {
      reject <- TRUE
    } else {
      reject <- FALSE
    }
  }
  # print out summary and evaluation
  print(paste("Significance level:", alpha))
  print(paste("Degrees of freedom:", df))
  print(paste("Test statistic:", round(tstat, 4)))
  print(paste("Critical value:", round(crit_val, 4)))
  print(paste("Reject H0:", reject))
}

A great piece of code :-)


One-mean t-test: An example

Now it is time to test our simple.ttest() function. Therefore, we redo the example from the previous section. We use the students data set. You may download the students.csv file here. Import the data set and assign a proper name to it:

students <- read.csv("https://userpage.fu-berlin.de/soga/data/raw-data/students.csv")

The students data set consists of 8239 rows, each of them representing a particular student, and 16 columns, each of them corresponding to a variable/feature related to that particular student. These self-explaining variables are: stud.id, name, gender, age, height, weight, religion, nc.score, semester, major, minor, score1, score2, online.tutorial, graduated, salary.

We examine the average weight of a random sample of students from the students data set and compare it to the average weight of all European adults. Walpole et al. (2012) published data on the average body mass (kg) per region, including Europe. They report the average body mass for the European adult population to be 70.8 kg. We therefore set \(\mu_0\), the population mean, accordingly (\(\mu_0 = 70.8\)). Further, we take a random sample (x) with a sample size of \(n=9\). The sample consists of the weights in kg of 9 randomly picked students from the students data set.

mu0 <- 70.8
n <- 9
x <- sample(x = students$weight, size = n)

Hypothesis testing

Step 1: State the null hypothesis, \(H_0\), and alternative hypothesis, \(H_A\)

The null hypothesis states that the average weight of students equals the average weight of European adults as reported by Walpole et al. (2012). In other words, there is no difference between the mean weight of students and the mean weight of European adults.

\[H_0: \quad \mu = 70.8 \]

Recall, that the formulation of the alternative hypothesis dictates, whether we apply a two-sided, a left tailed or a right tailed hypothesis test.

Alternative hypothesis 1 \[H_{A_1}: \quad \mu \ne 70.8 \] results in a two-sided hypothesis test.

Alternative hypothesis 2 \[H_{A_2}: \quad \mu < 70.8 \] results in a left tailed hypothesis test.

Alternative hypothesis 3 \[H_{A_3}: \quad \mu > 70.8 \] results in a right tailed hypothesis test.


Step 2: Decide on the significance level, \(\alpha\)

\[\alpha = 0.05\]

alpha <- 0.05

Step 3, 4 and 5: Compute the value of the test statistic, determine the critical value and evaluate the value of the test statistic. If it falls in the rejection region, reject \(H_0\); otherwise, do not reject \(H_0\)

Now, our self-built function simple.ttest() comes into play. We feed to the function a random sample in form of a vector, a value for \(\mu_0\), a significance level \(\alpha\) and the method (two-sided, left or right). Recall, that two-sided is the default value. Thus, if we do not specify any method, the function will apply the two-sided hypothesis test:

simple.ttest(x, mu0, alpha)
## [1] "Significance level: 0.05"
## [1] "Degrees of freedom: 8"
## [1] "Test statistic: 1.5164"
## [1] "Critical value: 2.306"
## [1] "Reject H0: FALSE"

A right-tailed t-test:

simple.ttest(x, mu0, alpha, method = "right")
## [1] "Significance level: 0.05"
## [1] "Degrees of freedom: 8"
## [1] "Test statistic: 1.5164"
## [1] "Critical value: 1.8595"
## [1] "Reject H0: FALSE"

A left-tailed t-test:

simple.ttest(x, mu0, alpha, method = "left")
## [1] "Significance level: 0.05"
## [1] "Degrees of freedom: 8"
## [1] "Test statistic: 1.5164"
## [1] "Critical value: -1.8595"
## [1] "Reject H0: FALSE"

Step 6: Interpret the result of the hypothesis test

If the test statistic and thus the sample mean falls beyond the critical value, meaning into the rejection region, we conclude that at the 5 % significance level the data does provide sufficient evidence to reject \(H_0\). In contrast, if the test statistic and thus the sample mean falls in the non-rejection region, we conclude that the data does not provide evidence to reject \(H_0\).


Hypothesis testing in R: The p-value approach

The second approach is based on the assignment of a probability to the value of the test statistic. If the test statistic is very extreme, given the null hypothesis is true, a low probability will be assigned to the test statistic. In contrast, if the test statistic is not extreme at all, the probability assigned to it will be much higher. That probability is called p-value.

In order to calculate the exact p-value for a given numeric value we rely on software. The alternative “look-up-in-table-method” is somehow tedious as not all numerical values of the test statistic are given in such a table and this causes rounding errors. Luckily, the R programming language provides a very powerful machinery to perform t-tests. The generic function is called t.test(). You may go into more detail by typing help(t.test) into your console. Although the function provides various arguments, the particular t-test in which we are interested right now, testing one population mean when \(\sigma\) is unknown, is quite straight forward.

Let us redo the same problem as in the previous section, using the already defined variables. Recall, the sample is a vector of weights (x), \(\mu_0 = 70.8\) (given as mu0), \(n = 9\) (given as n) and \(\alpha= 0.05\) (given as alpha). Using this information we apply the t.test() function for a two-sided t-test by typing t.test(x = x, mu = mu0, alpha = alpha, alternative = 'two.sided') into the R console.

# set parameters
mu0 <- 70.8
alpha <- 0.05
n <- 9
x <- sample(x = students$weight, size = n)
# call t.test() function
t.test(x = x, mu = mu0, alpha = alpha, alternative = "two.sided")
## 
##  One Sample t-test
## 
## data:  x
## t = 0.33659, df = 8, p-value = 0.7451
## alternative hypothesis: true mean is not equal to 70.8
## 95 percent confidence interval:
##  64.94882 78.65118
## sample estimates:
## mean of x 
##      71.8

Wow! R conducts a hypothesis test in one line of code and gives additional information for free! Let us go through the output of the t.test() function:

It is worth noting, that the t.test() function creates an object, a list, containing the results of the t-test. Thus, if we store the results of the t.test() function in a variable, we can access the result by regular indexing.

out <- t.test(x = x, mu = mu0, alpha = alpha, alternative = "two.sided")
str(out)
## List of 10
##  $ statistic  : Named num 0.337
##   ..- attr(*, "names")= chr "t"
##  $ parameter  : Named num 8
##   ..- attr(*, "names")= chr "df"
##  $ p.value    : num 0.745
##  $ conf.int   : num [1:2] 64.9 78.7
##   ..- attr(*, "conf.level")= num 0.95
##  $ estimate   : Named num 71.8
##   ..- attr(*, "names")= chr "mean of x"
##  $ null.value : Named num 70.8
##   ..- attr(*, "names")= chr "mean"
##  $ stderr     : num 2.97
##  $ alternative: chr "two.sided"
##  $ method     : chr "One Sample t-test"
##  $ data.name  : chr "x"
##  - attr(*, "class")= chr "htest"

To retrieve, for example, the p-value we type:

out$p.value
## [1] 0.7450889

Citation

The E-Learning project SOGA-R was developed at the Department of Earth Sciences by Kai Hartmann, Joachim Krois and Annette Rudolph. You can reach us via mail by soga[at]zedat.fu-berlin.de.

Creative Commons License
You may use this project freely under the Creative Commons Attribution-ShareAlike 4.0 International License.

Please cite as follow: Hartmann, K., Krois, J., Rudolph, A. (2023): Statistics and Geodata Analysis using R (SOGA-R). Department of Earth Sciences, Freie Universitaet Berlin.