CIFA Urban Ipixuna: model fitting
Perform confirmatory item factor analysis (CIFA) using the results obtained from the exploratory item factor analysis (EIFA) with 3 latent dimensions.
Load required libraries and data
rm(list = ls())
library(day2day)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(mirt)
#> Loading required package: stats4
#> Loading required package: lattice
library(spifa)
library(sf)
#> Linking to GEOS 3.10.2, GDAL 3.4.1, PROJ 8.2.1; sf_use_s2() is TRUE
library(purrr)
library(ggplot2)
path_main <- git_path()
path_data <- file.path(path_main, "data")
path_raw <- file.path(path_data, "raw")
path_processed <- file.path(path_data, "processed")
path_modelled <- file.path(path_data, "modelled")
fidata <- file.path(path_processed, "fi-items-ipixuna-urban.gpkg") |>
st_read(as_tibble = TRUE)
#> Reading layer `fi-items-ipixuna-urban' from data source
#> `/home/rstudio/documents/projects/food-insecurity-mapping/data/processed/fi-items-ipixuna-urban.gpkg'
#> using driver `GPKG'
#> Simple feature collection with 200 features and 36 fields
#> Geometry type: POINT
#> Dimension: XY
#> Bounding box: xmin: -71.70038 ymin: -7.06058 xmax: -71.68109 ymax: -7.03724
#> Geodetic CRS: WGS 84
eifa_data <- readRDS(file.path(path_modelled, "eifa-ipixuna-urban.rds"))
Get items data and eifa model
# items
items_data <- fidata |>
st_set_geometry(NULL) |>
dplyr::select(matches("^item_[0-9]+_[A-D]"))
# discrimination parameters with rotation
eifa_discrimiation <- summary(eifa_data$model[[3]], rotate = "varimax", suppres = 0.5)
#>
#> Rotation: varimax
#>
#> Rotated factor loadings:
#>
#> F1 F2 F3 h2
#> item_1_A_worried_that_food_ends NA -0.639 NA 0.742
#> item_2_A_run_out_of_food NA NA -0.529 0.689
#> item_3_A_ate_few_food_types -0.761 NA NA 0.831
#> item_4_B_skipped_a_meal -0.764 NA -0.506 0.906
#> item_5_B_ate_less_than_required -0.630 NA -0.653 0.910
#> item_6_B_hungry_but_did_not_eat -0.683 NA -0.634 0.993
#> item_7_B_at_most_one_meal_per_day -0.736 NA NA 0.817
#> item_8_C_ate_few_food_types -0.836 NA NA 0.825
#> item_9_C_ate_less_than_required -0.858 NA NA 0.880
#> item_10_C_decreased_food_quantity -0.875 NA NA 0.926
#> item_11_C_skipped_a_meal -0.935 NA NA 0.985
#> item_12_C_hungry_but_did_not_eat -0.881 NA NA 0.949
#> item_13_C_at_most_one_meal_per_day -0.946 NA NA 0.994
#> item_14_D_food_just_with_farinha -0.568 NA -0.640 0.797
#> item_15_D_credit_for_eating NA -0.753 NA 0.668
#> item_16_D_borrowed_food NA -0.892 NA 0.990
#> item_17_D_had_meals_at_neighbors NA -0.552 NA 0.581
#> item_18_D_reduced_meat_or_fish -0.541 NA NA 0.634
#>
#> Rotated SS loadings: 8.571 3.31 3.235
#>
#> Factor correlations:
#>
#> F1 F2 F3
#> F1 1 0 0
#> F2 0 1 0
#> F3 0 0 1
Execute confirmatory item factor analysis
# general dimensions
q <- ncol(items_data) # number of items
m <- 3 # number of latent factors
# restrictions on the discrimination parameters A for confirmatory analysis
L_a <- (abs(eifa_discrimiation$rotF) > 0.5) * 1
# prior for the discrimination parameters A
A_mean <- matrix(0, q, m)
A_mean[c(11, 13), 1] <- 1
A_mean[c(16), 2] <- 1
A_mean[c(14), 3] <- 1
A_sd <- matrix(1, q, m)
A_sd[A_mean == 1] <- 0.45
# execute model
iter <- 1.5 * 10 ^ 6
thin <- 200
samples <- spifa(
response = item_1_A_worried_that_food_ends:item_18_D_reduced_meat_or_fish,
data = items_data, nfactors = 3, niter = iter, thin = thin,
constrains = list(A = L_a),
A_opt = list(initial = A_mean, prior_mean = A_mean, prior_sd = A_sd)
)
Print the settings defined for the model.
attr(samples, "model_info")[-c(1:4)]
#> $nobs
#> [1] 200
#>
#> $nitems
#> [1] 18
#>
#> $nfactors
#> [1] 3
#>
#> $ngp
#> [1] 3
#>
#> $niter
#> [1] 1500000
#>
#> $thin
#> [1] 200
#>
#> $standardize
#> [1] TRUE
#>
#> $constrain_L
#> F1 F2 F3
#> item_1_A_worried_that_food_ends 0 1 0
#> item_2_A_run_out_of_food 0 0 1
#> item_3_A_ate_few_food_types 1 0 0
#> item_4_B_skipped_a_meal 1 0 1
#> item_5_B_ate_less_than_required 1 0 1
#> item_6_B_hungry_but_did_not_eat 1 0 1
#> item_7_B_at_most_one_meal_per_day 1 0 0
#> item_8_C_ate_few_food_types 1 0 0
#> item_9_C_ate_less_than_required 1 0 0
#> item_10_C_decreased_food_quantity 1 0 0
#> item_11_C_skipped_a_meal 1 0 0
#> item_12_C_hungry_but_did_not_eat 1 0 0
#> item_13_C_at_most_one_meal_per_day 1 0 0
#> item_14_D_food_just_with_farinha 1 0 1
#> item_15_D_credit_for_eating 0 1 0
#> item_16_D_borrowed_food 0 1 0
#> item_17_D_had_meals_at_neighbors 0 1 0
#> item_18_D_reduced_meat_or_fish 1 0 0
#>
#> $constrain_T
#> [,1] [,2] [,3]
#> [1,] 1 0 0
#> [2,] 0 1 0
#> [3,] 0 0 1
#>
#> $constrain_V_sd
#> [,1]
#> [1,] 1
#> [2,] 1
#> [3,] 1
#>
#> $adap_Sigma
#> [,1] [,2] [,3]
#> [1,] 0.001 0.000 0.000
#> [2,] 0.000 0.001 0.000
#> [3,] 0.000 0.000 0.001
#>
#> $adap_scale
#> [1] 1
#>
#> $adap_C
#> [1] 0.7
#>
#> $adap_alpha
#> [1] 0.8
#>
#> $adap_accep_prob
#> [1] 0.234
#>
#> $c_initial
#> [1] -0.458832737 -1.598318595 0.002298863 -0.114153014 1.257591637 -0.952911389 1.137376323
#> [8] -1.260219269 0.780287540 -0.247557977 -0.844603367 -0.699546482 0.146197106 -0.816330230
#> [15] 0.673824769 -0.290361446 0.476389343 -0.157801694
#>
#> $c_prior_mean
#> [1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
#>
#> $c_prior_sd
#> [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
#>
#> $A_initial
#> [,1] [,2] [,3]
#> [1,] 0 0 0
#> [2,] 0 0 0
#> [3,] 0 0 0
#> [4,] 0 0 0
#> [5,] 0 0 0
#> [6,] 0 0 0
#> [7,] 0 0 0
#> [8,] 0 0 0
#> [9,] 0 0 0
#> [10,] 0 0 0
#> [11,] 1 0 0
#> [12,] 0 0 0
#> [13,] 1 0 0
#> [14,] 0 0 1
#> [15,] 0 0 0
#> [16,] 0 1 0
#> [17,] 0 0 0
#> [18,] 0 0 0
#>
#> $A_prior_mean
#> [,1] [,2] [,3]
#> [1,] 0 0 0
#> [2,] 0 0 0
#> [3,] 0 0 0
#> [4,] 0 0 0
#> [5,] 0 0 0
#> [6,] 0 0 0
#> [7,] 0 0 0
#> [8,] 0 0 0
#> [9,] 0 0 0
#> [10,] 0 0 0
#> [11,] 1 0 0
#> [12,] 0 0 0
#> [13,] 1 0 0
#> [14,] 0 0 1
#> [15,] 0 0 0
#> [16,] 0 1 0
#> [17,] 0 0 0
#> [18,] 0 0 0
#>
#> $A_prior_sd
#> [,1] [,2] [,3]
#> [1,] 1.00 1.00 1.00
#> [2,] 1.00 1.00 1.00
#> [3,] 1.00 1.00 1.00
#> [4,] 1.00 1.00 1.00
#> [5,] 1.00 1.00 1.00
#> [6,] 1.00 1.00 1.00
#> [7,] 1.00 1.00 1.00
#> [8,] 1.00 1.00 1.00
#> [9,] 1.00 1.00 1.00
#> [10,] 1.00 1.00 1.00
#> [11,] 0.45 1.00 1.00
#> [12,] 1.00 1.00 1.00
#> [13,] 0.45 1.00 1.00
#> [14,] 1.00 1.00 0.45
#> [15,] 1.00 1.00 1.00
#> [16,] 1.00 0.45 1.00
#> [17,] 1.00 1.00 1.00
#> [18,] 1.00 1.00 1.00
#>
#> $R_initial
#> [,1] [,2] [,3]
#> [1,] 1 0 0
#> [2,] 0 1 0
#> [3,] 0 0 1
#>
#> $R_prior_eta
#> [1] 1.5
#>
#> $B_initial
#> [,1] [,2] [,3]
#> [1,] NA NA NA
#>
#> $B_prior_mean
#> [,1] [,2] [,3]
#> [1,] NA NA NA
#>
#> $B_prior_sd
#> [,1] [,2] [,3]
#> [1,] NA NA NA
#>
#> $sigmas_gp_initial
#> [1] NA NA NA
#>
#> $sigmas_gp_mean
#> [1] NA NA NA
#>
#> $sigmas_gp_sd
#> [1] NA NA NA
#>
#> $phi_gp_initial
#> [1] NA NA NA
#>
#> $phi_gp_mean
#> [1] NA NA NA
#>
#> $phi_gp_sd
#> [1] NA NA NA
#>
#> $model_type
#> [1] "cifa"
Save model output
saveRDS(samples, file = file.path(path_modelled, "cifa-ipixuna-urban.rds"))
Time to execute the task
Only useful when executed with Rscript
.
proc.time()
#> user system elapsed
#> 5685.509 9907.787 1975.831