Introduction to the ‘fmi’ Package

  1. Introduction to the ‘fmi’ Package

The fmi package provides tools to test for Functional Measurement Invariance (FMI) between two groups of functional data.

It adapts the concepts of configural, metric, and scalar invariance from multi-group confirmatory factor analysis (MGCFA) to the context of Functional Data Analysis (FDA).

The package’s main function, run_fmi(), performs a hierarchical permutation test. If invariance is established, the compare_latent_means() function can be used to test for differences in the underlying FPC scores.

  1. FMI Test Procedure using Simulation

The package includes simulate_fmi_data() to generate data for demonstrating the FMI testing procedure.

2.1. Generate Simulation Data

First, let’s generate data where configural, metric, and scalar invariance hold, but a latent mean difference (mean_shift) exists between the two groups.

Load required packages for the vignette

suppressPackageStartupMessages(library(dplyr)) suppressPackageStartupMessages(library(ggplot2)) suppressPackageStartupMessages(library(tibble)) suppressPackageStartupMessages(library(knitr))

We explicitly reference fmi:: functions for clarity

set.seed(123) # for reproducibility

Apply a mean_shift of 1.0 to FPC 1 for Group B

sim_data <- fmi::simulate_fmi_data( N_A = 50, N_B = 50, mean_shift = 1.0, seed = 456 )

Check data structure

str(sim_data)

# Load required packages for the vignette
suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(ggplot2))
suppressPackageStartupMessages(library(tibble))
suppressPackageStartupMessages(library(knitr))

# We explicitly reference fmi:: functions for clarity
set.seed(123) # for reproducibility

# Apply a mean_shift of 1.0 to FPC 1 for Group B
sim_data <- fmi::simulate_fmi_data(
  N_A = 50, 
  N_B = 50, 
  mean_shift = 1.0, 
  seed = 456
)

# Check data structure
str(sim_data)
#> List of 3
#>  $ Y_mat    : num [1:100, 1:51] 1.02 2.199 0.548 1.596 -1.508 ...
#>  $ group_vec: Factor w/ 2 levels "A","B": 1 1 1 1 1 1 1 1 1 1 ...
#>  $ argvals  : num [1:51] 0 0.02 0.04 0.06 0.08 0.1 0.12 0.14 0.16 0.18 ...

The generated data includes Y_mat (100 subjects x 51 time points), a group_vec, and argvals.

2.2. Run the Hierarchical FMI Test

We use run_fmi() to perform the test. For CRAN checks, we use a very low n_perms = 9. For actual research, 499 or higher is recommended.

# Run the FMI test with n_perms=9 (to pass CRAN checks)
fmi_results <- fmi::run_fmi(
  Y_mat = sim_data$Y_mat,
  group_vec = sim_data$group_vec,
  argvals = sim_data$argvals,
  n_perms = 9, # Use 499+ for actual research
  progress = FALSE # Turn off progress bar for vignette
)
#> ====================================================
#>   Starting Functional Measurement Invariance (FMI) Tests
#>  - Number of FPCs (npc): 3
#>  - Number of Permutations: 9
#> ====================================================
#> --- Step 1: Configural Invariance ---
#> P-value: 0.3000. Invariance Established.
#> --- Step 2: Metric ---
#> P-value: 0.7000. Invariance Established.
#> --- Step 3: Scalar Invariance ---
#> P-value: 0.9000. Invariance Established.
#> ====================================================
#>         FMI Tests Complete (All Levels Passed)
#> ====================================================
#> 
#> >> All required levels of invariance established.
#> >> You may now proceed with compare_latent_means().
#> 
#> >> Total elapsed time: 5.72 seconds

The run_fmi() function tests each level of invariance sequentially.

2.3. Compare Latent Means

Since all levels of invariance (especially scalar) were established, we can now use compare_latent_means() to test for group differences in the FPC scores.

# Check if fmi_results$scalar$passed is TRUE
if (!is.null(fmi_results$scalar) && fmi_results$scalar$passed) {
  
  message("Scalar invariance established. Comparing latent means.")
  mean_diff_results <- fmi::compare_latent_means(
    fmi_results = fmi_results,
    group_vec = sim_data$group_vec
  )
  
  print(mean_diff_results)
  
} else {
  message("Scalar invariance rejected. Latent mean comparison aborted.")
}
#> Scalar invariance established. Comparing latent means.
#> Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if
#> `.name_repair` is omitted as of tibble 2.0.0.
#> ℹ Using compatibility `.name_repair`.
#> ℹ The deprecated feature was likely used in the fmi package.
#>   Please report the issue to the authors.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.
#> --- Latent Mean Comparison (Welch's t-tests) ---
#> 
#> 
#> |component | mean_group1| mean_group2| mean_diff| t_stat| p_value|
#> |:---------|-----------:|-----------:|---------:|------:|-------:|
#> |FPC1      |       0.415|      -0.446|     0.861|  2.136|   0.035|
#> |FPC2      |       0.007|      -0.023|     0.030|  0.154|   0.878|
#> |FPC3      |      -0.056|       0.032|    -0.088| -0.658|   0.512|
#> # A tibble: 3 × 6
#>   component mean_group1 mean_group2 mean_diff t_stat p_value
#>   <chr>           <dbl>       <dbl>     <dbl>  <dbl>   <dbl>
#> 1 FPC1          0.415       -0.446     0.861   2.14   0.0352
#> 2 FPC2          0.00659     -0.0231    0.0297  0.154  0.878 
#> 3 FPC3         -0.0560       0.0325   -0.0884 -0.658  0.512
  1. Real Data Examples

3.1. DTI Example (Patients vs Controls)

This example uses the DTI dataset from the refund package. We use n_perms = 9 for CRAN check speed.

# Load data (requires 'refund' package)
if (!requireNamespace("refund", quietly = TRUE)) {
  knitr::knit_exit() # Stop if refund is not installed
}
data(DTI, package = "refund")

# 1. Pre-process the data (first visit, remove NA, set groups)
dti_meta_data <- tibble::tibble(
  ID = DTI$ID,
  visit = DTI$visit,
  case = factor(DTI$case, levels = c(0, 1), labels = c("Control", "Patient"))
)

dti_cca_data <- tibble::as_tibble(DTI$cca) %>%
  setNames(paste0("cca_", 1:ncol(DTI$cca)))

dti_filtered_df <- dplyr::bind_cols(dti_meta_data, dti_cca_data) %>%
  dplyr::filter(visit == 1) %>%
  dplyr::select(ID, case, dplyr::starts_with("cca_")) %>%
  tidyr::drop_na() # Use tidyr::drop_na

# 2. Prepare inputs for FMI
Y_mat <- dti_filtered_df %>%
  dplyr::select(dplyr::starts_with("cca_")) %>%
  as.matrix()

group_vec <- dti_filtered_df$case
argvals <- seq(0, 1, length.out = ncol(Y_mat))

# 3. Run FMI test (n_perms=9 for CRAN)
fmi_dti_results <- fmi::run_fmi(
  Y_mat = Y_mat,
  group_vec = group_vec,
  argvals = argvals,
  n_perms = 9, # Use 499+ for actual research
  progress = FALSE
)
#> ====================================================
#>   Starting Functional Measurement Invariance (FMI) Tests
#>  - Number of FPCs (npc): 6
#>  - Number of Permutations: 9
#> ====================================================
#> --- Step 1: Configural Invariance ---
#> P-value: 0.2000. Invariance Established.
#> --- Step 2: Metric ---
#> P-value: 0.3000. Invariance Established.
#> --- Step 3: Scalar Invariance ---
#> P-value: 0.1000. Invariance Established.
#> ====================================================
#>         FMI Tests Complete (All Levels Passed)
#> ====================================================
#> 
#> >> All required levels of invariance established.
#> >> You may now proceed with compare_latent_means().
#> 
#> >> Total elapsed time: 12.46 seconds

# 4. Compare latent means (if invariance holds)
if (!is.null(fmi_dti_results$scalar) && fmi_dti_results$scalar$passed) {
  message("\n[DTI] Scalar invariance established. Comparing latent means.")
  fmi::compare_latent_means(fmi_dti_results, group_vec)
} else {
  message("\n[DTI] Scalar invariance rejected. Latent mean comparison aborted.")
}
#> 
#> [DTI] Scalar invariance established. Comparing latent means.
#> --- Latent Mean Comparison (Welch's t-tests) ---
#> 
#> 
#> |component | mean_group1| mean_group2| mean_diff| t_stat| p_value|
#> |:---------|-----------:|-----------:|---------:|------:|-------:|
#> |FPC1      |      -0.042|       0.018|    -0.059| -7.487|   0.000|
#> |FPC2      |      -0.002|       0.001|    -0.003| -0.949|   0.346|
#> |FPC3      |       0.004|      -0.002|     0.006|  1.990|   0.050|
#> |FPC4      |       0.002|      -0.001|     0.003|  1.143|   0.256|
#> |FPC5      |      -0.002|       0.001|    -0.002| -0.985|   0.328|
#> |FPC6      |       0.001|       0.000|     0.001|  0.525|   0.601|

3.2. Berkeley Growth Example (Boys vs Girls)

This example uses the growth dataset from the fda package. We will use the run_growth_example() helper function directly, passing n_perms = 9 for CRAN check speed.

# Check if 'fda' package is installed
if (!requireNamespace("fda", quietly = TRUE)) {
  message("Package 'fda' needed for the Growth example. Skipping.")
  knitr::knit_exit()
}

# Run the pre-packaged growth example
# (n_perms=9 for CRAN check speed)
fmi_growth_results <- fmi::run_growth_example(n_perms = 9)
#> 
#> 
#> ===== Real Data Example: Berkeley Growth (Boys vs Girls) =====
#> ====================================================
#>   Starting Functional Measurement Invariance (FMI) Tests
#>  - Number of FPCs (npc): 3
#>  - Number of Permutations: 9
#> ====================================================
#> --- Step 1: Configural Invariance ---
#>   |                                                                              |                                                                      |   0%  |                                                                              |========                                                              |  11%  |                                                                              |================                                                      |  22%  |                                                                              |=======================                                               |  33%  |                                                                              |===============================                                       |  44%  |                                                                              |=======================================                               |  56%  |                                                                              |===============================================                       |  67%  |                                                                              |======================================================                |  78%  |                                                                              |==============================================================        |  89%  |                                                                              |======================================================================| 100%
#> P-value: 0.9000. Invariance Established.
#> --- Step 2: Metric ---
#>   |                                                                              |                                                                      |   0%  |                                                                              |========                                                              |  11%  |                                                                              |================                                                      |  22%  |                                                                              |=======================                                               |  33%  |                                                                              |===============================                                       |  44%  |                                                                              |=======================================                               |  56%  |                                                                              |===============================================                       |  67%  |                                                                              |======================================================                |  78%  |                                                                              |==============================================================        |  89%  |                                                                              |======================================================================| 100%
#> P-value: 0.1000. Invariance Established.
#> --- Step 3: Scalar Invariance ---
#>   |                                                                              |                                                                      |   0%  |                                                                              |========                                                              |  11%  |                                                                              |================                                                      |  22%  |                                                                              |=======================                                               |  33%  |                                                                              |===============================                                       |  44%  |                                                                              |=======================================                               |  56%  |                                                                              |===============================================                       |  67%  |                                                                              |======================================================                |  78%  |                                                                              |==============================================================        |  89%  |                                                                              |======================================================================| 100%
#> P-value: 0.9000. Invariance Established.
#> ====================================================
#>         FMI Tests Complete (All Levels Passed)
#> ====================================================
#> 
#> >> All required levels of invariance established.
#> >> You may now proceed with compare_latent_means().
#> 
#> >> Total elapsed time: 3.92 seconds
#> --- Latent Mean Comparison (Welch's t-tests) ---
#> 
#> 
#> |component | mean_group1| mean_group2| mean_diff| t_stat| p_value|
#> |:---------|-----------:|-----------:|---------:|------:|-------:|
#> |FPC1      |     -10.442|       7.537|   -17.978| -3.861|   0.000|
#> |FPC2      |       8.803|      -6.344|    15.147| 12.625|   0.000|
#> |FPC3      |      -0.995|       0.786|    -1.782| -1.765|   0.083|