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

#### 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 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()`.