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.
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.
suppressPackageStartupMessages(library(dplyr)) suppressPackageStartupMessages(library(ggplot2)) suppressPackageStartupMessages(library(tibble)) suppressPackageStartupMessages(library(knitr))
set.seed(123) # for reproducibility
sim_data <- fmi::simulate_fmi_data( N_A = 50, N_B = 50, mean_shift = 1.0, seed = 456 )
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 secondsThe 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.5123.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|