--- title: "Scoring the PID-5-BF" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Scoring the PID-5-BF} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(hitop) ``` ```{r} #| include: false library(tibble) ``` The PID-5-BF (Brief Form) is a 25-item version of the PID-5 that yields the 5 personality-trait domain scores and an overall total score (it does not produce the 25 facet scores). We can demonstrate the package's functionality using some simulated data. ## Score simulated PID-5-BF data The `sim_pid5bf` dataset is built into the package and contains 100 rows (each a simulated participant) across 25 columns named `pid5bf_01` to `pid5bf_25`. If your own PID-5-BF columns are named some other way, `rename_pid5_items()` with `version = "BF"` will rename them to this pattern. `label_pid5()` with `version = "BF"` then attaches the item prompts, or the domain and total display names under `target = "scales"`, as `label` attributes. To collect PID-5-BF responses online with hitop-form and read the saved files back with `read_form_responses()`, see the section "Collecting Responses Online with hitop-form" in `vignette("pid5_scoring")`. To compute the 5 domain scores and the total, we use `score_pid5()` with `version = "BF"`. As with the other forms, we can specify the items by column number (`items = 1:25`) and set `append = FALSE` to see just the scores. The only validity scale calculable from this subset of items is the percentage of missing items (PNA), which `validity_pid5()` returns. The `pid_total` column is the mean of all 25 items, following Markon et al. (2024, p. 23). Note that this is *not* the same as averaging the 5 domain scores whenever any items are missing: each scale applies the `missing` rule to its own items, so the total tolerates up to 6 unanswered items while a 5-item domain tolerates only 1. See `?score_pid5` for the details. ```{r} data("sim_pid5bf") score_pid5(sim_pid5bf, items = 1:25, version = "BF", append = FALSE) validity_pid5(sim_pid5bf, items = 1:25, version = "BF", append = FALSE) ``` ## Scale Reliability As we compute scale scores, we can also estimate their inter-item reliability using Cronbach's α (alpha) or McDonald's ω (omega total). α is fast and widely used, but it assumes tau-equivalence (all items load equally on a single factor); violations can make α under- or over-estimate reliability. ω is based on a congeneric single-factor model, allowing items to have different loadings and error variances; it typically provides a more accurate reliability estimate for unit-weighted sums. Both assume the scale is essentially unidimensional; α and ω coincide when tau-equivalence holds. We estimate reliability with the `reliability_pid5()` function, which returns a tibble with one row per scale: its printed name (`Scale`), the stem that names its column in the scored output (`camelCase`), the number of items (`nItems`), and the requested coefficients. By default it computes both `alpha` and `omega`; for the latter, we will need the **lavaan** package installed (set `omega = FALSE` to skip it). Note that, because this is naively simulated data, we would expect the reliability in this example to be poor. ```{r reliability} #| warning: false reliability_pid5( data = sim_pid5bf, items = 1:25, version = "BF" ) ``` ## Normative Scores The `pid_norms` dataset carries the normative score distributions published by Markon et al. (2024), including a set built on the brief form. The `norm_pid5()` function looks scored columns up in those tables and returns, for each one, the T score and percentile printed against the nearest tabled raw score. It converts scores rather than computing them, so we hand it the output of `score_pid5()`. The brief-form tables cover the five domain scales and the total score — every scale `score_pid5(version = "BF")` returns — so each column here gains both a `_t` and a `_ptl` column. ```{r norming} scored <- score_pid5(sim_pid5bf, items = 1:25, version = "BF") norm_pid5( scored, scores = paste0( "pid_", c("negativeAffectivity", "detachment", "antagonism", "disinhibition", "psychoticism", "total") ), version = "BF", append = FALSE ) ``` Every number returned is a cell of a published table: the nearest printed row is selected and nothing is interpolated. A score that falls outside a printed range is capped to the nearest end rather than extrapolated, and a warning reports how many observations that happened to. Every report this function makes is a warning, so a single `suppressWarnings()` call silences it. Note that `version = "BF"` selects the brief-form tables — the same raw score converts differently across forms. If the items were answered on a four-option response scale that starts somewhere other than 0 — 1 to 4, say — pass that range as `srange` and each score is reconciled to the published 0–3 metric before it is looked up, with a warning naming which scales were adjusted and which were left alone. The per-scale formulas are given in `?norm_pid5`. ## Profile Plots Once a respondent's scores are normed, `plot_pid5()` draws them as a profile against the published metric. It takes one respondent — a profile plot shows one person — so we norm the whole dataset and hand it a single row. Passing `version = "BF"` builds the plot against the brief form's own tables. ```{r profile-norm} #| message: false #| warning: false bf_scales <- paste0("pid_", pid_scales[["BF"]]$camelCase) normed <- norm_pid5(scored, scores = bf_scales, version = "BF") ``` ```{r profile-plot} #| fig.width: 7 #| fig.height: 4 #| eval: !expr rlang::is_installed("ggplot2", version = "3.4.0") plot_pid5(normed[1, ], version = "BF") ``` All six brief-form scales get a point, but the profile line stops before `total`: the total is an overall elevation across the five domains rather than a sixth domain, so joining it to the line would imply a comparability it does not have. The point itself is still drawn, so the elevation is readable alongside the domains it summarizes. The dashed line marks T = 50, the normative sample's mean, and the score axis spans the range the published brief-form tables actually print for these scales — so the axis does not rescale from respondent to respondent and two brief-form profiles are directly comparable. Nothing on the plot says whether a score is high, low, or concerning: {hitop} presents scores against norms and leaves the interpreting to you. There is no facet profile for this form. The brief form's 25 items yield the five domains and the total and no facet scores at all, so `level = "facet"` is an error here rather than an empty plot; facet profiles are available for the full and short forms. Set `metric = "percentile"` for a percentile axis instead of T scores; the full-form vignette shows one. The result is an ordinary ggplot object, so you can restyle it with any ggplot2 layer.