# Complete Riemannian PCA Workflow with riem.pca

#### Oldemar Rodríguez Rojas & Jennifer Lobo Vásquez

# Overview

This example shows how to use the `riemannianStats` package to explore a small student data set and reduce its dimensionality.

The data set contains the grades of 10 students in 5 subjects: Mathematics, Sciences, Spanish, History, and Physical Education.

The objective of this example is not to classify students or use known groups, but to explore the general structure of the data using Riemannian principal components.

    library(riemannianStats)

# 1. Create the data set

The first column identifies each student, and the remaining columns contain their grades in five subjects.

    students <- data.frame(
      Student = c(
        "Lucia", "Pedro", "Ines", "Luis", "Andres",
        "Ana", "Carlos", "Jose", "Sonia", "Maria"
      ),
      Mathematics = c(7, 7.5, 7.6, 5, 6, 7.8, 6.3, 7.9, 6, 6.8),
      Sciences = c(6.5, 9.4, 9.2, 6.5, 6, 9.6, 6.4, 9.7, 6, 7.2),
      Spanish = c(9.2, 7.3, 8, 6.5, 7.8, 7.7, 8.2, 7.5, 6.5, 8.7),
      History = c(8.6, 7, 8, 7, 8.9, 8, 9, 8, 5.5, 9),
      PhysicalEducation = c(8, 7, 7.5, 9, 7.3, 6.5, 7.2, 6, 8.7, 7)
    )

    students
    #>    Student Mathematics Sciences Spanish History PhysicalEducation
    #> 1    Lucia         7.0      6.5     9.2     8.6               8.0
    #> 2    Pedro         7.5      9.4     7.3     7.0               7.0
    #> 3     Ines         7.6      9.2     8.0     8.0               7.5
    #> 4     Luis         5.0      6.5     6.5     7.0               9.0
    #> 5   Andres         6.0      6.0     7.8     8.9               7.3
    #> 6      Ana         7.8      9.6     7.7     8.0               6.5
    #> 7   Carlos         6.3      6.4     8.2     9.0               7.2
    #> 8     Jose         7.9      9.7     7.5     8.0               6.0
    #> 9    Sonia         6.0      6.0     6.5     5.5               8.7
    #> 10   Maria         6.8      7.2     8.7     9.0               7.0

# 2. Prepare the data for the analysis

The Riemannian analysis is applied only to numeric variables. Therefore, the `Student` column is used as the row names, while only the subject grades are kept for the analysis.

    data.analysis <- students[, c(
      "Mathematics",
      "Sciences",
      "Spanish",
      "History",
      "PhysicalEducation"
    )]

    student.names <- students$Student

    rownames(data.analysis) <- student.names

    data.analysis
    #>        Mathematics Sciences Spanish History PhysicalEducation
    #> Lucia          7.0      6.5     9.2     8.6               8.0
    #> Pedro          7.5      9.4     7.3     7.0               7.0
    #> Ines           7.6      9.2     8.0     8.0               7.5
    #> Luis           5.0      6.5     6.5     7.0               9.0
    #> Andres         6.0      6.0     7.8     8.9               7.3
    #> Ana            7.8      9.6     7.7     8.0               6.5
    #> Carlos         6.3      6.4     8.2     9.0               7.2
    #> Jose           7.9      9.7     7.5     8.0               6.0
    #> Sonia          6.0      6.0     6.5     5.5               8.7
    #> Maria          6.8      7.2     8.7     9.0               7.0

# 3. Compute UMAP similarities

First, a local similarity matrix between students is computed. This matrix summarizes which students are close to each other according to their grade patterns.

    n.neighbors <- 3

    umap.similarities <- riem.similarities.umap(
      data = data.analysis,
      n.neighbors = n.neighbors,
      min.dist = 0.1,
      metric = "euclidean"
    )

    round(umap.similarities, 3)
    #>        [,1]  [,2]  [,3]  [,4]  [,5] [,6]  [,7]  [,8]  [,9] [,10]
    #>  [1,] 0.000 0.000 0.000 0.000 0.000    0 0.585 0.000 0.000 1.000
    #>  [2,] 0.000 0.000 0.828 0.000 0.000    1 0.000 0.585 0.000 0.000
    #>  [3,] 0.000 0.828 0.000 0.000 0.000    1 0.000 0.000 0.000 0.000
    #>  [4,] 0.000 0.000 0.000 0.000 0.585    0 0.000 0.000 1.000 0.000
    #>  [5,] 0.000 0.000 0.000 0.585 0.000    0 1.000 0.000 0.585 0.585
    #>  [6,] 0.000 1.000 1.000 0.000 0.000    0 0.000 1.000 0.000 0.000
    #>  [7,] 0.585 0.000 0.000 0.000 1.000    0 0.000 0.000 0.000 1.000
    #>  [8,] 0.000 0.585 0.000 0.000 0.000    1 0.000 0.000 0.000 0.000
    #>  [9,] 0.000 0.000 0.000 1.000 0.585    0 0.000 0.000 0.000 0.000
    #> [10,] 1.000 0.000 0.000 0.000 0.585    0 1.000 0.000 0.000 0.000
    #> attr(,"n.neighbors")
    #> [1] 3
    #> attr(,"min.dist")
    #> [1] 0.1
    #> attr(,"metric")
    #> [1] "euclidean"
    #> attr(,"type")
    #> [1] "umap_fuzzy_graph_similarity"

# 4. Compute the Rho matrix

The Rho matrix is obtained as `1 - umap.similarities`. This matrix is used to weight the differences between students.

    rho <- riem.rho(umap.similarities)

    round(rho, 3)
    #>        [,1]  [,2]  [,3]  [,4]  [,5] [,6]  [,7]  [,8]  [,9] [,10]
    #>  [1,] 1.000 1.000 1.000 1.000 1.000    1 0.415 1.000 1.000 0.000
    #>  [2,] 1.000 1.000 0.172 1.000 1.000    0 1.000 0.415 1.000 1.000
    #>  [3,] 1.000 0.172 1.000 1.000 1.000    0 1.000 1.000 1.000 1.000
    #>  [4,] 1.000 1.000 1.000 1.000 0.415    1 1.000 1.000 0.000 1.000
    #>  [5,] 1.000 1.000 1.000 0.415 1.000    1 0.000 1.000 0.415 0.415
    #>  [6,] 1.000 0.000 0.000 1.000 1.000    1 1.000 0.000 1.000 1.000
    #>  [7,] 0.415 1.000 1.000 1.000 0.000    1 1.000 1.000 1.000 0.000
    #>  [8,] 1.000 0.415 1.000 1.000 1.000    0 1.000 1.000 1.000 1.000
    #>  [9,] 1.000 1.000 1.000 0.000 0.415    1 1.000 1.000 1.000 1.000
    #> [10,] 0.000 1.000 1.000 1.000 0.415    1 0.000 1.000 1.000 1.000
    #> attr(,"n.neighbors")
    #> [1] 3
    #> attr(,"min.dist")
    #> [1] 0.1
    #> attr(,"metric")
    #> [1] "euclidean"
    #> attr(,"type")
    #> [1] "umap_fuzzy_graph_similarity"

# 5. Compute Riemannian differences and distances

Riemannian differences compare each student with the others, weighting those differences by the local structure of the data.

    riemannian.diff <- riem.diff(data = data.analysis, rho = rho)

    dim(riemannian.diff)
    #> [1] 10 10  5

A Riemannian distance matrix is then computed from those differences.

    distance.matrix <- riem.dist(riemannian.diff)

    round(distance.matrix, 3)
    #>        Lucia Pedro  Ines  Luis Andres   Ana Carlos  Jose Sonia Maria
    #> Lucia  0.000 3.979 3.114 3.854  1.947 3.887  0.629 4.278 4.317 0.000
    #> Pedro  3.979 0.000 0.230 4.393  4.214 0.000  3.910 0.628 4.426 3.360
    #> Ines   3.114 0.230 0.000 4.422  3.700 0.000  3.265 1.685 4.769 2.526
    #> Luis   3.854 4.393 4.422 0.000  1.275 5.113  3.439 5.445 0.000 4.071
    #> Andres 1.947 4.214 3.700 1.275  0.000 4.202  0.000 4.460 1.619 0.718
    #> Ana    3.887 0.000 0.000 5.113  4.202 0.000  3.772 0.000 5.360 3.002
    #> Carlos 0.629 3.910 3.265 3.439  0.000 3.772  0.000 4.047 4.200 0.000
    #> Jose   4.278 0.628 1.685 5.445  4.460 0.000  4.047 0.000 5.643 3.302
    #> Sonia  4.317 4.426 4.769 0.000  1.619 5.360  4.200 5.643 0.000 4.697
    #> Maria  0.000 3.360 2.526 4.071  0.718 3.002  0.000 3.302 4.697 0.000

# 6. Compute Riemannian covariance and correlation

The Riemannian covariance matrix summarizes the joint variability of the subjects while considering Riemannian centering.

    covariance.matrix <- riem.cov(
      data = data.analysis,
      rho = rho,
      umap.distance.matrix = distance.matrix
    )

    round(covariance.matrix, 3)
    #>                   Mathematics Sciences Spanish History PhysicalEducation
    #> Mathematics             0.733    1.068   0.198   0.211            -0.620
    #> Sciences                1.068    2.303  -0.551  -0.568            -0.620
    #> Spanish                 0.198   -0.551   1.471   1.782            -0.684
    #> History                 0.211   -0.568   1.782   2.325            -0.896
    #> PhysicalEducation      -0.620   -0.620  -0.684  -0.896             0.841

The Riemannian correlation matrix makes it possible to compare the relationship between subjects on a common scale.

    correlation.matrix <- riem.cor(
      data = data.analysis,
      rho = rho,
      umap.distance.matrix = distance.matrix
    )

    round(correlation.matrix, 3)
    #>                   Mathematics Sciences Spanish History PhysicalEducation
    #> Mathematics             1.000    0.822   0.191   0.162            -0.790
    #> Sciences                0.822    1.000  -0.300  -0.245            -0.446
    #> Spanish                 0.191   -0.300   1.000   0.963            -0.615
    #> History                 0.162   -0.245   0.963   1.000            -0.641
    #> PhysicalEducation      -0.790   -0.446  -0.615  -0.641             1.000

# 7. Compute Riemannian principal components

Riemannian principal components make it possible to represent students in a lower-dimensional space.

    components <- riem.ind.coord(
      data = data.analysis,
      correlation.matrix = correlation.matrix,
      rho = rho,
      umap.distance.matrix = distance.matrix
    )

    round(components, 3)
    #>        Component.1 Component.2 Component.3 Component.4 Component.5
    #> Lucia        0.000       0.000       0.000       0.000       0.000
    #> Pedro       -0.494      -2.358       0.022      -0.024      -0.138
    #> Ines        -0.208      -1.741      -0.423      -0.642       0.082
    #> Luis        -3.729       0.168      -0.603       0.328      -0.014
    #> Andres      -0.471       0.260       0.042       0.255       0.126
    #> Ana          0.460      -2.246      -0.074       0.098       0.031
    #> Carlos       0.000       0.000       0.000       0.000       0.000
    #> Jose         0.762      -2.474       0.144       0.488       0.052
    #> Sonia       -3.554      -0.603       0.670      -0.220       0.028
    #> Maria        0.000       0.000       0.000       0.000       0.000

# 8. Compute explained inertia

Explained inertia indicates the proportion of total variability represented by the selected components.

    inertia <- riem.inertia(
      correlation.matrix = correlation.matrix,
      component1 = 1,
      component2 = 2
    ) * 100

    inertia
    #> [1] 96.10499

# 9. Interpret the subjects in the reduced space

The correlations between the subjects and the components help interpret which subjects are most associated with each axis of the principal plane.

    loadings <- riem.var.coord(
      data = data.analysis,
      components = components,
      rho = rho,
      umap.distance.matrix = distance.matrix
    )

    round(loadings, 3)
    #>                   Component.1 Component.2
    #> Mathematics             0.731      -0.647
    #> Sciences                0.336      -0.914
    #> Spanish                 0.771       0.618
    #> History                 0.784       0.598
    #> PhysicalEducation      -0.959       0.158

# 10. Visualize the principal plane

The principal plane represents the students using the first two Riemannian components. In this example, no clusters are used because the objective is to explore the structure of the data without predefined groups.

    riem.plot(
      data = data.analysis,
      choix = "ind",
      components = components,
      explained.inertia = inertia,
      title = "Student grades"
    )

# 11. Visualize the correlation circle

The correlation circle helps interpret how the subjects are related to the first two Riemannian components.

    riem.plot(
      data = data.analysis,
      choix = "var",
      correlations = loadings,
      explained.inertia = inertia,
      title = "Student grades"
    )

# 12. Visualize the biplot

The biplot combines the principal plane of the students with the variable arrows, allowing the structure of the observations and the interpretation of the subjects to be explored in the same figure.

    riem.biplot(
      data = data.analysis,
      components = components,
      correlations = loadings,
      explained.inertia = inertia,
      title = "Student grades"
    )

This example shows how to apply `riemannianStats` to a small and simple data set. The workflow computes Riemannian similarities, distances, correlations, and principal components in order to explore the structure of the grades and reduce the dimensionality of the data.