Learn Linear Discriminant Analysis with R: A Step-by-Step Tutorial


Linear Discriminant Analysis (LDA) is a foundational statistical technique used extensively in machine learning for both supervised classification and effective dimensionality reduction. Its primary goal is to find linear combinations of features that best separate two or more classes of objects. Unlike Principal Component Analysis (PCA), which focuses on maximizing variance, LDA specifically seeks to maximize the distance between class means while minimizing the scatter within each class.

This comprehensive tutorial provides a rigorous, step-by-step walkthrough demonstrating the implementation of Linear Discriminant Analysis using the powerful R statistical computing environment. We will cover data preparation, model fitting, interpretation of the linear discriminants, and final visualization to ensure a deep understanding of this classification method.

Step 1: Load Essential Libraries

To successfully execute the Linear Discriminant Analysis, we must first ensure that the necessary statistical packages are installed and loaded into the current R session. The MASS package is absolutely critical for this task, as it contains the primary function, lda(), used for model fitting. Furthermore, we will load the ggplot2 package, which is the industry standard in R for generating high-quality, informative data visualizations later in the process.

Before proceeding, verify that both the MASS and ggplot2 libraries are available on your system. If they are not installed, you can typically install them using install.packages("MASS") and install.packages("ggplot2"). Once installed, execute the following R commands to activate them:

library(MASS)
library(ggplot2)

Step 2: Access and Examine the Dataset

For practical demonstration purposes, we will utilize the renowned built-in iris dataset in R. This dataset is a classic benchmark in classification tasks and consists of 150 observations of iris flowers, categorized into three distinct species (Setosa, Versicolor, and Virginica). The data provides four key physical measurements for each flower.

The following R commands load the data and provide a structural summary, allowing us to inspect the variable types and ensure data integrity before modeling:

# Attach the iris dataset to make variable names accessible
attach(iris)

# View the structure and variable types of the dataset
str(iris)

'data.frame':	150 obs. of  5 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 ...

The summary confirms that the dataset contains 150 observations across five variables. Our objective is to build an LDA model capable of predicting the flower’s species based on its four continuous measurements. In the context of our classification problem, the variables are clearly defined as follows:

The Predictor Variables (or features) used to inform the model are:

  • Sepal.Length
  • Sepal.Width
  • Petal.Length
  • Petal.Width

The Response Variable (or target class) is Species, which is a categorical variable with three distinct levels:

  • setosa
  • versicolor
  • virginica

Step 3: Standardize the Input Data

Standardizing the data is a crucial preprocessing step for Linear Discriminant Analysis. A core assumption of LDA is that the variance and covariance structure of the predictor variables must be equal across all classes (homoscedasticity). Even if this assumption is slightly violated, standardizing the data ensures that all features contribute equally to the distance calculations used by the discriminant functions, preventing variables with larger scales from dominating the model.

We standardize the data by scaling each continuous predictor variable so that it has a mean of 0 and a standard deviation of 1. The scale() function in R efficiently performs this Z-score normalization:

# Scale the first four columns (the predictor variables)
iris[1:4] <- scale(iris[1:4])

We can quickly confirm the success of the scaling operation by calculating the mean and standard deviation for the first four columns using the apply() function. The results below confirm that the means are approximately zero (due to minor floating-point errors) and the standard deviations are exactly 1, verifying the standardization required for optimal LDA performance.

# Calculate the mean of each scaled predictor variable
apply(iris[1:4], 2, mean)

 Sepal.Length   Sepal.Width  Petal.Length   Petal.Width 
-4.484318e-16  2.034094e-16 -2.895326e-17 -3.663049e-17 

# Calculate the standard deviation of each scaled predictor variable
apply(iris[1:4], 2, sd) 

Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
           1            1            1            1

Step 4: Divide Data into Training and Test Samples

A critical step in any robust machine learning workflow is splitting the dataset into training and testing subsets. The training set is used to fit the model and derive the discriminant functions, while the testing set is reserved to evaluate the model’s performance on unseen data, thereby assessing its generalization ability.

For this demonstration, we allocate 70% of the observations to the training set and the remaining 30% to the testing set. We utilize set.seed(1) to ensure that the random sampling process is fully reproducible, meaning the same subsets will be generated every time the code is run.

# Ensure the random sampling is reproducible
set.seed(1)

# Create a logical vector for sampling (70% TRUE, 30% FALSE)
sample <- sample(c(TRUE, FALSE), nrow(iris), replace=TRUE, prob=c(0.7,0.3))
train <- iris[sample, ]
test <- iris[!sample, ] 

Step 5: Fit the Linear Discriminant Analysis Model

With the data standardized and split, we are ready to fit the LDA model. We employ the lda() function from the MASS package. The formula Species~. is a shorthand instruction in R that tells the function to use all other variables in the dataset (the four physical measurements) to predict the Species variable, utilizing only the observations contained within our training data.

# Fit the LDA model using the training data
model <- lda(Species~., data=train)

# Display the detailed model output
model

Call:
lda(Species ~ ., data = train)

Prior probabilities of groups:
    setosa versicolor  virginica 
 0.3207547  0.3207547  0.3584906 

Group means:
           Sepal.Length Sepal.Width Petal.Length Petal.Width
setosa       -1.0397484   0.8131654   -1.2891006  -1.2570316
versicolor    0.1820921  -0.6038909    0.3403524   0.2208153
virginica     0.9582674  -0.1919146    1.0389776   1.1229172

Coefficients of linear discriminants:
                    LD1        LD2
Sepal.Length  0.7922820  0.5294210
Sepal.Width   0.5710586  0.7130743
Petal.Length -4.0762061 -2.7305131
Petal.Width  -2.0602181  2.6326229

Proportion of trace:
   LD1    LD2 
0.9921 0.0079 

The model output is highly informative and provides the necessary parameters for interpreting the discriminant functions:

  • Prior probabilities of group: These represent the proportion of observations in the training set belonging to each species. For example, 35.8% belong to the virginica species. In Bayesian classification, these prior probabilities are used to bias the final classification towards more frequently observed classes.
  • Group means: This table displays the mean value for each scaled predictor variable, calculated separately for each species. These group means define the exact centroid (center) of each class in the feature space.
  • Coefficients of linear discriminants: These are the weights assigned to the predictor variables that form the discriminant functions (LD1 and LD2). These functions are constructed to maximize the separation between the class means relative to the within-class variance. Note the large negative weight of Petal.Length on LD1, indicating its strong importance in separation.
  • Proportion of trace: This metric indicates the percentage of the total between-class separation explained by each linear discriminant function. Here, LD1 accounts for approximately 99.21% of the total discriminating power, confirming that it is by far the most significant dimension for separating the three species.

Step 6: Generate Predictions and Evaluate Accuracy

Once the LDA model has been fitted to the training data, we use the predict() function to generate classifications for the observations in our reserved, unseen test dataset. This step is crucial for evaluating the model’s accuracy and generalization capabilities.

# Use the LDA model to generate predictions on the test data
predicted <- predict(model, test)

names(predicted)

[1] "class"     "posterior" "x"   

The prediction output is returned as a list containing three vital components:

  • class: The final predicted categorical classification (e.g., setosa, versicolor, virginica) for each observation in the test set.
  • posterior: The posterior probability that an observation belongs to each of the possible classes, given the observed feature values.
  • x: The scores for the linear discriminants (LD1 and LD2) calculated for each observation in the test set.

Reviewing the results for the first six observations in the test set provides a tangible example of the model’s output:

# View the predicted class for the initial six observations
head(predicted$class)

[1] setosa setosa setosa setosa setosa setosa
Levels: setosa versicolor virginica

# View the posterior probabilities for the initial six observations
head(predicted$posterior)

   setosa   versicolor    virginica
4       1 2.425563e-17 1.341984e-35
6       1 1.400976e-21 4.482684e-40
7       1 3.345770e-19 1.511748e-37
15      1 6.389105e-31 7.361660e-53
17      1 1.193282e-25 2.238696e-45
18      1 6.445594e-22 4.894053e-41

# View the linear discriminant scores (LD1 and LD2) for the initial six observations
head(predicted$x)

         LD1        LD2
4   7.150360 -0.7177382
6   7.961538  1.4839408
7   7.504033  0.2731178
15 10.170378  1.9859027
17  8.885168  2.1026494
18  8.113443  0.7563902

Finally, we calculate the overall model accuracy by comparing the predicted class labels (`predicted$class`) against the actual species labels (`test$Species`) in the test set.

# Calculate the proportion of correctly classified observations
mean(predicted$class==test$Species)

[1] 1

The result of [1] 1 indicates that the model achieved a remarkable 100% accuracy, correctly predicting the species for every observation in the test dataset. While such perfect separation is uncommon in complex, real-world applications, the iris dataset is known for its highly separable classes, making it an excellent demonstration of the power of LDA when the assumptions of the model are largely met.

Step 7: Visualize the Linear Discriminants

The final and most intuitive step is visualizing the results using the calculated linear discriminant scores (LD1 and LD2). Since LD1 captured 99.21% of the variance, plotting the data points projected onto the LD1 and LD2 axes provides a definitive visual confirmation of the class separation achieved by the model.

We begin by combining the training data with the calculated discriminant scores and then use the ggplot2 package to generate a scatter plot, coloring the points based on their actual species affiliation. This visualization transforms the four-dimensional feature space into a highly separable two-dimensional plot.

# Combine the training data with the calculated discriminant scores
lda_plot <- cbind(train, predict(model)$x)

# Create a scatter plot using LD1 and LD2, colored by Species
ggplot(lda_plot, aes(LD1, LD2)) +
  geom_point(aes(color = Species))

Linear discriminant analysis in R

The resulting plot visually confirms the exceptional separation power of the LDA model. The three species groups form distinct, non-overlapping clusters based on their coordinates along the LD axes. This graphic validation directly corresponds to the high classification accuracy observed in the previous step, demonstrating how Linear Discriminant Analysis effectively projects multi-dimensional data onto a lower-dimensional space optimized for classification.

The complete R code used for this Linear Discriminant Analysis tutorial is available for review here.

Cite this article

Mohammed looti (2025). Learn Linear Discriminant Analysis with R: A Step-by-Step Tutorial. PSYCHOLOGICAL STATISTICS. Retrieved from https://statistics.arabpsychology.com/linear-discriminant-analysis-in-r-step-by-step/

Mohammed looti. "Learn Linear Discriminant Analysis with R: A Step-by-Step Tutorial." PSYCHOLOGICAL STATISTICS, 6 Nov. 2025, https://statistics.arabpsychology.com/linear-discriminant-analysis-in-r-step-by-step/.

Mohammed looti. "Learn Linear Discriminant Analysis with R: A Step-by-Step Tutorial." PSYCHOLOGICAL STATISTICS, 2025. https://statistics.arabpsychology.com/linear-discriminant-analysis-in-r-step-by-step/.

Mohammed looti (2025) 'Learn Linear Discriminant Analysis with R: A Step-by-Step Tutorial', PSYCHOLOGICAL STATISTICS. Available at: https://statistics.arabpsychology.com/linear-discriminant-analysis-in-r-step-by-step/.

[1] Mohammed looti, "Learn Linear Discriminant Analysis with R: A Step-by-Step Tutorial," PSYCHOLOGICAL STATISTICS, vol. X, no. Y, ص Z-Z, November, 2025.

Mohammed looti. Learn Linear Discriminant Analysis with R: A Step-by-Step Tutorial. PSYCHOLOGICAL STATISTICS. 2025;vol(issue):pages.

Download Post (.PDF)
Scroll to Top