The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Data preparation

spifa() expects data in a specific shape: one row per respondent, and the binary item responses stored as a single matrix-valued column (nobs x nitems) rather than one column per item. This is the same convention base R uses for multivariate lm(). This vignette builds a small dataset acceptable by spifa().

library(spifa)
library(sf)

Simulating a raw questionnaire

Five binary items, a household wealth covariate, and GPS coordinates, for 20 households:

set.seed(1)
nobs <- 20
nitems <- 5

raw <- data.frame(
  id = seq_len(nobs),
  wealth = rnorm(nobs),
  lon = runif(nobs, -71.70, -71.68),
  lat = runif(nobs, -7.06, -7.04),
  item1 = rbinom(nobs, 1, 0.1) == 1,
  item2 = rbinom(nobs, 1, 0.2) == 1,
  item3 = rbinom(nobs, 1, 0.3) == 1,
  item4 = rbinom(nobs, 1, 0.4) == 1,
  item5 = rbinom(nobs, 1, 0.5) == 1
)
raw
#>    id      wealth       lon       lat item1 item2 item3 item4 item5
#> 1   1 -0.62645381 -71.68358 -7.041742 FALSE FALSE  TRUE  TRUE FALSE
#> 2   2  0.18364332 -71.68706 -7.054128 FALSE FALSE FALSE  TRUE  TRUE
#> 3   3 -0.83562861 -71.68434 -7.050819 FALSE FALSE FALSE FALSE FALSE
#> 4   4  1.59528080 -71.68894 -7.053352 FALSE  TRUE FALSE FALSE  TRUE
#> 5   5  0.32950777 -71.68941 -7.046983 FALSE FALSE  TRUE  TRUE  TRUE
#> 6   6 -0.82046838 -71.68421 -7.054840 FALSE FALSE FALSE FALSE FALSE
#> 7   7  0.48742905 -71.69953 -7.050429 FALSE FALSE FALSE FALSE FALSE
#> 8   8  0.73832471 -71.69046 -7.044674 FALSE FALSE FALSE  TRUE FALSE
#> 9   9  0.57578135 -71.68535 -7.058315 FALSE  TRUE FALSE FALSE  TRUE
#> 10 10 -0.30538839 -71.68615 -7.042494 FALSE FALSE FALSE  TRUE FALSE
#> 11 11  1.51178117 -71.69045 -7.053219 FALSE  TRUE FALSE  TRUE  TRUE
#> 12 12  0.38984324 -71.68278 -7.043211 FALSE FALSE FALSE FALSE  TRUE
#> 13 13 -0.62124058 -71.69124 -7.053066 FALSE FALSE FALSE FALSE  TRUE
#> 14 14 -2.21469989 -71.69510 -7.053325 FALSE FALSE FALSE FALSE FALSE
#> 15 15  1.12493092 -71.69859 -7.050473 FALSE FALSE  TRUE FALSE FALSE
#> 16 16 -0.04493361 -71.69801 -7.042156 FALSE FALSE FALSE FALSE  TRUE
#> 17 17 -0.01619026 -71.69367 -7.042713 FALSE FALSE FALSE FALSE  TRUE
#> 18 18  0.94383621 -71.68963 -7.052200 FALSE FALSE FALSE FALSE  TRUE
#> 19 19  0.82122120 -71.68676 -7.044454 FALSE FALSE  TRUE FALSE  TRUE
#> 20 20  0.59390132 -71.69186 -7.040788 FALSE FALSE FALSE FALSE  TRUE

Data for spifa()

We extract the item as a matrix, and then provide it to a new data.frame to later convert to sf:

# get items as a matrix
items <- subset(raw, select = grep("^item", names(raw))) |>
  as.matrix() |>
  unname()
# create data.frame and geo-reference it
data <- data.frame(items = I(items), wealth = raw$wealth, x = raw$lon, y = raw$lat) |>
    st_as_sf(coords = c("x", "y"), crs = 4326)
data
#> Simple feature collection with 20 features and 2 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: -71.69953 ymin: -7.058315 xmax: -71.68278 ymax: -7.040788
#> Geodetic CRS:  WGS 84
#> First 10 features:
#>    items.1 items.2 items.3 items.4 items.5     wealth                    geometry
#> 1    FALSE   FALSE    TRUE    TRUE   FALSE -0.6264538 POINT (-71.68358 -7.041742)
#> 2    FALSE   FALSE   FALSE    TRUE    TRUE  0.1836433 POINT (-71.68706 -7.054128)
#> 3    FALSE   FALSE   FALSE   FALSE   FALSE -0.8356286 POINT (-71.68434 -7.050819)
#> 4    FALSE    TRUE   FALSE   FALSE    TRUE  1.5952808 POINT (-71.68894 -7.053352)
#> 5    FALSE   FALSE    TRUE    TRUE    TRUE  0.3295078 POINT (-71.68941 -7.046983)
#> 6    FALSE   FALSE   FALSE   FALSE   FALSE -0.8204684  POINT (-71.68421 -7.05484)
#> 7    FALSE   FALSE   FALSE   FALSE   FALSE  0.4874291 POINT (-71.69953 -7.050429)
#> 8    FALSE   FALSE   FALSE    TRUE   FALSE  0.7383247 POINT (-71.69046 -7.044674)
#> 9    FALSE    TRUE   FALSE   FALSE    TRUE  0.5757814 POINT (-71.68535 -7.058315)
#> 10   FALSE   FALSE   FALSE    TRUE   FALSE -0.3053884 POINT (-71.68615 -7.042494)

Notice that we can easily access the response items:

data$items
#>        [,1]  [,2]  [,3]  [,4]  [,5]
#>  [1,] FALSE FALSE  TRUE  TRUE FALSE
#>  [2,] FALSE FALSE FALSE  TRUE  TRUE
#>  [3,] FALSE FALSE FALSE FALSE FALSE
#>  [4,] FALSE  TRUE FALSE FALSE  TRUE
#>  [5,] FALSE FALSE  TRUE  TRUE  TRUE
#>  [6,] FALSE FALSE FALSE FALSE FALSE
#>  [7,] FALSE FALSE FALSE FALSE FALSE
#>  [8,] FALSE FALSE FALSE  TRUE FALSE
#>  [9,] FALSE  TRUE FALSE FALSE  TRUE
#> [10,] FALSE FALSE FALSE  TRUE FALSE
#> [11,] FALSE  TRUE FALSE  TRUE  TRUE
#> [12,] FALSE FALSE FALSE FALSE  TRUE
#> [13,] FALSE FALSE FALSE FALSE  TRUE
#> [14,] FALSE FALSE FALSE FALSE FALSE
#> [15,] FALSE FALSE  TRUE FALSE FALSE
#> [16,] FALSE FALSE FALSE FALSE  TRUE
#> [17,] FALSE FALSE FALSE FALSE  TRUE
#> [18,] FALSE FALSE FALSE FALSE  TRUE
#> [19,] FALSE FALSE  TRUE FALSE  TRUE
#> [20,] FALSE FALSE FALSE FALSE  TRUE

This dataset can directly be used to fit a model:

spifa(items ~ wealth, data = data, nfactors = 2, niter = 5)
#> Item factor analysis model: spifa_pred
#> Formula: items ~ wealth
#> Dimensions: 20 respondents, 5 items, 2 latent factors, 2 spatial processes
#> MCMC: 1 chain, iter = 5, thin = 1, samples = 5
#> 
#> Item model parameters:
#>          mean median   sd   q10    q90 ess_bulk rhat
#> c[1]   -1.478 -1.471 0.24 -1.71 -1.241       NA  0.9
#> c[2]   -0.298 -0.353 0.20 -0.44 -0.091       NA  1.9
#> c[3]   -0.651 -0.783 0.20 -0.79 -0.422       NA  0.9
#> c[4]   -0.734 -0.705 0.30 -1.02 -0.488       NA  0.9
#> c[5]   -0.039 -0.044 0.17 -0.19  0.111       NA  0.9
#> A[1,1]  0.470  0.500 0.22  0.25  0.663       NA  1.9
#> A[2,1]  0.770  0.753 0.16  0.63  0.917       NA  1.9
#> A[3,1]  0.846  0.907 0.35  0.47  1.179       NA  0.9
#> A[4,1] -0.031 -0.173 0.63 -0.55  0.625       NA  1.9
#> A[5,1]  0.441  0.370 0.56 -0.11  1.010       NA  1.9
#> A[2,2]  1.176  1.282 0.34  0.80  1.480       NA  1.9
#> A[3,2]  0.035 -0.024 0.20 -0.15  0.252       NA  0.9
#> A[4,2] -0.394 -0.343 0.38 -0.76 -0.070       NA  1.9
#> A[5,2] -0.161 -0.254 0.27 -0.35  0.111       NA  1.9
#> 
#> Factor model parameters:
#>              mean median      sd    q10   q90 ess_bulk rhat
#> B[1,1]     0.1078 0.1789 0.33165 -0.250 0.402       NA  1.9
#> B[1,2]     0.0023 0.1551 0.35755 -0.384 0.321       NA  1.9
#> T[1,1]     0.4868 0.4828 0.01278  0.477 0.500       NA  0.9
#> T[2,2]     0.6929 0.6908 0.00903  0.684 0.702       NA  1.9
#> phi[1]     0.0471 0.0477 0.00097  0.046 0.048       NA  1.9
#> phi[2]     0.0536 0.0539 0.00062  0.053 0.054       NA  0.9
#> Corr[2,1] -0.0021 0.0023 0.02035 -0.023 0.018       NA  1.9
#> 
#> ess_bulk is the bulk effective sample size; rhat is the potential
#> scale reduction factor on split chains (Rhat = 1 at convergence).
#> Use summary() for the full set of statistics (incl. ess_tail).

Alternatively, you could also create the sf data first and then assign the items:

data <- data.frame(wealth = raw$wealth, x = raw$lon, y = raw$lat) |>
    st_as_sf(coords = c("x", "y"), crs = 4326)
data$items <- items
data
#> Simple feature collection with 20 features and 2 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: -71.69953 ymin: -7.058315 xmax: -71.68278 ymax: -7.040788
#> Geodetic CRS:  WGS 84
#> First 10 features:
#>        wealth                    geometry items.1 items.2 items.3 items.4 items.5
#> 1  -0.6264538 POINT (-71.68358 -7.041742)   FALSE   FALSE    TRUE    TRUE   FALSE
#> 2   0.1836433 POINT (-71.68706 -7.054128)   FALSE   FALSE   FALSE    TRUE    TRUE
#> 3  -0.8356286 POINT (-71.68434 -7.050819)   FALSE   FALSE   FALSE   FALSE   FALSE
#> 4   1.5952808 POINT (-71.68894 -7.053352)   FALSE    TRUE   FALSE   FALSE    TRUE
#> 5   0.3295078 POINT (-71.68941 -7.046983)   FALSE   FALSE    TRUE    TRUE    TRUE
#> 6  -0.8204684  POINT (-71.68421 -7.05484)   FALSE   FALSE   FALSE   FALSE   FALSE
#> 7   0.4874291 POINT (-71.69953 -7.050429)   FALSE   FALSE   FALSE   FALSE   FALSE
#> 8   0.7383247 POINT (-71.69046 -7.044674)   FALSE   FALSE   FALSE    TRUE   FALSE
#> 9   0.5757814 POINT (-71.68535 -7.058315)   FALSE    TRUE   FALSE   FALSE    TRUE
#> 10 -0.3053884 POINT (-71.68615 -7.042494)   FALSE   FALSE   FALSE    TRUE   FALSE
spifa(items ~ wealth, data = data, nfactors = 2, niter = 5)
#> Item factor analysis model: spifa_pred
#> Formula: items ~ wealth
#> Dimensions: 20 respondents, 5 items, 2 latent factors, 2 spatial processes
#> MCMC: 1 chain, iter = 5, thin = 1, samples = 5
#> 
#> Item model parameters:
#>          mean median   sd    q10     q90 ess_bulk rhat
#> c[1]   -0.521 -0.620 1.15 -1.596  0.6837       NA  1.9
#> c[2]   -0.395 -0.422 0.22 -0.586 -0.1786       NA  1.9
#> c[3]   -0.680 -0.644 0.64 -1.319 -0.0379       NA  1.9
#> c[4]   -0.269 -0.381 0.26 -0.506  0.0105       NA  0.9
#> c[5]    0.343  0.310 0.14  0.240  0.4781       NA  0.9
#> A[1,1]  0.719  0.623 0.26  0.570  0.9666       NA  1.9
#> A[2,1]  0.733  0.877 0.24  0.464  0.9076       NA  1.9
#> A[3,1] -0.418 -0.393 0.43 -0.853  0.0078       NA  1.9
#> A[4,1]  0.072  0.084 0.13 -0.060  0.2057       NA  1.9
#> A[5,1]  0.539  0.674 0.46  0.039  0.9192       NA  1.9
#> A[2,2]  0.513  0.558 0.14  0.365  0.6409       NA  0.9
#> A[3,2] -0.640 -0.627 0.30 -0.939 -0.3380       NA  1.9
#> A[4,2]  0.338  0.604 0.59 -0.306  0.8515       NA  1.9
#> A[5,2] -0.347 -0.385 0.23 -0.562 -0.1122       NA  1.9
#> 
#> Factor model parameters:
#>             mean median      sd     q10   q90 ess_bulk rhat
#> B[1,1]    0.0339 0.0249 0.36675 -0.3279 0.345       NA  0.9
#> B[1,2]    0.0378 0.0188 0.19827 -0.1308 0.222       NA  1.9
#> T[1,1]    0.5518 0.5554 0.01123  0.5399 0.560       NA  0.9
#> T[2,2]    0.5037 0.5025 0.00435  0.5008 0.508       NA  0.9
#> phi[1]    0.0487 0.0487 0.00079  0.0480 0.049       NA  1.9
#> phi[2]    0.0512 0.0511 0.00082  0.0505 0.052       NA  1.9
#> Corr[2,1] 0.0061 0.0043 0.01438 -0.0058 0.021       NA  1.9
#> 
#> ess_bulk is the bulk effective sample size; rhat is the potential
#> scale reduction factor on split chains (Rhat = 1 at convergence).
#> Use summary() for the full set of statistics (incl. ess_tail).

Missing values

Missing item responses

Individual missing item responses (NA) don’t need to be dropped or imputed – spifa() handles them natively, treating a missing response as an unobserved auxiliary variable to be sampled along with everything else, rather than requiring complete rows:

data_na <- data
data_na$items[1:5, 1] <- NA

fit_na <- spifa(items ~ 1, data = data_na, nfactors = 2, ngp = 0, niter = 5)
fit_na
#> Item factor analysis model: eifa
#> Formula: items ~ 1
#> Dimensions: 20 respondents, 5 items, 2 latent factors, 0 spatial processes
#> MCMC: 1 chain, iter = 5, thin = 1, samples = 5
#> 
#> Item model parameters:
#>         mean median   sd     q10   q90 ess_bulk rhat
#> c[1]   -0.76 -0.759 0.41 -1.1637 -0.35       NA  1.9
#> c[2]   -1.09 -1.114 0.34 -1.4281 -0.76       NA  1.9
#> c[3]   -0.11 -0.083 0.29 -0.3795  0.18       NA  1.9
#> c[4]   -0.51 -0.431 0.29 -0.8142 -0.24       NA  0.9
#> c[5]    0.35  0.348 0.10  0.2492  0.45       NA  1.9
#> A[1,1]  0.45  0.577 0.25  0.1766  0.67       NA  0.9
#> A[2,1] -0.37 -0.461 0.25 -0.5951 -0.11       NA  1.9
#> A[3,1]  0.25  0.284 0.27 -0.0021  0.48       NA  1.9
#> A[4,1]  0.59  0.670 0.24  0.3540  0.74       NA  1.9
#> A[5,1]  0.23  0.194 0.24  0.0111  0.47       NA  1.9
#> A[2,2]  1.49  1.534 0.30  1.2155  1.72       NA  0.9
#> A[3,2]  0.15  0.217 0.20 -0.0657  0.34       NA  0.9
#> A[4,2]  0.28  0.413 0.38 -0.1132  0.54       NA  0.9
#> A[5,2]  0.30  0.239 0.16  0.1605  0.47       NA  1.9
#> 
#> ess_bulk is the bulk effective sample size; rhat is the potential
#> scale reduction factor on split chains (Rhat = 1 at convergence).
#> Use summary() for the full set of statistics (incl. ess_tail).

As you can see in Dimensions, the missing responses are not removed but rather modelled.

Missing predictor values

Missing predictor values behave differently, and are worth calling out: a NA in a predictor column drops that respondent from the whole fit:

data_na <- data
data_na$wealth[1:5] <- NA

fit_na <- spifa(items ~ wealth, data = data_na, nfactors = 2, ngp = 0, niter = 5)
fit_na
#> Item factor analysis model: cifa_pred
#> Formula: items ~ wealth
#> Dimensions: 15 respondents, 5 items, 2 latent factors, 0 spatial processes
#> MCMC: 1 chain, iter = 5, thin = 1, samples = 5
#> 
#> Item model parameters:
#>          mean median   sd   q10    q90 ess_bulk rhat
#> c[1]   -0.286 -0.472 0.48 -0.71  0.249       NA  1.9
#> c[2]   -0.724 -0.606 0.69 -1.39 -0.081       NA  1.9
#> c[3]   -1.120 -1.276 0.40 -1.46 -0.673       NA  1.9
#> c[4]   -0.614 -0.572 0.22 -0.84 -0.436       NA  1.9
#> c[5]    0.214  0.299 0.14  0.06  0.311       NA  1.9
#> A[1,1]  0.437  0.412 0.63 -0.19  1.082       NA  1.9
#> A[2,1] -0.266 -0.233 0.18 -0.46 -0.111       NA  1.9
#> A[3,1]  0.148 -0.040 0.38 -0.17  0.571       NA  1.9
#> A[4,1]  0.099  0.151 0.24 -0.13  0.276       NA  1.9
#> A[5,1] -0.601 -0.679 0.35 -0.90 -0.242       NA  1.9
#> A[2,2]  0.547  0.559 0.31  0.25  0.815       NA  1.9
#> A[3,2] -0.070 -0.022 0.14 -0.22  0.048       NA  0.9
#> A[4,2] -0.224 -0.257 0.23 -0.44  0.019       NA  0.9
#> A[5,2]  0.046 -0.070 0.27 -0.19  0.338       NA  1.9
#> 
#> Factor model parameters:
#>             mean median    sd     q10   q90 ess_bulk rhat
#> B[1,1]    -0.495  -0.45 0.329 -0.8420 -0.19       NA  1.9
#> B[1,2]    -0.780  -0.91 0.247 -0.9809 -0.50       NA  0.9
#> Corr[2,1]  0.016   0.01 0.022 -0.0031  0.04       NA  0.9
#> 
#> ess_bulk is the bulk effective sample size; rhat is the potential
#> scale reduction factor on split chains (Rhat = 1 at convergence).
#> Use summary() for the full set of statistics (incl. ess_tail).

As you can see in Dimensions, the 5 respondents with a missing wealth value are dropped – 15 respondents remain, unlike the item-response case above.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.