Simple Example of Riemannian STATS: Student DataSet - ACP-UMAP

Overview

This example shows how to use the riemannianStats package to explore a small student data set and reduce its dimensionality using Riemannian principal component analysis.

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

The objective is not to classify students or use predefined groups, but to explore the general structure of the data using Riemannian principal components based on UMAP similarities.

1. Load the package

The first step is to load the riemannianStats package.

library(riemannianStats)

2. Create the student data set

The data set contains one row per student. The first column identifies the student, while the remaining columns contain 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

3. Prepare the data for the analysis

The Riemannian PCA is applied only to numeric variables. Therefore, the student names are used as row names, while only the subject grades are kept in the data matrix used 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

4. Fit the Riemannian PCA model

The riem.pca() function performs the complete Riemannian PCA workflow internally. The Riemannian structure is constructed from a similarity matrix, which can be calculated using three methods:

  • "umap" constructs similarities from a neighborhood-based structure.
  • "isomap" uses a neighborhood graph and geodesic distances to represent the manifold structure.
  • "dbscan" constructs similarities using the density-based structure identified by DBSCAN.

In this example, the similarity matrix is calculated using UMAP. To use ISOMAP or DBSCAN, only the value of the method argument needs to be changed to "isomap" or "dbscan", respectively.

The argument scale.unit = FALSE indicates that the original variables are used without standardization. The model calculates five principal components through ncp = 5, while the first two axes are selected for visualization using axes = c(1, 2).

modelo <- riem.pca(
  data = data.analysis,
  scale.unit = FALSE,
  ncp = 5,
  axes = c(1, 2),
  n.neighbors = 3,
  min.dist = 0.1,
  metric = "euclidean",
  method = "umap"
)

modelo
#> Riemannian Principal Component Analysis
#> Similarity method: umap 
#> Number of observations: 10 
#> Number of variables: 5 
#> Number of retained dimensions: 2 
#> 
#> Eigenvalues:
#>        eigenvalue percentage of variance cumulative percentage of variance
#> comp 1     2.7839                55.6775                           55.6775
#> comp 2     2.0214                40.4275                           96.1050
#> comp 3     0.1020                 2.0391                           98.1441
#> comp 4     0.0882                 1.7632                           99.9073
#> comp 5     0.0046                 0.0927                          100.0000

For example, the other similarity methods can be selected as follows:

# ISOMAP similarities
method = "isomap"

# DBSCAN similarities
method = "dbscan"

Some arguments are method-specific. In particular, min.dist is used by UMAP and does not affect the ISOMAP or DBSCAN calculations.

5. Inspect the eigenvalues

The eigenvalues summarize the amount of inertia explained by each Riemannian principal component. The second and third columns show the percentage of explained inertia and the cumulative percentage, respectively.

round(modelo$eig, 4)
#>        eigenvalue percentage of variance cumulative percentage of variance
#> comp 1     2.7839                55.6775                           55.6775
#> comp 2     2.0214                40.4275                           96.1050
#> comp 3     0.1020                 2.0391                           98.1441
#> comp 4     0.0882                 1.7632                           99.9073
#> comp 5     0.0046                 0.0927                          100.0000

The inertia explained by the first two components is useful for interpreting the principal plane.

inertia <- sum(modelo$eig[c("comp 1", "comp 2"), "percentage of variance"])

inertia
#> [1] 96.10499

6. Inspect the UMAP similarity matrix

The similarity matrix describes the local relationships between students. Larger values indicate greater similarity between two students according to their grade profiles.

umap.similarities <- modelo$ind$similarities

round(umap.similarities, 3)
#>        Lucia Pedro  Ines  Luis Andres Ana Carlos  Jose Sonia Maria
#> Lucia  0.000 0.000 0.000 0.000  0.000   0  0.585 0.000 0.000 1.000
#> Pedro  0.000 0.000 0.828 0.000  0.000   1  0.000 0.585 0.000 0.000
#> Ines   0.000 0.828 0.000 0.000  0.000   1  0.000 0.000 0.000 0.000
#> Luis   0.000 0.000 0.000 0.000  0.585   0  0.000 0.000 1.000 0.000
#> Andres 0.000 0.000 0.000 0.585  0.000   0  1.000 0.000 0.585 0.585
#> Ana    0.000 1.000 1.000 0.000  0.000   0  0.000 1.000 0.000 0.000
#> Carlos 0.585 0.000 0.000 0.000  1.000   0  0.000 0.000 0.000 1.000
#> Jose   0.000 0.585 0.000 0.000  0.000   1  0.000 0.000 0.000 0.000
#> Sonia  0.000 0.000 0.000 1.000  0.585   0  0.000 0.000 0.000 0.000
#> Maria  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"

7. Inspect the Rho matrix

The Rho matrix is the Riemannian dissimilarity matrix derived from the similarity matrix. It is used to weight the differences between observations.

rho <- modelo$ind$rho

round(rho, 3)
#>        Lucia Pedro  Ines  Luis Andres Ana Carlos  Jose Sonia Maria
#> Lucia  1.000 1.000 1.000 1.000  1.000   1  0.415 1.000 1.000 0.000
#> Pedro  1.000 1.000 0.172 1.000  1.000   0  1.000 0.415 1.000 1.000
#> Ines   1.000 0.172 1.000 1.000  1.000   0  1.000 1.000 1.000 1.000
#> Luis   1.000 1.000 1.000 1.000  0.415   1  1.000 1.000 0.000 1.000
#> Andres 1.000 1.000 1.000 0.415  1.000   1  0.000 1.000 0.415 0.415
#> Ana    1.000 0.000 0.000 1.000  1.000   1  1.000 0.000 1.000 1.000
#> Carlos 0.415 1.000 1.000 1.000  0.000   1  1.000 1.000 1.000 0.000
#> Jose   1.000 0.415 1.000 1.000  1.000   0  1.000 1.000 1.000 1.000
#> Sonia  1.000 1.000 1.000 0.000  0.415   1  1.000 1.000 1.000 1.000
#> Maria  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"

8. Inspect the Riemannian differences

The Riemannian differences compare each student with the others while taking the local similarity structure into account.

riemannian.diff <- modelo$ind$differences

round(riemannian.diff, 3)
#> , , Mathematics
#> 
#>         Lucia  Pedro   Ines  Luis Andres  Ana Carlos   Jose Sonia  Maria
#> Lucia   0.000 -0.500 -0.600 2.000  1.000 -0.8  0.291 -0.900   1.0  0.000
#> Pedro   0.500  0.000 -0.017 2.500  1.500  0.0  1.200 -0.166   1.5  0.700
#> Ines    0.600  0.017  0.000 2.600  1.600  0.0  1.300 -0.300   1.6  0.800
#> Luis   -2.000 -2.500 -2.600 0.000 -0.415 -2.8 -1.300 -2.900   0.0 -1.800
#> Andres -1.000 -1.500 -1.600 0.415  0.000 -1.8  0.000 -1.900   0.0 -0.332
#> Ana     0.800  0.000  0.000 2.800  1.800  0.0  1.500  0.000   1.8  1.000
#> Carlos -0.291 -1.200 -1.300 1.300  0.000 -1.5  0.000 -1.600   0.3  0.000
#> Jose    0.900  0.166  0.300 2.900  1.900  0.0  1.600  0.000   1.9  1.100
#> Sonia  -1.000 -1.500 -1.600 0.000  0.000 -1.8 -0.300 -1.900   0.0 -0.800
#> Maria   0.000 -0.700 -0.800 1.800  0.332 -1.0  0.000 -1.100   0.8  0.000
#> 
#> , , Sciences
#> 
#>         Lucia  Pedro   Ines   Luis Andres  Ana Carlos   Jose Sonia  Maria
#> Lucia   0.000 -2.900 -2.700  0.000  0.500 -3.1  0.042 -3.200   0.5  0.000
#> Pedro   2.900  0.000  0.034  2.900  3.400  0.0  3.000 -0.125   3.4  2.200
#> Ines    2.700 -0.034  0.000  2.700  3.200  0.0  2.800 -0.500   3.2  2.000
#> Luis    0.000 -2.900 -2.700  0.000  0.208 -3.1  0.100 -3.200   0.0 -0.700
#> Andres -0.500 -3.400 -3.200 -0.208  0.000 -3.6  0.000 -3.700   0.0 -0.498
#> Ana     3.100  0.000  0.000  3.100  3.600  0.0  3.200  0.000   3.6  2.400
#> Carlos -0.042 -3.000 -2.800 -0.100  0.000 -3.2  0.000 -3.300   0.4  0.000
#> Jose    3.200  0.125  0.500  3.200  3.700  0.0  3.300  0.000   3.7  2.500
#> Sonia  -0.500 -3.400 -3.200  0.000  0.000 -3.6 -0.400 -3.700   0.0 -1.200
#> Maria   0.000 -2.200 -2.000  0.700  0.498 -2.4  0.000 -2.500   1.2  0.000
#> 
#> , , Spanish
#> 
#>         Lucia  Pedro   Ines Luis Andres  Ana Carlos   Jose Sonia  Maria
#> Lucia   0.000  1.900  1.200 2.70  1.400  1.5  0.415  1.700  2.70  0.000
#> Pedro  -1.900  0.000 -0.121 0.80 -0.500  0.0 -0.900 -0.083  0.80 -1.400
#> Ines   -1.200  0.121  0.000 1.50  0.200  0.0 -0.200  0.500  1.50 -0.700
#> Luis   -2.700 -0.800 -1.500 0.00 -0.540 -1.2 -1.700 -1.000  0.00 -2.200
#> Andres -1.400  0.500 -0.200 0.54  0.000  0.1  0.000  0.300  0.54 -0.374
#> Ana    -1.500  0.000  0.000 1.20 -0.100  0.0 -0.500  0.000  1.20 -1.000
#> Carlos -0.415  0.900  0.200 1.70  0.000  0.5  0.000  0.700  1.70  0.000
#> Jose   -1.700  0.083 -0.500 1.00 -0.300  0.0 -0.700  0.000  1.00 -1.200
#> Sonia  -2.700 -0.800 -1.500 0.00 -0.540 -1.2 -1.700 -1.000  0.00 -2.200
#> Maria   0.000  1.400  0.700 2.20  0.374  1.0  0.000  1.200  2.20  0.000
#> 
#> , , History
#> 
#>         Lucia  Pedro   Ines  Luis Andres  Ana Carlos   Jose Sonia  Maria
#> Lucia   0.000  1.600  0.600 1.600 -0.300  0.6 -0.166  0.600 3.100  0.000
#> Pedro  -1.600  0.000 -0.172 0.000 -1.900  0.0 -2.000 -0.415 1.500 -2.000
#> Ines   -0.600  0.172  0.000 1.000 -0.900  0.0 -1.000  0.000 2.500 -1.000
#> Luis   -1.600  0.000 -1.000 0.000 -0.789 -1.0 -2.000 -1.000 0.000 -2.000
#> Andres  0.300  1.900  0.900 0.789  0.000  0.9  0.000  0.900 1.411 -0.042
#> Ana    -0.600  0.000  0.000 1.000 -0.900  0.0 -1.000  0.000 2.500 -1.000
#> Carlos  0.166  2.000  1.000 2.000  0.000  1.0  0.000  1.000 3.500  0.000
#> Jose   -0.600  0.415  0.000 1.000 -0.900  0.0 -1.000  0.000 2.500 -1.000
#> Sonia  -3.100 -1.500 -2.500 0.000 -1.411 -2.5 -3.500 -2.500 0.000 -3.500
#> Maria   0.000  2.000  1.000 2.000  0.042  1.0  0.000  1.000 3.500  0.000
#> 
#> , , PhysicalEducation
#> 
#>         Lucia  Pedro   Ines   Luis Andres Ana Carlos  Jose  Sonia  Maria
#> Lucia   0.000  1.000  0.500 -1.000  0.700 1.5  0.332 2.000 -0.700  0.000
#> Pedro  -1.000  0.000 -0.086 -2.000 -0.300 0.0 -0.200 0.415 -1.700  0.000
#> Ines   -0.500  0.086  0.000 -1.500  0.200 0.0  0.300 1.500 -1.200  0.500
#> Luis    1.000  2.000  1.500  0.000  0.706 2.5  1.800 3.000  0.000  2.000
#> Andres -0.700  0.300 -0.200 -0.706  0.000 0.8  0.000 1.300 -0.581  0.125
#> Ana    -1.500  0.000  0.000 -2.500 -0.800 0.0 -0.700 0.000 -2.200 -0.500
#> Carlos -0.332  0.200 -0.300 -1.800  0.000 0.7  0.000 1.200 -1.500  0.000
#> Jose   -2.000 -0.415 -1.500 -3.000 -1.300 0.0 -1.200 0.000 -2.700 -1.000
#> Sonia   0.700  1.700  1.200  0.000  0.581 2.2  1.500 2.700  0.000  1.700
#> Maria   0.000  0.000 -0.500 -2.000 -0.125 0.5  0.000 1.000 -1.700  0.000

9. Inspect the Riemannian distance matrix

The Riemannian distance matrix is computed from the Riemannian differences. It summarizes the pairwise distances between students under the Riemannian structure.

distance.matrix <- modelo$ind$distance.matrix

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

10. Inspect the Riemannian covariance matrix

The Riemannian covariance matrix summarizes the joint variability between subjects after incorporating the Riemannian structure of the observations.

covariance.matrix <- modelo$var$cov

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

11. Inspect the Riemannian correlation matrix

The Riemannian correlation matrix expresses the relationships between variables on a common scale.

correlation.matrix <- modelo$var$cor

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

12. Inspect the individual coordinates

The individual coordinates represent the students in the reduced Riemannian principal component space.

individual.coordinates <- modelo$ind$coord

round(individual.coordinates, 3)
#>         Dim.1  Dim.2
#> Lucia   0.000  0.000
#> Pedro  -0.494 -2.358
#> Ines   -0.208 -1.741
#> Luis   -3.729  0.168
#> Andres -0.471  0.260
#> Ana     0.460 -2.246
#> Carlos  0.000  0.000
#> Jose    0.762 -2.474
#> Sonia  -3.554 -0.603
#> Maria   0.000  0.000

13. Inspect the variable coordinates

The variable coordinates are used to interpret the relationship between the original subjects and the Riemannian principal components.

variable.coordinates <- modelo$var$coord

round(variable.coordinates, 3)
#>                    Dim.1  Dim.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

14. Plot individuals using the object interface

The easiest way to plot the individuals is to pass the full riem.pca object to riem.plot() and set choix = "ind".

riem.plot(
  modelo,
  choix = "ind",
  title = "Student grades"
)

15. Plot variables using the object interface

The correlation circle is obtained by passing the full riem.pca object to riem.plot() and setting choix = "var".

riem.plot(
  modelo,
  choix = "var",
  title = "Student grades"
)

16. Create a Riemannian biplot using the object interface

The biplot combines the individual principal plane with the variable arrows in a single visualization.

riem.biplot(
  modelo,
  title = "Student grades"
)

17. Plot individuals using the manual interface

The package also allows plotting by manually passing the data, coordinates and explained inertia. This option is useful when the user has computed the components separately.

riem.plot(
  data = modelo$data,
  choix = "ind",
  components = modelo$ind$coord,
  explained.inertia = inertia,
  title = "Riemannian PCA"
)

18. Plot variables using the manual interface

The same manual approach can be used for the correlation circle by passing the variable coordinates directly.

riem.plot(
  data = modelo$data,
  choix = "var",
  correlations = modelo$var$coord,
  explained.inertia = inertia,
  title = "Riemannian PCA"
)

19. Summary

This example shows how to use riem.pca() as a complete workflow for Riemannian principal component analysis. The function stores the most important objects inside a single result, including similarities, the Rho matrix, Riemannian differences, distances, covariance and correlation matrices, individual coordinates, variable coordinates, and explained inertia.

The object can then be explored directly with components such as modelo$eig, modelo$ind$coord, and modelo$var$coord, or visualized with riem.plot() and riem.biplot().