Skip to contents

The HiTOP-SR instrument has 405 items and yields 76 scale scores. To demonstrate the ability of the package to calculate these scale scores, we can use real example data (n=411) that was collected at the University of Kansas (KU) by Girard & Gray in 2024–2025. This data is stored in the package under the name ku_hitopsr.

First, we load the package into memory using the library() function. If this doesn’t work, make sure you installed the package properly (see the README on GitHub).

Next, we can load the example dataset from the package using the data() function. It is a large tibble that contains a participant column with a unique identifier for each participant, a biosex column indicating whether each participant is “female” or “male”, and then 405 columns numbered hsr_001 to hsr_405 containing each participant’s rating on each item of the HiTOP-SR (on a numerical scale from 1 to 4).

data("ku_hitopsr")
ku_hitopsr
#> # A tibble: 411 × 407
#>    participant biosex hsr_001 hsr_002 hsr_003 hsr_004 hsr_005 hsr_006 hsr_007
#>    <chr>       <fct>    <int>   <int>   <int>   <int>   <int>   <int>   <int>
#>  1 P001        male         1       1       1       1       1       1       2
#>  2 P002        male         3       4       1       1       2       2       2
#>  3 P003        male         3       3       1       1       2       1       2
#>  4 P004        male         2       3       1       1       1       1       1
#>  5 P005        male         1       2       1       1       3       1       2
#>  6 P006        female       2       1       1       1       2       2       2
#>  7 P007        female       1       1       1       1       1       1       1
#>  8 P008        male         2       4       1       1       1       1       2
#>  9 P009        female       4       1       1       1       2       1       3
#> 10 P010        female       1       3       1       1       1       1       1
#> # ℹ 401 more rows
#> # ℹ 398 more variables: hsr_008 <int>, hsr_009 <int>, hsr_010 <int>,
#> #   hsr_011 <int>, hsr_012 <int>, hsr_013 <int>, hsr_014 <int>, hsr_015 <int>,
#> #   hsr_016 <int>, hsr_017 <int>, hsr_018 <int>, hsr_019 <int>, hsr_020 <int>,
#> #   hsr_021 <int>, hsr_022 <int>, hsr_023 <int>, hsr_024 <int>, hsr_025 <int>,
#> #   hsr_026 <int>, hsr_027 <int>, hsr_028 <int>, hsr_029 <int>, hsr_030 <int>,
#> #   hsr_031 <int>, hsr_032 <int>, hsr_033 <int>, hsr_034 <int>, …

Basic Scoring

To turn these item-level ratings into mean scores on the 76 scales, we can use the score_hitopsr() function. It needs to know what object contains the data and which columns contain the item-level data. There are several ways we can specify the items. First, we can provide the column numbers and use the : shortcut. In this tibble, the items are from column 3 to column 407 so we can use items = 3:407. I am going to also set append = FALSE so that you can quickly see the scale scores.

scores <- score_hitopsr(
  data = ku_hitopsr,
  items = 3:407,
  append = FALSE
)
scores
#> # A tibble: 411 × 76
#>    hsr_agoraphobia hsr_antisocialBehavior hsr_appearanceFocus hsr_appetiteLoss
#>              <dbl>                  <dbl>               <dbl>            <dbl>
#>  1             1                     1.5                  2.2             1   
#>  2             1.4                   1                    3               1.67
#>  3             2                     1.12                 2               1   
#>  4             1.2                   1                    2.6             1.33
#>  5             1.6                   1                    1.4             2.67
#>  6             1                     1                    1.6             1.33
#>  7             1                     1                    1               1   
#>  8             1                     1                    1.4             1.33
#>  9             2                     1                    2.2             1   
#> 10             1                     1                    2.2             1   
#> # ℹ 401 more rows
#> # ℹ 72 more variables: hsr_bingeEating <dbl>, hsr_bodilyDistress <dbl>,
#> #   hsr_bodyDissatisfaction <dbl>, hsr_callousness <dbl>, hsr_checking <dbl>,
#> #   hsr_cleaning <dbl>, hsr_cognitiveProblems <dbl>,
#> #   hsr_conversionSymptoms <dbl>, hsr_counting <dbl>,
#> #   hsr_dietaryRestraint <dbl>, hsr_difficultiesReachingOrgasm <dbl>,
#> #   hsr_diseaseConviction <dbl>, hsr_dishonesty <dbl>, …

Appending

If I had instead set append = TRUE (or left it off, as that is the default), we would get back the ku_hitopsr tibble with the scale scores added to the end as extra columns. Notice below how we now have 483 columns instead of 407.

scores <- score_hitopsr(
  data = ku_hitopsr,
  items = 3:407
)
scores
#> # A tibble: 411 × 483
#>    participant biosex hsr_001 hsr_002 hsr_003 hsr_004 hsr_005 hsr_006 hsr_007
#>    <chr>       <fct>    <int>   <int>   <int>   <int>   <int>   <int>   <int>
#>  1 P001        male         1       1       1       1       1       1       2
#>  2 P002        male         3       4       1       1       2       2       2
#>  3 P003        male         3       3       1       1       2       1       2
#>  4 P004        male         2       3       1       1       1       1       1
#>  5 P005        male         1       2       1       1       3       1       2
#>  6 P006        female       2       1       1       1       2       2       2
#>  7 P007        female       1       1       1       1       1       1       1
#>  8 P008        male         2       4       1       1       1       1       2
#>  9 P009        female       4       1       1       1       2       1       3
#> 10 P010        female       1       3       1       1       1       1       1
#> # ℹ 401 more rows
#> # ℹ 474 more variables: hsr_008 <int>, hsr_009 <int>, hsr_010 <int>,
#> #   hsr_011 <int>, hsr_012 <int>, hsr_013 <int>, hsr_014 <int>, hsr_015 <int>,
#> #   hsr_016 <int>, hsr_017 <int>, hsr_018 <int>, hsr_019 <int>, hsr_020 <int>,
#> #   hsr_021 <int>, hsr_022 <int>, hsr_023 <int>, hsr_024 <int>, hsr_025 <int>,
#> #   hsr_026 <int>, hsr_027 <int>, hsr_028 <int>, hsr_029 <int>, hsr_030 <int>,
#> #   hsr_031 <int>, hsr_032 <int>, hsr_033 <int>, hsr_034 <int>, …

Items as Strings

Alternatively, we could provide the item column names as a character string. Typing out all 405 item names would be a hassle, but luckily this dataset named them consistently so we can build the names automatically using sprintf(). If we use the “hsr_%03d” format and apply that across the numbers 1 to 405, that will create the zero-padded column names we need. The same pattern names the items in the package’s other example dataset (sim_hitopsr) and in data collected through its Qualtrics and REDCap exports, so this one expression selects the items in all of them.

scores <- score_hitopsr(
  data = ku_hitopsr,
  items = sprintf("hsr_%03d", 1:405),
  append = FALSE
)
scores
#> # A tibble: 411 × 76
#>    hsr_agoraphobia hsr_antisocialBehavior hsr_appearanceFocus hsr_appetiteLoss
#>              <dbl>                  <dbl>               <dbl>            <dbl>
#>  1             1                     1.5                  2.2             1   
#>  2             1.4                   1                    3               1.67
#>  3             2                     1.12                 2               1   
#>  4             1.2                   1                    2.6             1.33
#>  5             1.6                   1                    1.4             2.67
#>  6             1                     1                    1.6             1.33
#>  7             1                     1                    1               1   
#>  8             1                     1                    1.4             1.33
#>  9             2                     1                    2.2             1   
#> 10             1                     1                    2.2             1   
#> # ℹ 401 more rows
#> # ℹ 72 more variables: hsr_bingeEating <dbl>, hsr_bodilyDistress <dbl>,
#> #   hsr_bodyDissatisfaction <dbl>, hsr_callousness <dbl>, hsr_checking <dbl>,
#> #   hsr_cleaning <dbl>, hsr_cognitiveProblems <dbl>,
#> #   hsr_conversionSymptoms <dbl>, hsr_counting <dbl>,
#> #   hsr_dietaryRestraint <dbl>, hsr_difficultiesReachingOrgasm <dbl>,
#> #   hsr_diseaseConviction <dbl>, hsr_dishonesty <dbl>, …

Scale Prefixes

Also note that each scale column has the prefix “hsr_” in its name. You can change the prefix (e.g., setting it to "hitop_") or even turn it off (e.g., setting it to "") using the prefix argument.

scores <- score_hitopsr(
  data = ku_hitopsr,
  items = sprintf("hsr_%03d", 1:405),
  prefix = "hitop_",
  append = FALSE
)
scores
#> # A tibble: 411 × 76
#>    hitop_agoraphobia hitop_antisocialBehavior hitop_appearanceFocus
#>                <dbl>                    <dbl>                 <dbl>
#>  1               1                       1.5                    2.2
#>  2               1.4                     1                      3  
#>  3               2                       1.12                   2  
#>  4               1.2                     1                      2.6
#>  5               1.6                     1                      1.4
#>  6               1                       1                      1.6
#>  7               1                       1                      1  
#>  8               1                       1                      1.4
#>  9               2                       1                      2.2
#> 10               1                       1                      2.2
#> # ℹ 401 more rows
#> # ℹ 73 more variables: hitop_appetiteLoss <dbl>, hitop_bingeEating <dbl>,
#> #   hitop_bodilyDistress <dbl>, hitop_bodyDissatisfaction <dbl>,
#> #   hitop_callousness <dbl>, hitop_checking <dbl>, hitop_cleaning <dbl>,
#> #   hitop_cognitiveProblems <dbl>, hitop_conversionSymptoms <dbl>,
#> #   hitop_counting <dbl>, hitop_dietaryRestraint <dbl>,
#> #   hitop_difficultiesReachingOrgasm <dbl>, hitop_diseaseConviction <dbl>, …

Simple Standard Errors (deprecated)

The calc_se argument is deprecated. It, and the _se columns it adds, will be removed in a future release, and a call that passes calc_se = TRUE now warns. Use interval_hitopsr() instead, shown under Confidence Intervals below.

What the argument computes, while it lasts: the SD of the items the respondent actually answered divided by the square root of how many of those items they answered. Each one summarizes how much a respondent’s answers varied within a scale; it is not an estimate of how precisely the scale measures the underlying trait, so it does not give a confidence interval for a respondent’s true score. That is what replaces it: an interval, from the reliability of the scale rather than from one respondent’s spread of answers.

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_hitopsr() 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).

reliability_hitopsr(
  data = ku_hitopsr,
  items = sprintf("hsr_%03d", 1:405)
)
#> # A tibble: 76 × 5
#>    Scale                camelCase           nItems alpha  omega
#>    <chr>                <chr>                <int> <dbl>  <dbl>
#>  1 Agoraphobia          agoraphobia              5 0.820  0.840
#>  2 Antisocial Behavior  antisocialBehavior       8 0.351 NA    
#>  3 Appearance Focus     appearanceFocus          5 0.776  0.780
#>  4 Appetite Loss        appetiteLoss             3 0.762  0.797
#>  5 Binge Eating         bingeEating              3 0.849  0.849
#>  6 Bodily Distress      bodilyDistress           6 0.787  0.794
#>  7 Body Dissatisfaction bodyDissatisfaction      4 0.897  0.898
#>  8 Callousness          callousness              6 0.795  0.800
#>  9 Checking             checking                 5 0.835  0.836
#> 10 Cleaning             cleaning                 6 0.785  0.793
#> # ℹ 66 more rows

Confidence Intervals

A scale score is measured with error, so it is worth reporting a range rather than a single number. interval_hitopsr() returns three columns per scale: _est, an estimate of the respondent’s true score, and _lo and _hi, the bounds of a confidence interval around it.

scored <- score_hitopsr(
  data = ku_hitopsr,
  items = sprintf("hsr_%03d", 1:405),
  append = FALSE
)

interval_hitopsr(
  data = scored,
  scores = c("hsr_agoraphobia", "hsr_wellBeing"),
  append = FALSE
)
#> # A tibble: 411 × 6
#>    hsr_agoraphobia_est hsr_agoraphobia_lo hsr_agoraphobia_hi hsr_wellBeing_est
#>                  <dbl>              <dbl>              <dbl>             <dbl>
#>  1                1.05              0.480               1.61              3.53
#>  2                1.42              0.851               1.98              3.53
#>  3                1.97              1.41                2.54              2.22
#>  4                1.23              0.666               1.80              3.72
#>  5                1.60              1.04                2.17              3.16
#>  6                1.05              0.480               1.61              2.60
#>  7                1.05              0.480               1.61              3.16
#>  8                1.05              0.480               1.61              1.85
#>  9                1.97              1.41                2.54              2.60
#> 10                1.05              0.480               1.61              2.60
#> # ℹ 401 more rows
#> # ℹ 2 more variables: hsr_wellBeing_lo <dbl>, hsr_wellBeing_hi <dbl>

The estimate is not the observed score. It is the observed score pulled toward the reference group’s mean, because with imperfect measurement a true score tends to lie nearer the mean than the observed score does – the less reliable the scale, the further it is pulled. The method is the regression approach with scale correction from Schmukle (2026), which puts the estimate back on the same metric as the observed score so the two can be read against each other.

The width comes from the scale’s reliability and the reference group’s standard deviation, so it is the same for every respondent on a given scale and it narrows as reliability rises. Widen or narrow the interval with level:

interval_hitopsr(
  data = scored,
  scores = "hsr_agoraphobia",
  level = 0.80,
  append = FALSE
)
#> # A tibble: 411 × 3
#>    hsr_agoraphobia_est hsr_agoraphobia_lo hsr_agoraphobia_hi
#>                  <dbl>              <dbl>              <dbl>
#>  1                1.05              0.676               1.41
#>  2                1.42              1.05                1.79
#>  3                1.97              1.60                2.34
#>  4                1.23              0.861               1.60
#>  5                1.60              1.23                1.97
#>  6                1.05              0.676               1.41
#>  7                1.05              0.676               1.41
#>  8                1.05              0.676               1.41
#>  9                1.97              1.60                2.34
#> 10                1.05              0.676               1.41
#> # ℹ 401 more rows

What the reference group is

The mean, standard deviation and reliability behind every number above are shipped as hitopsr_devstats, transcribed from Table 1 of the HiTOP-SR introduction paper.

hitopsr_devstats
#> # A tibble: 93 × 8
#>    Scale          camelCase type  nItems reliability reliabilityType  mean    sd
#>    <chr>          <chr>     <chr>  <int>       <dbl> <chr>           <dbl> <dbl>
#>  1 Agoraphobia    agorapho… scale      5        0.86 alpha            1.62  0.77
#>  2 Antisocial Be… antisoci… scale      8        0.86 alpha            1.07  0.25
#>  3 Appearance Fo… appearan… scale      5        0.81 alpha            1.72  0.68
#>  4 Appetite Loss  appetite… scale      3        0.8  alpha            1.53  0.68
#>  5 Binge Eating   bingeEat… scale      3        0.83 alpha            1.65  0.79
#>  6 Bodily Distre… bodilyDi… scale      6        0.85 alpha            1.84  0.74
#>  7 Body Dissatis… bodyDiss… scale      4        0.88 alpha            2.31  0.94
#>  8 Callousness    callousn… scale      6        0.84 alpha            1.47  0.55
#>  9 Checking       checking  scale      5        0.88 alpha            1.8   0.77
#> 10 Cleaning       cleaning  scale      6        0.82 alpha            1.53  0.58
#> # ℹ 83 more rows

That reference group is the paper’s Development Sample 2, N = 780 Prolific Academic participants stratified by sex and age to approximate a community-representative United States population. It is a development sample, and not a community norm. No census weighting was applied and the paper publishes no raw-score to T-score table. So an interval here says where a score sits relative to the sample the instrument was developed on; it does not say what percentile that score occupies in any population.

Three further limits are worth knowing before you report one of these intervals.

  • The interval is symmetric and the same width for every respondent on a scale, which is what classical test theory implies, and it is not clipped to the 1-4 response range. On a strongly skewed scale – Conversion Symptoms, say, whose mean is 1.12 – a bound can therefore fall below 1.
  • The coverage the method demonstrates is across a population of respondents: about level of the intervals contain the true score when respondents are drawn from the reference distribution. It is not a guarantee for any one respondent.
  • A scale scored from fewer than its full items – from a module form, or from data scored with missing = "available" – is not on the same footing as the reference statistics, which come from complete scales. interval_hitopsr() receives scores rather than items, so it cannot tell such a column from a fully scored one; treat the interval as not comparable.

Scoring Only Some Scales

Not every study administers all 405 items. The package can build, field, and score a chosen set of the instrument’s scales; that whole workflow has its own walkthrough in Building HiTOP-SR Modules.

Renaming Item Columns

If your dataset was collected before the item numbers were standardized or used arbitrary variable names, you must rename your columns to the standard format before running the scoring engine. The rename_hitopsr_items() function handles this preparation via two approaches.

Method 1: Legacy “Original” Pool Names

If your columns are labeled with older pool names (e.g., HiTOP_659 or Ext_432), use method = "original". The function scans your data frame columns, matches them against the package’s built-in item database, and renames them to standard names.

# Setup a mock dataset with older legacy column names
legacy_data <- data.frame(
  HiTOP_659 = c(1, 2, 4),
  HiTOP_301 = c(3, 4, 2),
  ParticipantID = c(101, 102, 103)
)

# Rename legacy columns using the native pipe
standardized_legacy <-
  legacy_data |>
  rename_hitopsr_items(method = "original", prefix = "HSR_")
#> Warning: Only 2 out of 405 HiTOP-SR items were successfully matched and renamed.
#>  Note: If you plan to use `score_hitopsr()`, ensure uncollected items exist in
#>   the data frame as `NA` columns.

standardized_legacy
#>   HSR_001 HSR_002 ParticipantID
#> 1       1       3           101
#> 2       2       4           102
#> 3       4       2           103

Method 2: Matching via Literal Item Text

If your dataset uses completely customized column names but you tracked the exact question prompts presented to your participants, use method = "text". Provide a vector of your current column names alongside a matching vector of literal item text strings.

# Setup a mock dataset with completely custom column names
custom_data <- data.frame(
  q_party = c(1, 2, 1),
  q_flawless = c(4, 3, 4),
  Age = c(21, 25, 30)
)

# Define the mapping pairs
my_cols <- c("q_party", "q_flawless")
my_texts <- c(
  "I preferred to stay home than to go to a party.",
  "I felt that my work must be flawless."
)

# Rename custom columns based on text matching
standardized_custom <- custom_data |>
  rename_hitopsr_items(
    method = "text",
    item_cols = my_cols,
    item_text = my_texts,
    prefix = "HSR_"
  )
#> Warning: Only 2 out of 405 HiTOP-SR items were successfully matched and renamed.
#>  Note: If you plan to use `score_hitopsr()`, ensure uncollected items exist in
#>   the data frame as `NA` columns.

standardized_custom
#>   HSR_001 HSR_002 Age
#> 1       1       4  21
#> 2       2       3  25
#> 3       1       4  30

Note: Because these small mock examples contain only some of the full item pool, rename_hitopsr_items() will safely issue a cli warning letting you know that fewer than 405 items were matched. This perfectly accommodates researchers who intentionally administer only part of the instrument.

Labelling Columns

Column names like hsr_001 and hsr_agoraphobia are compact but say nothing about what they hold. The label_hitopsr() function attaches a label attribute to each column it recognizes: the literal item text for item columns, and the printed scale name for scored columns. Tools that read that attribute — data viewers and reporting packages — can then show the wording instead of the column name, so the labels travel with the data rather than living in a separate lookup table.

Which columns it recognizes depends on prefix, which must match how your item columns are actually named. The default, "hsr_", is how both example datasets and the package’s REDCap export name them (hsr_001 to hsr_405), so sim_hitopsr needs no prefix; data collected through the package’s Qualtrics export is named HSR_001 to HSR_405 and would pass prefix = "HSR_". The number must be zero-padded to three digits: columns carrying the prefix and a number without the leading zeros are not labelled, and the function warns and names them.

data("sim_hitopsr")
labelled_items <- label_hitopsr(sim_hitopsr, target = "items")
attr(labelled_items$hsr_001, "label")
#> [1] "I preferred to stay home than to go to a party."

Set target = "scales" to label the output of score_hitopsr() instead. Here prefix is the one the scoring function put on its output columns. Both functions default to "hsr_", so a scored frame built with default settings labels with none given:

sim_scores <- score_hitopsr(sim_hitopsr, items = 1:405, append = FALSE)
labelled_scales <- label_hitopsr(sim_scores, target = "scales")
attr(labelled_scales$hsr_agoraphobia, "label")
#> [1] "Agoraphobia"

Columns the function does not recognize are returned untouched, and if no column matches the prefix at all it says so with a warning rather than silently returning the data unchanged.