score_pid5() scores the PID5BF+M. version = "BFPM" scores the
36-item modified brief form of Bach et al. (2020). It returns 18 facets of
2 items each and 6 domains, Anankastia among them, as item means on the 0
to 3 scale. Each domain is the mean of its 3 facets. The form's own key
sums the items, so its values are twice these on complete data. Under the
default missing = "apa", any missing item makes its facet NA. With
whole-number responses this is the same output as missing = "complete". reliability_pid5(),
rename_pid5_items() and label_pid5() also take version = "BFPM". Its
item columns are pid5bfpm_01 to pid5bfpm_36. reliability_pid5()
returns the 18 facets and then the 6 domains. The keying is in a new
pid_items$BFPM column and a new pid_scales$BFPM element. A new
dataset, pid_bfpm_domains, maps each domain to its 3 facets.
validity_pid5(), norm_pid5() and plot_pid5() do not take the new
version.
score_pid5() scores the PID-5 Informant Form. version = "IRF"
scores the 218-item informant-report form of Markon et al. (2013), on
which an informant rates the person receiving care. It returns the full
form's 25 facets and 5 domains under the same column names. The form has
no counterpart to self-report items 96 and 177, so its Anxiousness and
Suspiciousness facets each have one item fewer. Fourteen items are
reverse-scored, the ones the APA key's Facet Table marks; the key's Step 1
list also names items 98 and 176, which are not reversed. The informant key
says to "round up" a fractional prorated raw score. The package applies the
nearest-whole-number rule of the other APA PID-5 keys, so informant and
self-report facets prorate the same way. reliability_pid5(),
rename_pid5_items() and label_pid5() also take version = "IRF", and
label_pid5() labels items with the informant wording. Item columns are
pid5irf_001 to pid5irf_218. pid_items gains an IRF column (each
informant item's number) and a TextIRF column (its wording), and
pid_scales gains an IRF element. pid_items now has 18 columns. IRF sits after BFPM, so the columns from
Reverse to Text each move one place right; code that reads pid_items
by position needs updating. Informant scores carry the full form's column
names, so do not norm or plot them as version = "FULL". validity_pid5(), norm_pid5() and
plot_pid5() do not take the new version.
score_pid5() scores the PID-5 Forensic Faceted Brief Form.
version = "FFBF" scores the 100-item PID-5-FFBF of Niemeyer et al.
(2022), an adaptation of the faceted short form for people in prison, with
self-report and informant versions. It returns 25 facets, named and ordered
as the SF's, then the 5 APA domains and the two forensic domains of the
paper's four-factor solution, Disinhibited Aggression and Insecurity. Items
12 and 26 are reverse-scored. The paper prints no missing-data rule, so the
default missing = "apa" applies the SF's rule; the authors' analysis code
scores missing items differently, as the help page explains. Informant data
use the same version, after the user averages two informants item by item
as the paper did. reliability_pid5(), rename_pid5_items() and
label_pid5() also take version = "FFBF". Item columns are
pid5ffbf_001 to pid5ffbf_100. Under method = "text",
rename_pid5_items() matches any of the form's four texts, English or
German, self or informant, and refuses a call in which two columns match
the same item. label_pid5() labels items with the English
self-report text. Two new exported tables hold the form:
pid_ffbf_items (each item's facet, reverse flag and four texts) and
pid_ffbf_domains (the 7 domains and their facets). pid_scales gains an
FFBF element and now has 6 elements. The PID-5 functions take only
full version names, so spell out "FULL" (see Breaking changes).
validity_pid5(), norm_pid5() and plot_pid5() do
not take the new version. FFBF facet and APA domain columns carry the short
form's names, so do not norm or plot them as version = "SF".
New PID5BF+M forms. generate_docx_pid5bfpm(),
generate_qualtrics_pid5bfpm() and generate_redcap_pid5bfpm() write the
36 items in BF+M order with their PID-5 text, and the PID-5 instructions
and 0 to 3 response options. The Word scoring page lists the 2 items of
each of the 18 facets, then a second table names the 3 facets that each
of the 6 domains averages. The Qualtrics question IDs are PID5BFPM_01 to
PID5BFPM_36. The REDCap fields are pid5bfpm_01 to pid5bfpm_36, the
names rename_pid5_items() and label_pid5() use with
version = "BFPM". Ready-made Word (US and A4),
Qualtrics and REDCap files are on a new PID5BF+M download page. The online
form does not offer the PID5BF+M.
New PID-5 Informant Form forms. generate_docx_pid5irf(),
generate_qualtrics_pid5irf() and generate_redcap_pid5irf() write the
218 items in the form's order with the informant wording, the form's
instructions ending in its rating prompt and the stem "He or she…", and
its 0 to 3 response options. The Word scoring page lists each facet's
informant item numbers with (R) on the 14 reverse-scored items, and the
Word footer carries the APA copyright and permission notice the form
prints. The Qualtrics question IDs are PID5IRF_001 to PID5IRF_218. The
REDCap fields are pid5irf_001 to pid5irf_218, the names
rename_pid5_items() and label_pid5() use with version = "IRF".
Ready-made Word (US and A4), Qualtrics and REDCap files are on a new
PID-5 Informant Form download page. The online form does not offer the
informant form.
New PID-5 child forms for ages 11 to 17. generate_docx_pid5child(),
generate_qualtrics_pid5child() and generate_redcap_pid5child() write
the APA's 220-item PID-5 child form, and generate_docx_pid5bfchild(),
generate_qualtrics_pid5bfchild() and generate_redcap_pid5bfchild()
write its 25-item brief child form (Krueger et al., 2013). The child forms
print the adult forms' items in the same order with the same scoring key.
So the new forms carry the adult items, the child instructions and the 0
to 3 response options. The Word footer carries the APA notice that the
child forms print. score_pid5() scores them with version = "FULL" and
version = "BF". The Qualtrics question IDs and REDCap fields are the
adult forms' (PID5_001 and pid5_001 to 220, PID5BF_01 and
pid5bf_01 to 25), so child data need no renaming before scoring. One
REDCap project therefore cannot hold both the adult and the child
instrument of a form. Ready-made Word (US and A4), Qualtrics and REDCap
files are on two new download pages. The package has no child norms, and
the online form does not offer the child forms.
The reliability_*() functions report omega as NA for a scale with
fewer than 3 items. A one-factor model of 2 items is not identified, so
no model is fitted. No PID-5, HiTOP-SR or HiTOP-BR scale had fewer than 3
items before the PID5BF+M, so no earlier result changes. calc_omega()
itself is unchanged.
"A Google Sheet" is a choice under "Where responses go" in the Study
Link Builder. The menu now lists "A file on the participant's device",
"A Google Sheet", "Another web address" and "A Supabase table". "A Google
Sheet" shows the setup steps, a "Copy the script" button that copies the
Apps Script code, and a "Web app URL" field. That field refuses an address
whose host is not script.google.com or whose path does not end in
/exec, such as the sheet's own address. Both web choices make the same
link as "A web address" did, so old links still work. A link with a web
app's address opens with "A Google Sheet" chosen.
"HiTOP-SR module" is an instrument choice in the Study Link Builder.
Each instrument row offers "HiTOP-SR module (scales you choose)" after
the HiTOP-SR. A row set to it shows the "Module file" box and "Choose the
module file" in that row. The optional section that held them is now
"Item order", with only "Show the items in a random order". The link is
the same as before: the module row writes hitopsr with the module file.
A list cannot hold both a HiTOP-SR row and a module row, or two module
rows. "Make the link" refuses a module row whose box is empty. An opened
link with a module fills a module row, and one with a module and no
HiTOP-SR is refused.
A hitop-form study link is no longer limited to 50 questions. A
setup holds as many questions as fit in the 100,000 bytes that the online
form reads. A link without consent text or questions is now held to the
same 100,000 bytes, and the online form refuses a larger one and names
its size. With a Supabase table, the Study Link Builder makes no link for
a table of more than 1,600 columns, PostgreSQL's limit, and names the
count. If a link is longer than 8,000 characters, the builder shows a
warning under it. The warning is drawn empty first and its text written
in the next animation frame, so that a screen reader can announce it.
The warning says that some sites and mail programs cut long links. For a
link that carries its setup, it says that you can keep the setup in a
file you host. For a link that names a setup file, it says that the
file's address makes the link long. The builder makes no link that
counts more than 8,177 characters after the host name. When the online
form is not in the cache that serves GitHub Pages, its host, GitHub
refuses a longer one. The count
takes each Prolific ID as 24 characters, and a Prolific link counts the
three IDs once more, as Prolific's "I'll use URL parameters" option can
append them. SONA's %SURVEY_CODE% counts as 7 characters, its longest
code. The refusal names the count, and for a link that carries its
setup, it says to choose "In a file I host".
A hitop-form study link can name a setup file you host. Under "Where
the setup is kept", the Study Link Builder offers "In the study link", the
default, and "In a file I host". The second shows "Download the setup
file", which saves the setup as setup.json for you to host, for example
in a public GitHub repository. The builder fetches the hosted file, and
when it matches the form, makes a link whose setup parameter names the
file's address and whose sha256 parameter holds its fingerprint. The
online form fetches the file. If the setup in the file changed after the
link was made, the online form refuses it. A change of spaces or line
endings between the values alone is not refused. Opened on the builder,
such a link fills the form from the file. A link's participantParam can
no longer be setup or sha256.
The hitop-form online form speaks to the participant. An error screen gives one sentence: check the connection and reload, open the link in another browser, or contact the study team. Each screen ends with a closed "Details for the study team" section. It holds the technical reason on an error or failed-send screen, and the build date and package version on every screen after the instrument files load. Outside that section, no screen shows a host name, and a completion link reads "Continue to the next step of the study". With a send address, the start screen says that an unconfirmed send saves the answers as a file. The identifier field has a hint, asks a phone keyboard not to correct it, and starts the form on Enter. Each item page shows its page, and its part under a list, above the items and beside "Next" or "Finish". A closed "Instructions" section repeats the instructions. After a press of "Next" or "Finish", each blank item is marked "Please answer this item", with a count of blank items above the first. Each option is at least 44 px high. "I do not agree" asks once to confirm. A send shows "Sending your answers. Please keep this page open.". An unconfirmed send's screen is headed "Your answers were not sent". It says that no confirmation came that the answers reached the study team.
The hitop-form Study Link Builder shows its required choices first.
The page shows the instruments, the study name and where responses go
first. Five optional sections follow, each closed until you open it:
"Participants and recruiting site", "Item order",
"Consent", "When the participant finishes" and "Your own questions". A
section's summary reads "Not used" or lists its fields that hold a value.
When the page refuses a field, it opens the field's section and moves
focus to the field. Move and Remove buttons are disabled where they do
nothing. The built link appears under "Your study link", with a "Copy the
link" button and one next step for the choices made. The hints are
shorter, and they link to the hitop-form README for detail. The site's
Instruments menu names the two pages "Module Builder" and "Study Link
Builder". The online-collection and HiTOP-SR modules articles use the
name "Study Link Builder", and call the .json file that write_module()
writes the module file.
The hitop-builder Module Builder starts short and leads to the Study
Link Builder. The page's opening text is short. The list of hosts it
loads from and R's log sit in a closed "Technical details" section, which
a failed load or build opens. Each scale's checkbox name ends with its
item count. A "Definition" button on each row opens the scale's
definition by click or keyboard, in place of the popup that opened on
hover. A filter that matches no scale says so. Each format has one name,
"Word form", "Qualtrics file", "REDCap dictionary" or "Online form", on
its card, its download button and its build status. The three zip file
formats also use it in the README.txt title. The step buttons name the
step they lead to, "Next: Choose a format and download" and "Back: Choose
scales". In Chromium, a mouse click on one no longer draws a focus ring on
the new step's heading, and a key press still does. After an Online form
save, a "Next: make the study link" panel holds a link that opens the
Study Link Builder in a new tab, with the module filled in. In the Study
Link Builder, you can choose the module file as a file as well as paste
its text.
A Word module form can carry subscale scoring rows.
generate_docx_hitopsr() accepts include_subscales = TRUE with a
module. The scoring page then lists the subscales of the scales the
module holds, and no others, sorted by name with the scale rows. Each
subscale row gives its items in the form's printed numbers. The call used
to stop with an error.
HiTOP-SR scoring can include the 17 subscales. score_hitopsr() and
reliability_hitopsr() take include_subscales = FALSE. With TRUE,
score_hitopsr() adds one column per subscale in hitopsr_subscales, such
as hsr_cynicism, after the scale columns. reliability_hitopsr() adds one
row per subscale after the scale rows. With a module, only the subscales
whose parent scale is in the module are included. label_hitopsr() labels
the subscale columns, and interval_hitopsr() converts them. The new
argument breaks two kinds of call, listed under Breaking changes.
hitop-form can give two or three instruments in one session. A study
link's instruments field lists them in order, such as
["hitopbr", "pid5bf"], in place of instrument. A list holds at most one
form of the PID-5 and names no instrument twice, and a module descriptor
applies to its HiTOP-SR. The page gives each instrument in turn, each with
its own start screen ("Part 2 of 3") and pages. The instruments come
between any questions before and after the form. It saves one row with
one group of item columns per instrument, in the list's order, in the
shape read_form_responses() reads. Score each instrument in its own
call, with its columns chosen by stem. The link builder gains "Add an
instrument" and an ordered list of up to three rows, and its Supabase SQL
holds every instrument's columns. The online-collection article's section
"Several instruments in one session" describes the screens and the file.
hitop-form can ask the researcher's own questions. A study link's
questions field holds a before list, an after list or both. The
page asks the before questions on a screen of
its own ahead of the start screen, after any consent screen, and the after
questions on a screen of its own after the last page of items. A question
is a one-line text, a whole number with an optional minimum and maximum,
one choice of 2 to 20 options, or any number of such choices, and it can
be required. Each answer is written as text in a column named q_ and
then the question's name, after the item columns, in the row, the file and
the Supabase table. A choice is written as the chosen option's number,
counted from 1, and several choices as those numbers in ascending order,
separated by spaces. An unanswered question is an empty cell, which
read_form_responses() reads as NA. The link builder gains a question
editor, and a link with questions travels as a z parameter. The
online-collection article has a section on the questions.
The hitop-form link builder reads the questions from a spreadsheet
file. "Load questions from a file" reads a CSV file saved as "CSV
UTF-8" and puts its questions in place of the editor's. The file stays in
the browser. Its first row names the columns list, name, text and
type, and optionally options, required, min and max. Each
further row is one question, with its options separated by |. The
builder refuses a file with a fault and names the fault. A fault in one
cell is named by its row and column, or by its row and field number in the
header row or past the last column. "Download these questions" saves
the editor's questions as such a file, and "Download a template" saves one
with an example question of each type.
read_form_responses() reads answer columns named q_. A column
after submitted whose name starts with q_, such as q_age, is read as
the answer to a researcher's own question. The result places these columns
after the item columns, as character with a blank cell as NA, in the
order the reader first meets them. Files with different answer columns read
together, with NA where a file lacks a column, and the answer columns do
not enter the check that every file holds the same item columns. A name
that starts with q_ and does not match ^q_[a-z][a-z0-9_]{0,29}$ is an
error naming the file and the column. A file with no column whose name
starts with q_ reads as before.
read_form_responses() reads a file whose item columns span two or more
instruments. The item columns form one group per instrument, the columns
of each group side by side. The instrument cell holds the stems in the
order of the groups, joined by single spaces (hitopbr pid5bf), and the
form_build cell holds one build date per instrument in the same order
(2026-09-20 2026-09-18). Under a random order, item_order holds one
group of item numbers per instrument, joined by |. The reader refuses
an instrument cell that differs from the stems, a stem whose columns
another stem's columns split, a form_build cell whose date count differs
from the stem count, and an item_order cell that breaks the groups. It
names the file, and the row for a fault in a row. Score each instrument in
its own call, with its columns chosen by stem. form_build is now
character in every result, holding each row's dates as written, where it was a Date before. Use
as.Date() on a single-instrument file's form_build when you need a
date. A file of one instrument reads as before in every other column.
hitop-form can show the researcher's consent text before the form.
The link builder gains a "Consent text" box. A link with text there
carries a consent field, and the page shows the text as plain text on a
screen of its own before the start screen, with an "I agree" and an "I do
not agree" button. "I do not agree" shows a closing screen with no way
back to the form, and the page sends no answer and saves no file. A "Declined text" box sets what that screen
says. A completeDeclined address, from the builder's "Completion URL
after a decline" field, sends a participant who declines to a recruiting
site's address for that outcome. The text can hold up to 20,000
characters. A link with consent text travels compressed, as a z
parameter in place of c, and the page reads either. participantParam
now refuses z, as it refuses c. The rows, the file and the Supabase
table keep the same columns. The online-collection article has a section
on consent text.
hitop-form takes the participant identifier from SONA, CloudResearch
Connect or another recruiting site. The link builder's "Recruiting
site" menu replaces the "Recruit through Prolific" box and offers None,
Prolific, SONA, CloudResearch Connect and Another site. A site other than
Prolific puts a participantParam field in the link, naming the address
parameter that carries the identifier: id for SONA, whose link then ends in
&id=%SURVEY_CODE%, participantId for Connect, or a typed name. The page
takes the participant identifier from that parameter, and asks on its
start screen when the value is missing, blank, or an unfilled %…% or
{{…}} placeholder. No column is added. A completion URL may hold
{participant} after its ? or #, and the page replaces it with the
identifier, so SONA's completion URL can carry each participant's survey
code. A completion URL is refused if it holds {participant} in its
host or path, or holds a spelling the page would not fill, such as
{Participant} or {{participant}}. The online-collection article has a
section on the SONA and Connect routes.
The instrument download pages gain an online-form strip and keep three cards per row. The five download pages with a JSON export (HiTOP-SR, HiTOP-BR, PID-5, PID-5-SF and PID-5-BF) show their Word, Qualtrics and REDCap cards in one row of three. Under the row, a strip titled "Online Form" holds three links. A "Make a study link" button opens the hitop-form link builder with that instrument chosen. The JSON export is a download button with its build badge. A text link opens the online-collection article. The HiTOP-HSUM page has no JSON export and no strip. The Instruments menu gains "Make a study link" after "Build a HiTOP-SR Module". The overview page lists the four steps from instrument to scores under its instrument cards.
The hitop-form link builder fills its fields from a study link.
Opening link.html with a study link's c parameter fills the
builder's fields from the link, so a link can be edited and made again,
and a page can hand a module over the same way with a c that carries
only the instrument and the module. A c the page cannot read, one that
does not hold a form, or one naming an instrument the builder does not
offer is refused in a message naming the c parameter, and no field is
filled from it. Above the form, three steps name the route from instrument
to link, with links to the Module Builder and the online-collection
article.
The hitop-form page confirms each press of "Save the file". A press
of the button on a saved-file screen shows "The file was saved again."
under the button, in a role="status" region, which screen readers are
meant to announce. The page does not move focus, so after a keyboard
press focus stays on the button. The region is on the page,
empty, before the first press. A second press saves the file again and
writes the sentence afresh.
The hitop-form page lets a participant save the file again. Each screen that names a saved file now shows a "Save the file" button under its instructions. Those screens follow a Finish with no store in the link or a send the store did not confirm. The instructions end with "If the file did not appear, press Save the file." A press saves the same file again, with the same content, offered under the same file name (the browser can add a number to keep both copies). The download after an unconfirmed send starts only when the wait ends, up to 30 seconds after Finish. The online-collection article mentions the button in its "Make the study link" step and in its Prolific route.
The hitop-form page gives each Prolific outcome its own end. The
link builder's "Completion URL after a saved file" field, allowed only
beside a completion URL, names a second https:// address that the
saved-file screens link to in place of the completion URL, so a study
can give a saved file its own Prolific completion code. A confirmed send
now shows the sent screen, with a link to the completion URL in place of
"You can close this page.", before it sends the participant there. A
Prolific parameter the address carries twice, as when Prolific's "I'll
use URL parameters" option appends the three to a link that already ends
in the builder's placeholders, reads as its first value that is neither
blank nor a placeholder. The online-collection article's Prolific
section describes all three.
The hitop-form page fits a Prolific study. The link builder's
"Recruit through Prolific" box makes the page take each participant's
identifier from the Prolific ID in the page's address and write the study
and session identifiers into prolific_study and prolific_session,
which read_form_responses() reads. The builder prints the link with
Prolific's three placeholders, to paste as the study URL. A "Completion
URL" field names an https:// address the page sends the participant to
after a confirmed send, and links to after a saved file. The
online-collection article's Prolific section walks the route, and the
reader's tests read one such file.
read_form_responses() accepts two Prolific columns. A response file
may hold prolific_study and prolific_session, either or both, anywhere
after submitted. They hold the study and session identifiers that
Prolific adds to a study link, recorded for a study recruited through
Prolific. The result places prolific_study seventh and
prolific_session eighth, as character, and a row from a file without a
column holds NA in it, as does a blank cell. Neither is an item column,
so neither enters the check that every file holds the same item columns,
and the scoring functions do not read them. Every result now has eight
lead columns, and the item columns start ninth. Code that selects the item
columns by name, as the articles and the vignettes do, needs no change.
The hitop-form page can show each participant the items in a random
order. The link builder's "Show the items in a random order" box asks
for it. The file and the row then keep the item columns in the
instrument's order under the official item names and record the order
shown in item_order, which read_form_responses() reads. The help page,
the online-collection article, the modules article and
vignette("pid5_scoring") describe the column, and the reader's tests
read one such file.
read_form_responses() accepts an item_order column. A response
file may hold a column named item_order anywhere after submitted. It
records the order the participant saw the items, as item numbers joined by
single spaces. The result places it sixth, after submitted and before the
item columns, as character, and a row from a file without the column holds
NA there. It is not an item column, so it does not enter the check that
every file holds the same item columns, and the scoring functions do not
read it. A cell that is not blank and does not list the file's item
numbers, each once, is an error naming the file and the response row.
A REDCap or Qualtrics module export scores from its descriptor alone.
The descriptor that generate_redcap_hitopsr(descriptor = ) or
generate_qualtrics_hitopsr(descriptor = ) writes now has a columns
field: the names the generated file gives the items, one per module item,
in ascending item-number order. These are the REDCap dictionary's field
names, or the Qualtrics questions' [[ID:]] values. A data export with the
item variable names as column headers uses these names. Export Qualtrics
data as numeric values, not choice text, to score it. read_module()
returns the field on the module's columns attribute, and
write_module() writes that attribute back. score_hitopsr() and
reliability_hitopsr() now use those names when items is omitted or
NULL. A supplied items is always used. Omitting items is an error
with no module, with a module that has no columns, or under
layout = "printed". The Word descriptor has no columns field. The
format stays "1.0": a "1.0" reader ignores fields it does not know, so
releases from 0.2.0 on still read the new files. A bad columns field stops
read_module() with an error of class hitop_module_file_bad_columns.
read_form_responses() reads a store's download too. A file it reads
may hold one or more response rows, as the CSV download of a Google Sheet
or a Supabase table that the hitop-form page sends to does, one row per
participant. Every row of every file is a row of the result, the files in
path order and the rows in file order. A file with a header and no response
row is an error naming the file. The new article "Collecting Responses
Online with hitop-form" walks the Google Sheet route from the study link to
the scores, and the installed example responses-sheet-hitopbr.csv is one
such download.
read_form_responses() reads the files the hitop-form web page saves.
Given a directory or a vector of file paths, it reads each participant's CSV
file and binds them into one tibble: study, participant,
instrument and form_build as character, submitted as a UTC
date-time, then the item columns as integers, in the order the first file
(after sorting the paths) holds them. The scoring functions take the result
as it is, with the item columns passed through items by name for a full
form, or by name or position with layout = "printed" for a module the page showed
in a shuffled order. Files whose item
columns differ in name, count or order stop the read with an error of
class hitop_form_responses_mismatch naming the files; a directory holding
no .csv file stops it with hitop_form_responses_none. The modules
article shows the whole hand-off, from a study link to scored scales.
Files saved from the PID-5, PID-5-SF and PID-5-BF read the same way, with
the value 0 kept as 0: pass the item columns to score_pid5() by name with
the matching version. The "Scoring the PID-5" vignette shows this hand-off.
The three PID-5 forms ship as JSON exports. pid5.json, pid5sf.json
and pid5bf.json are downloadable from each form's page on the package
website. Each file holds the items of that form, the four response options
and the administration instructions as the package's tables hold them. The
package version and the build date are at the top. The item names are
pid5_001 to pid5_220, pid5sf_001 to pid5sf_100 and pid5bf_01 to
pid5bf_25. The files carry no keying. Each has a row in
hitop_artifacts. A web form outside the package can read one to show the
questionnaire online.
The HiTOP-SR and HiTOP-BR ship as JSON exports. hitopsr.json and
hitopbr.json, downloadable from each instrument's page on the package
website, hold the form's items, response options and administration
instructions as the package's tables hold them, with the package version and
build date at the top. They carry no keying. Each has a row in
hitop_artifacts. A web form outside the package can read one to show the
questionnaire online.
score_hitopsr() and reliability_hitopsr() score data entered off a
shuffled form. Both gain a layout argument. The default,
"instrument", is the existing behavior: item columns in ascending
HiTOP-SR order. layout = "printed" takes columns in the order a shuffled
Word form printed its items, column k holding the answer to printed item k.
The function puts them back into instrument order through the module's
item_order attribute. A descriptor written by generate_docx_hitopsr()
with randomize = TRUE records that order, and read_module() returns it.
The hand reorder the help pages and the modules article used to recommend
is no longer needed. Under layout = "printed", a call with no module, a
module with no item_order, or an item_order that is not a permutation
of the module's items is an error. The message names the argument and says
how to get an order.
label_pid5() attaches PID-5 item text and scale names to columns. Given
a data frame and the form its columns belong to, target = "items" attaches
each item's questionnaire prompt to that item's column as a label
attribute, and target = "scales" attaches each scale's display name to the
columns score_pid5() writes -- the facet and domain names for the full and
short forms, the domain and total names for the brief form. The column-name
prefix defaults to the form's own stem for items (pid5_, pid5sf_,
pid5bf_) and to pid_ for scales. An item column carrying the prefix and a
number that is not one of the form's expected names is left unlabelled and
named in a warning of class hitop_unpadded_items, as the two HiTOP
labelling functions do.
rename_pid5_items() renames PID-5 item columns to the standard names.
Given a data frame and the form its items belong to, it renames the item
columns to pid5_001-style names: method = "number" (the default) reads the
item number out of a column already named pid_1, pid_2 and so on, the
spelling this package's own PID-5 datasets carried before they were renamed to
match the exports, and method = "text" matches the literal item prompts in
pid_items$Text for callers whose data came from elsewhere. Item text, and
columns spelled like an item number but numbered outside the form, are left
alone and named in a warning of class hitop_unmatched_items; a column that
looks like no item at all is left alone without comment.
interval_hitopbr() puts a confidence interval around a HiTOP-BR scale
score. Given columns that score_hitopbr() produced, it returns an _est,
_lo and _hi column for each: an estimate of the respondent's true score
and the bounds of a confidence interval around it, by the regression-based
approach with scale correction of Schmukle (2026). The reference mean,
standard deviation and reliability come from the new hitopbr_devstats
dataset.
hitopbr_devstats ships the HiTOP-BR development-sample statistics. One
row per scale, carrying the item count, Cronbach's alpha, mean and standard
deviation printed for it in the HiTOP-SR introduction paper's Table 1. 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 says where a score
sits relative to the sample the instrument was developed on, not what
percentile it occupies in any population.
calc_se is deprecated in score_pid5(), score_hitopsr() and
score_hitopbr(). Calling any of the three with calc_se = TRUE now warns.
The argument, and the _se columns it adds, will be removed in a future
release; the deprecation adds a warning and moves no value they hold. (The
HiTOP-BR item-36 rekey under Breaking changes below does move
hbr_detachment_se and hbr_internalizing_se.) The number was never
a standard error of measurement — no reliability estimate entered it — so it
never gave a confidence interval for a respondent's true score. Use
interval_hitopsr() or interval_hitopbr() for that. The PID-5 has no
interval function in this package; for measurement precision on it, see
reliability_pid5(). The warning carries the condition class
hitop_deprecated_calc_se, so a caller who wants the columns without the
notice can silence it by class.The PID-5 functions take only full version names. score_pid5(),
reliability_pid5(), rename_pid5_items(), label_pid5(),
validity_pid5(), norm_pid5() and plot_pid5() accept version as a
full name in any letter case, such as "FULL" or "bf". Before, an
abbreviation selected a version where it was unique among that
function's names: "S" selected the SF in all seven functions, "BFP"
the PID5BF+M in the four that take it, and "F" and "B" the FULL and
BF in validity_pid5(), norm_pid5() and plot_pid5(). Each is now an
error, so write the full name. Any refused version is an error of class
hitop_unknown_version, which names the argument, the value given and
the accepted names. Before, the error came from match.arg(). Its
message called the argument 'arg', and it did not name the function.
rename_pid5_items(method = "text") refuses two columns that match one
item, for every form. Both columns would take the same name. Before,
only the PID-5-FFBF refused, and the other forms returned two columns
with one name. The error now has the class hitop_duplicate_item_match
and names the columns.
A HiTOP-SR module edited by hand is refused. score_hitopsr(),
reliability_hitopsr(), generate_docx_hitopsr(),
generate_qualtrics_hitopsr(), generate_redcap_hitopsr() and
write_module() rebuild a module with hitop_module() from its
instrument and scales. They stop with an error of class
hitop_module_mismatch when its items, nItems, camelCase or
instrument differ from that build, or when it cannot be rebuilt, as when
instrument names another instrument or a scale name is unknown. The
error names the
items the module lacks or holds outside its scales, and each field that
differs. Before, scoring refused a module whose nItems disagreed with its
items. A module lacking an item of one of its scales, with its nItems
lowered to match, scored that scale from its other items with no warning.
If the lacking item was a subscale item, scoring with
include_subscales = TRUE stopped instead with an internal error that
asked for a bug report. The Word form of a module lacking an item printed
NA in a scoring row. A module saved before a scale was
renamed, such as one naming Body Focus rather than Appearance Focus, is now
refused where it scored with that scale dropped. Build the module again
with hitop_module(), using the current scale names. The
deprecated hitop_subset() class and a module with double items are
accepted as before. write_module() already refused a module whose
items or nItems differed. It now also compares camelCase and
instrument. These refusals, and its refusal of a module that cannot be
rebuilt, now carry the class.
generate_docx_hitopsr() takes include_subscales as TRUE or FALSE
only, as score_hitopsr() does. A value such as 1, which used to add
the subscale rows to a full-instrument form, is now refused.
Two kinds of call to score_hitopsr() and reliability_hitopsr() now
fail. Both come from the new include_subscales argument. The first kind abbreviates items as i =. It now fails with an
error that the argument "matches multiple formal arguments", because i
also starts include_subscales. Write items = in full. A call that passes the
deprecated subset by position now fails, because include_subscales
takes that position and accepts only TRUE or FALSE. Write module =
instead.
Scoring refuses an item column it cannot read as numbers.
score_pid5(), score_hitopsr(), score_hitopbr(), reliability_pid5(),
reliability_hitopsr(), reliability_hitopbr() and validity_pid5() now
stop with an error of class hitop_nonnumeric_items when an item column is
not numeric, logical, or character text of numbers. Refused columns include
factors, dates, lists and choice text. Before, a
column of choice text such as "Not at all" scored as all NA with only a
base R coercion warning, and a factor scored as its level codes with no
warning. The error names the first five refused columns with each one's
class, shows the first value that does not parse in a character column, and
counts the rest. Numeric and logical columns score as
before. A character column of digits, such as a Qualtrics CSV export read
after its extra header rows are removed, also scores as before, with blank
cells counted as missing. The text "NA" in such a column is refused.
Two numeric column types that used to return wrong scores without a
warning now stop with the same error. An SPSS column (haven_labelled_spss)
that holds a value it declares missing, such as 99, used to score that code
as an answer. Turn such codes into NA with haven::zap_missing() before
scoring. A declared value that is blank is not refused, since blank cells
count as missing. The SPSS class of haven before 2.0 (labelled_spss),
whose declared codes also used to score as answers, is refused the same
way. haven::zap_missing() leaves that class unchanged, so the error says
to set the values its na_values or na_range attribute declares to NA.
A 64-bit integer column (integer64) read without bit64 loaded
used to score as tiny numbers near zero. Text that is not valid UTF-8, such
as a Latin-1 file read without its encoding, used to stop with a base R
error. In a UTF-8 session it is now refused with the same error, which
shows each invalid byte ("1<a0>") and names iconv(). Some text marked
Latin-1, such as "1" followed by a non-breaking space, also used to stop
with a base R error. Text marked Latin-1 is now read as its text.
A haven labelled column of digit
text, which used to stop with an error from haven, now scores as its plain
text does. A refused value made only of invisible characters, such as a
non-breaking space, is shown by its code points (U+00A0). An invisible
character inside a value with visible ones is shown the same way, so
"1" followed by a non-breaking space shows as "1<U+00A0>".
read_module() refuses item numbers that are not JSON numbers. In a
module descriptor's items, nItems and itemOrder fields, every value
must now be a JSON number with a whole value. A JSON string such as "12",
a JSON boolean, or a fraction such as 12.4 stops the read. Before, such
values were converted, and one that converted to the right item number read
as valid. A JSON null inside an array is refused as before. items and
nItems raise
hitop_module_file_items_mismatch, and itemOrder raises
hitop_module_file_bad_item_order. Whole numbers written as 2.0 or 3e0
still read. A file that write_module() wrote never holds the refused
values, but a hand-written descriptor can. Write its item numbers as
numbers.
write_module() refuses a module whose items do not match its scales.
It now rebuilds the module with hitop_module() from its instrument and
scales. If the module's items differ from the rebuild in value or
order, or its nItems differs in value, it stops with an error that names
the field. If the rebuild fails, for example on an unknown scale name, it
stops with the rebuild's error as the cause. In both cases no file is
written, and an existing file at the path is left unchanged. Before, some
such modules wrote a file that read_module() then refused. Others wrote a
file that read back, but recorded the items in an order other than the
rebuild's, or as strings. A module whose items
are doubles equal to the rebuild's is still written.
Every response value the package ships is an integer. The 405 item columns
of ku_hitopsr, the 45 of ku_hitopbr and the 100 of ku_pid5sf, and
hitophsum_choices$Value, are stored as integers where they were doubles; the
five sim_* datasets already were. No value moved: only the type differs, so
subsetting, == and arithmetic behave as before, and every score, validity
scale, reliability coefficient, interval and normed score these datasets
produce is unchanged. No distributed Qualtrics or REDCap file changed a byte.
What changes is identical(), which distinguishes an integer from a double: a
test written as identical(ku_pid5sf$pid5sf_001[[1]], 0) now fails and wants
0L (or expect_equal(), which ignores the difference). typeof(),
is.integer() and str() also report the new type. No deprecation period
precedes this change.
Every item number the package ships is an integer. pid_items' FULL,
SF, BF, INC, INCS, ORS, ORSS, PRD, PRDS, SDTD and SDTDS
columns, hitopsr_items$HSR, hitopbr_items$HBR and $HSR,
hitophsum_items$Item, the itemNumbers vectors and the item-number columns
of the nested itemdata frames in pid_scales, hitopsr_scales,
hitopsr_subscales and hitopbr_scales, and the items element of what
hitop_module() returns, are all stored as integers where they were doubles.
No value moved and no exported file changed: only the type differs, so
subsetting, == and arithmetic on these numbers behave as before, integer
overflow being unreachable at these magnitudes. What changes is
identical(), which distinguishes an integer from a double: a test written as
identical(hitopsr_scales$itemNumbers[["agoraphobia"]], c(1, 2, 3)) now fails
and wants c(1L, 2L, 3L) (or expect_equal(), which ignores the
difference). typeof(), is.integer() and str() also report the new type.
No deprecation period precedes this change.
The PID-5 example datasets now use the item column pattern the package's
own REDCap export writes. sim_pid5's item columns are now pid5_001 to
pid5_220; sim_pid5sf's, and the item columns of ku_pid5sf, are
pid5sf_001 to pid5sf_100; sim_pid5bf's are pid5bf_01 to pid5bf_25.
Only the names changed: every value and column position is as it was, and
ku_pid5sf keeps its response_id column. Each form carries its own stem
because the three forms number their items independently -- short-form item 5
is full-form item 16, and brief-form item 5 is full-form item 31 -- so one
shared stem would give one name to different items. The Qualtrics export
writes the same pattern with an uppercase stem: PID5_001, PID5SF_001,
PID5BF_01. All three forms previously shared one unpadded stem, so code
selecting the old names moves form by form: paste0("pid_", 1:220) becomes
sprintf("pid5_%03d", 1:220), paste0("pid_", 1:100) becomes
sprintf("pid5sf_%03d", 1:100), and paste0("pid_", 1:25) becomes
sprintf("pid5bf_%02d", 1:25); the PID-5 vignettes show the new idiom.
Selecting items by position (items = 1:220) is unaffected. The scoring
functions' prefix argument names the output score columns and still
defaults to "pid_", so on this instrument the item columns and the score
columns carry different stems. No deprecation period precedes this change.
The per-scale tables the package ships or returns now join on one column
shape. hitopsr_devstats and hitopbr_devstats name their display-name
column Scale, as available_scales(), the reliability_*() family and
every keying table already spelled it; it was scale. And
reliability_pid5(), reliability_hitopsr() and reliability_hitopbr()
return a camelCase column, holding the stem that names the scale's column
in the matching score_*() output, read from the keying table on the same
row as Scale; it sits second, between Scale and nItems, so nItems,
alpha and omega each move one position to the right. Code reading
hitopsr_devstats$scale or selecting reliability columns by position must
migrate; no reliability, interval or reference value moves. No deprecation
period precedes either change.
The HiTOP example datasets and the item-naming helpers now use one item
column pattern, the one the package's own REDCap export writes (the
Qualtrics export writes the same pattern with an uppercase stem, HSR_001
and HBR_01, which prefix = "HSR_" / "HBR_" matches). The item columns of ku_hitopsr and sim_hitopsr are now
hsr_001 to hsr_405, and those of ku_hitopbr and sim_hitopbr are
hbr_01 to hbr_45; only the names changed, every value and column
position is as it was. rename_hitopsr_items(), label_hitopsr() and
label_hitopbr() now zero-pad the item number to the instrument's width
(three digits for the HiTOP-SR, two for the HiTOP-BR) for every prefix,
and their default prefix is "hsr_" / "hbr_" rather than "HSR_" /
"HBR_". So rename_hitopsr_items() writes hsr_001 where it wrote
HSR_1; label_*() with a custom prefix against unpadded columns such
as HSR_1 no longer labels them (only items where padding makes no
difference, HiTOP-SR 100 and up and HiTOP-BR 10 and up, still match) and
warns, under the condition class hitop_unpadded_items, naming the
columns it skipped; and
label_hitopsr(x, target = "scales") with no prefix now matches
score_hitopsr()'s default output (likewise for the HiTOP-BR pair). Code
selecting the old names — sprintf("hsr%03d", 1:405) and
paste0("hsr_", 1:405) for the two HiTOP-SR datasets, sprintf("hbr%02d", 1:45) and paste0("hitopbr_", 1:45) for the two HiTOP-BR datasets — must
move to sprintf("hsr_%03d", 1:405) and sprintf("hbr_%02d", 1:45); the
vignettes show the new idiom. No
deprecation period precedes this change.
One HiTOP-BR item moved to the scale its development workbook gives it.
Item 36 ("I had a hard time asserting myself to others.") was keyed to
Detachment; the HiTOP Society development workbook this keying was built
from puts it under Internalizing, in its item-to-scale sheet and again in
its scoring syntax, and the instrument's introduction paper agrees in both its
descriptive table and its factor table. It was the only HiTOP-BR item where
the package and the workbook disagreed.
score_hitopbr() and reliability_hitopbr() therefore return different
values for hbr_detachment (now 5 items: 7, 12, 30, 31, 37) and
hbr_internalizing (now 8 items: 8, 9, 18, 22, 23, 36, 42, 44). No other
scale changes, and no item text, item number or response option changes.
Scores computed with an earlier version are not comparable for those two
scales. The scoring-key page of the two HiTOP-BR Word forms in inst/extdata/
has been rebuilt with the corrected item lists.
rename_pid5_items(method = "text") matches text copied from a printed
form. Typographic quotes (‘ ’ “ ”) match straight ones, and any
periods, ellipses (... or …), spaces (the no-break space too), tabs
or line breaks at the
start or the end of the text do not count. Text copied from the printed informant form still needs its
"He or she..." stem removed.
The score_pid5() help states the proration rounding rule exactly. A
prorated half rounds away from zero. For the default 0 to 3 coding this
is "halves up", as before. Under a negative srange a negative half
rounds down. No score changes.
The hitop-form Study Link Builder says what went wrong where it was
silent, and its module rows say more. If form.js does not load, or
the browser cannot read it, the page says that the Study Link Builder did
not start. It asks you to reload, or to open it in a current version of
Chrome, Edge, Firefox or Safari. With JavaScript off, the page asks you
to turn it on. A failure while the page fills the form from an opened
link now ends with a message and an enabled "Make the link".
Before, a second failure while the form was emptied left no message and
the button disabled. A refusal that names its fault stays in place, and a
failure on a page opened with no link says that the builder did not
start correctly. In a browser that supports inert, the form takes no
typing or presses while an opened link unpacks or its setup file is
fetched. So the fill does not replace what you type during the wait. An
opened link whose module is nested too deeply to show in the "Module
file" box is refused by name, with no field filled. Before, the
refusal said only that the link "could not be read". Screen readers name
each module row's box, file control, message and status for the row's
number, as in "Module file for instrument 2". When you set a module row
to another instrument, its message and the line that names a file are
cleared, and a file still being read is dropped. Two module rows are
refused as a repeated instrument, with the added sentence "A list holds
one HiTOP-SR module." The Instruments hint says "At most one HiTOP-SR,
whole or as a module." Section summaries leave out a hint, a field, a
menu or a box at any depth inside a field's label.
The hitop-form Study Link Builder ignores presses until it has read an
opened link, and several of its refusals change. "Make the link" is
disabled until the page has read the study link it was opened with.
Before, a press or Enter in a field sent the form as a plain web request
while a compressed link was unpacked or before the page's script loaded.
The page then reloaded with its fields in the address. An edit typed
into the "Module file" box while a chosen file is read now wins, and the
file's text is dropped. The
refusal of an instrument export, pasted or chosen as a file, now says to
use the file that the Module Builder or write_module() saved. Before,
it said to paste it. A refusal of the instruments list moves focus to the
instrument at fault, the second of a repeated instrument or the second
PID-5 form. When a setup step after the opened link is read fails, the
page shows its "could not be read" refusal with an empty form that still
makes a link. Before, the page stopped with no message. The section
summaries are drawn without copying page elements on each keystroke. The
online form refuses a link whose instrument field is not text, naming
the field. Before, a link with instrument set to ["hitopbr"] or
[["pid5"]] fetched that instrument.
The hitop-form Study Link Builder's hints give back facts that the
shorter hints dropped, and the tutorials name its sections. The SONA
hint says that each participant's survey code becomes their identifier,
and names the "Completion URL" field for SONA's completion address. The
hint of "Completion URL after a decline" says to put {participant} after
the address's ? or #. The hint of "Completion URL after a saved file"
says that a file is saved when the link has no place for responses or a
send is not confirmed. The "Completion URL" hint gives Prolific's
completion URL on the study's page. The
"Publishable key" hint says that opening a study link on the Study Link
Builder or the online form puts the key in GitHub Pages' logs. The page
calls itself the Study Link Builder in place of "this page". Four
refusals that it shares with the online form name the online form. The
online-collection, HiTOP-SR modules and PID-5 scoring articles name the
closed section that holds each field they tell you to fill in. They also
say that the link shows under "Your study link". The HiTOP-SR modules article
lists the five forms that the online form gives. The PID-5 scoring
article and the online strip on the five download pages use the names
"Study Link Builder" and "online form". The overview page names the web
address or Supabase table in place of "store". The strip's
button reads "Open the Study Link Builder".
hitop-form refuses a participant identifier it cannot write, and the
link builder keeps each recruiting site's names to that site. A link
whose participant identifier holds half of a two-part character (a lone
UTF-16 surrogate) is refused before the form starts. A typed identifier
that holds one is asked for again on the start screen, and the link
builder refuses one in its participant field. The link refusal applies
with or without a completion URL. Before, such a link started the form,
and with a completion URL the page sent or saved the answers and then
stopped before the closing screen. Under "Another site", the link builder
refuses the address parameters id and participantId, and names SONA or
CloudResearch Connect as the choice to use. Before, a link made that way
opened in the builder as SONA or Connect, and a SONA rebuild added
&id=%SURVEY_CODE%.
A module's item_order must hold whole numbers. score_hitopsr() and
reliability_hitopsr() under layout = "printed", and write_module(),
now refuse a module whose item_order holds a fraction such as 66.5.
Before, if cutting the fraction off still gave a permutation, they used
the whole number. They refuse an item_order that holds Inf or
-Inf with no base R warning. Before, base R warnings, on integer
coercion and on recycling, came before the error. An item_order of whole numbers stored as
doubles, such as c(66, 109), is accepted as before.
read_form_responses() caps four of its line and row errors at five,
refuses a UTF-16 or UTF-32 file once, and keeps the mismatch class when a
path holds a brace. Four errors now name the first five lines or rows at fault. When
more are at fault, one line such as "... and 2 more rows." follows. They
are the errors on a NUL
byte or a byte sequence that is not UTF-8, on a line of spaces and tabs,
on a row's field count, and on an instrument cell. The errors on an item
value already named five cells this way. Before, the four named every line
or row at fault. A file that starts with the byte-order mark FF FE 00 00
or 00 00 FE FF is taken as UTF-32, and one that starts with FF FE or
FE FF as UTF-16. A file with no mark is read as UTF-32 and then as
UTF-16, each little-endian and then big-endian. In each encoding the lines
are split on that encoding's line feed. Empty lines and lines of a lone
carriage return at the top are skipped. The first line that is not blank
then has its trailing carriage return dropped. If it holds only printable
ASCII characters and tabs, the file is taken as that encoding. A file of
blank lines only in one of these encodings is also taken as that encoding
when it holds at least one line feed. A file taken as UTF-16 or UTF-32 is
refused in one error. The error names the file, its encoding and no line,
and asks that the file be saved as UTF-8. A file taken as UTF-32 is
refused as UTF-32, not as UTF-16. Before, the byte error said that each
line held a NUL byte. For a UTF-16LE file it also named a line past the
file's last. A UTF-16 or UTF-32 file that the rule does not take, such as
one whose first line holds a character outside ASCII, still gets that byte
error when it holds a NUL byte. A file whose path
holds a brace, such as b{x}.csv, can differ from the first file in its
item columns. It now raises hitop_form_responses_mismatch, with the path
shown as written. Before, cli read the brace as markup. b{x}.csv and
b{.csv raised another error in place of the class, and b{.val x}.csv
was shown as b"x".csv.
read_form_responses() refuses three more hand-edit faults by name, and
its value refusals name each cell. A line holding a NUL byte or a byte
sequence that is not UTF-8 stops the read before any other check. The
message names the file and each such line, counted from the file's first
line, and no warning is raised. Before, a Latin-1 byte raised base R's
"invalid input found on input connection" warning, and the read then
stopped on a field count the file did not have. A line outside a quoted
cell made only of spaces and tabs is refused the same way. Before, such a
line was read as a line of one field, so the read stopped on a field
count the file did not have, on the file's first columns, or with base
R's "first five rows are empty" when the file was that one line. An
instrument cell that differs from the
item columns' stems (pid5bf beside hitopbr_01, a blank cell, a padded
one) is refused before any value is checked. The message names each such
response row, the cell and the stems. Before, the file read with the cell
as written. The whole-number and integer-range refusals now name each
cell at fault on its own line, as the response row, the column and the
value as written. The lines follow the file's row then column order, the
first five cells, then a count of the rest. Before, the two refusals
named the columns in one line and the rows in another.
?read_form_responses describes the refusals.
read_form_responses() refuses a malformed file by name, and the
hitop-form page refuses a descriptor whose items are not ascending. A
response row holding fewer or more fields than the header stops the read
with a message naming the file and each such row. Before, a row holding
one field too many was misread, and the read stopped on a fault the file
did not have, such as a form_build value that does not parse or base
R's "duplicate 'row.names'", and a short row read as a row with blank
items. A file with no header row
(zero bytes, blank lines only, a byte-order mark only) is refused with the
reader's own message naming the file, not base R's "no lines available in
input". An item column whose name is not a stem, an underscore and the
item number (foo, Hitopbr_01) is refused naming the column, before any
value is checked. The four value
refusals (an item value that is not a whole number, one outside the
integer range, a form_build or a submitted that does not parse) now
name the response rows at fault, counted from the first row after the
header. The hitop-form page and its link builder refuse a module
descriptor whose items are not in ascending order, naming the fault.
?read_form_responses, ?write_module and the online-collection article
describe the refusals.
Functions called as hitop::f() work without library(hitop). The
package's functions read its datasets, such as hitopsr_scales and
pid_items, by name. R shows an installed package's datasets only when the
package is attached. So a call such as hitop::hitop_module(),
hitop::score_hitopsr() or hitop::score_pid5() in a session that never ran
library(hitop) stopped with "object 'hitopsr_scales' not found". The
package now binds its datasets when it loads. A dataset is still read only
when a function first uses it.
write_module() writes LF line endings on every platform. On Windows it
wrote CRLF line endings. It now writes every descriptor, including the one
the generators' descriptor argument writes, as UTF-8 with LF line endings
and no CR byte.
The misordered-items warning gives the right remedy under layout = "printed".
If items names that share a prefix and end in numbers are not
ascending, score_hitopsr() and reliability_hitopsr() warn. Under layout = "printed", the warning no longer tells
you to sort them. Sorting undid the printed order and silently scored the
wrong items. Under the default layout the wording is unchanged.
Names in a module's printed order score without a warning. Under
layout = "printed", score_hitopsr() and reliability_hitopsr() no
longer warn about items names whose trailing numbers equal the module's
item_order. hitop-form gives such names to the item columns of a
shuffled module. Before, those names warned that they were not ascending.
Other names that are not ascending still warn, and the warning says that
names in the module's printed order pass.
ku_hitopsr's item columns held the wrong items and have been rebuilt.
The dataset was assembled as though the questionnaire that collected it
numbered its questions the way this package numbers HiTOP-SR items. It does
not, so all but two of the 405 hsr_ columns carried another item's answers.
The columns are now mapped through the collected questionnaire's own item
list, putting each answer under the item it was given for. No answer was
added, dropped or changed -- the rebuilt dataset holds the same 405 columns of
answers in a different order -- but anything computed from ku_hitopsr
moves, the worked examples in the HiTOP-SR articles included. ku_hitopbr,
which was already built from the correct mapping, and every other shipped
dataset are unchanged.
Every warning rename_pid5_items(), rename_hitopsr_items(),
label_pid5(), label_hitopsr() and label_hitopbr() raise now carries a
condition class, so a caller catches or suppresses one by class instead of
by matching its message text, which these functions promise nothing about.
Two classes are new: hitop_no_columns_matched, raised when no column matched
the expected names and nothing was renamed or labelled, at every one of these
five functions and under either target on the three that take one; and
hitop_incomplete_rename, raised by the two rename helpers when some but not
all of a form's items were renamed. rename_hitopsr_items(method = "text")
now raises its unmatched item-text report under the existing
hitop_unmatched_items class, which its PID-5 sibling already used. The
wording of every message is unchanged, with one behavioral exception: that
same HiTOP-SR report now escapes curly braces in the item text it echoes, so
item_text containing a {...} sequence raises the warning instead of
failing with a cli evaluation error. Separately,
rename_pid5_items(method = "number") given a column whose digits exceed R's
integer range no longer leaks base R's "NAs introduced by coercion" warning;
the column is still reported as unmatched.
The label_*() family reports every item column it could not label, and
says what is wrong with each. label_pid5(), label_hitopsr() and
label_hitopbr() raise the hitop_unpadded_items warning whether or not any
other column matched, so a frame whose item columns are every one mis-padded
is told which names were skipped rather than only that nothing matched. The
report separates the two mistakes it finds: a number padded to the wrong
width is reported as not zero-padded, and a number outside the instrument's
item range is reported as out of range in its own sentence naming that range,
with one hint at the end showing the spelling expected. Each sentence is
pluralized by the number of columns it names.
nItems is an integer in every shipped per-scale table. pid_scales
(each of its three elements), hitopsr_scales, hitopsr_subscales and
hitopbr_scales stored the item count as a double where available_scales(),
hitop_module() and the reliability family returned an integer, so
identical() across that boundary was FALSE on type alone. The tables are
rebuilt with an integer count and nothing else in them changed;
identical(available_scales()$nItems, hitopsr_scales$nItems) now holds.
Each REDCap export now stages its data dictionary in a directory of its
own. Every generate_redcap_*() call wrote that dictionary to the same path
inside the session's temporary directory before packing it, so two exports in
one session used one file, and an export that failed left the file behind for
the next call to find. Each call now gets a directory created for it and
removed whether or not the archive is written.
zip is now required at version 2.1.0 or later. That is the first release
whose zip() accepts the archive mode the REDCap exports pass it; an older
version installed cleanly and then failed at the call.
The online-collection article warns that a spreadsheet program can run a
participant's text as a formula. In a Google Sheet download or a Supabase
export, a participant identifier or a text answer that starts with =,
such as =1+1, is text in the sheet or table. If you open the file in a
spreadsheet program, it can read such a cell as a formula.
read_form_responses() returns it as text.
hitophsum_choices's help page reported the wrong number of rows. Its
@format section said 42 rows; the dataset has 185. The other twenty shipped
datasets' row counts were checked against their objects and are correct.
Three scoring vignettes gained sections for functions they never
demonstrated. Scoring the PID-5 now ranks each participant's five
highest-scoring facets with rank_scales(), and the HiTOP-SR and HiTOP-BR
scoring vignettes each show label_hitopsr() and label_hitopbr() attaching
the item text to a raw item column and the printed scale name to a scored
column.
The HiTOP-HSUM download page links its file generators. It gained the
"Custom File Generation" card the five sibling instrument pages already
carried, pointing at generate_docx_hitophsum() and
generate_redcap_hitophsum(). The Qualtrics survey file for this instrument
is still a prebuilt download only; the package exports no generator for it.
The test suite now sweeps every export against the vignettes. Each entry
in NAMESPACE must be called in an evaluated chunk of a vignette or article,
or link there to its own reference page, unless it is a deprecated function.
A name that appears only in prose, in a comment, or inside an unevaluated
chunk does not count.
The PID-5 scoring vignette and the HiTOP-SR modules article now read their
example files from the installed package. Both read a file from the
package's tests/ folder, which is not installed, so
their code failed when a user reran it. The files now install under
inst/examples/, and both pages read them with
system.file("examples", <file>, package = "hitop").
This release makes several breaking API changes to stabilize the interface before a CRAN submission.
Reliability tables now print each scale's canonical name, in a column named
Scale. reliability_pid5(), reliability_hitopsr() and
reliability_hitopbr() used to rebuild a display name from the camelCase stem
that names the scored column, which got nine names wrong. They now read the
name from the same keying table the questionnaires and available_scales()
print, so the nine change as follows:
| was | is |
|---|---|
| Distress Dysphoria | Distress-Dysphoria |
| Non Persistence | Non-persistence |
| Non Planfulness | Non-planfulness |
| Non Suicidal Self Injury | Non-suicidal Self-injury |
| Sex Related Substance Use | Sex-Related Substance Use |
| Well Being | Well-being |
| P Factor | p-Factor |
| Unusual Beliefs Experiences | Unusual Beliefs & Experiences |
| Negative Affectivity | Negative affectivity |
The first six are HiTOP-SR scales, the seventh is HiTOP-BR, Unusual Beliefs & Experiences is a PID-5 facet on both the full and short forms, and Negative affectivity is a PID-5-BF domain. Every HiTOP-SR name reliability_hitopsr()
returns is now a name hitop_module() accepts and hands back unchanged;
hitop_module() rejected all six of the old HiTOP-SR spellings.
The column carrying those names is renamed scale to Scale, matching
available_scales(). Code selecting rel$scale must migrate to rel$Scale;
there is no dual column and no deprecation shim, the same one-release migration
this package uses for its other renamed output columns. Nothing else about
these functions' output changed -- the same rows in the same order, and the
same nItems, alpha and omega values.
Relatedly, available_scales() returns nItems as an integer rather than a
double, so it now matches the nItems of hitop_module() and of the
reliability tables. The shipped hitopsr_scales and hitopbr_scales datasets
still store nItems as a double, so identical() between one of those columns
and available_scales()$nItems now returns FALSE where it used to return
TRUE; == and dplyr joins are unaffected. The snakecase package is no longer an import; the
regeneration scripts under data-raw/ still use it, and say so.
Two HiTOP-SR scales are now named the way the instrument's introduction
paper prints them. The scale abbreviated NSSI is named in full,
Non-suicidal Self-injury, spelled out the way the other 75 scales already
were; and the scale called Body Focus is named Appearance Focus.
Breaking: score_hitopsr() returns hsr_nonSuicidalSelfInjury and
hsr_nonSuicidalSelfInjury_se where it returned hsr_nssi and hsr_nssi_se,
and hsr_appearanceFocus and hsr_appearanceFocus_se where it returned
hsr_bodyFocus and hsr_bodyFocus_se; code selecting the old names must be
updated. Because the scale tables are sorted by name, both scales also move
position in the returned tibble — the first from 448 to 451 and its standard
error from 524 to 527, the second from 412 to 408 and its standard error from
488 to 484 — and the columns lying between an old and a new position shift by
one, so code selecting scored columns by position rather than by name must be
updated too. Both scales are also addressed by name elsewhere:
hitop_module() no longer accepts "NSSI" or "Body Focus", and
read_module() rejects a saved module descriptor that records either, so any
descriptor written before this release must be rebuilt;
available_scales("hitopsr") lists the new names. No score changes: every
column, those two included, returns exactly the values it did before. The
names also change on the scoring page of the two Word questionnaires; the
Qualtrics and REDCap exports print no scale names and are unchanged.
PID-5-BF total score (breaking). score_pid5(version = "BF") now returns a
total column after its five domains, so the brief form's normed total score
in pid_norms has something to convert. Following Markon et al. (2024, p. 23),
it is the item-level mean over all 25 items rather than the mean of the five
domain means; the two agree on complete data and differ only when items are
missing. Because each scale applies the missing rule independently, a total
can be reported alongside one or more NA domains — see ?score_pid5 for the
exact bounds. Two consequences for existing code: reliability_pid5(version = "BF") now returns six rows rather than five, and the printed scoring table
on the PID-5-BF Word forms gains a Total row listing all 25 items (both forms
are rebuilt, with new hitop_artifacts entries). Code that counts the columns
of score_pid5(version = "BF") or the rows of reliability_pid5(version = "BF") must be updated. The PID-5 and PID-5-SF are unaffected.
Scoring and converting now refuse two argument shapes they used to let
fall through. Re-running score_pid5(), score_hitopsr(),
score_hitopbr(), validity_pid5(), norm_pid5(), rank_scales() or
interval_hitopsr() with append = TRUE over data that already holds the
columns that call produces is now an error naming every colliding column,
where before it reached tibble's duplicated-names complaint, which named
neither the argument nor the function. Nothing is overwritten: a same-named
column in your data need not have come from this package, so it is not
destroyed on your behalf. Pass append = FALSE to get only the new columns,
or drop the colliding columns first. The condition is classed
hitop_append_collision.
Separately, a scores argument to norm_pid5() or interval_hitopsr(), or
a scales argument to rank_scales(), that names no columns is now an error
rather than a base-R complaint about differing numbers of rows -- and, in
rank_scales(), rather than a report that top was out of range "between 1
and 0", which blamed a consequence of the empty selection for its cause. The
empty selection is reported ahead of the other selection arguments, so the
cause named is the empty selection itself; an invalid data is still
reported first. The condition is classed hitop_empty_selection. Both
classes are part of the package's public contract.
Scoring and converting return exactly what they returned before for every
call that still succeeds; no arithmetic changed. Two calls that used to
succeed no longer do: norm_pid5() and interval_hitopsr() with
append = FALSE and an empty scores returned an empty tibble, and now
raise the empty-selection error along with every other shape of that call.
The Qualtrics and REDCap generators now check their arguments (breaking).
block_name, id_prefix, and include_instructions on every
generate_qualtrics_*(), form_name and required on every
generate_redcap_*(), and breaks on every generator that takes it,
previously wrote whatever they were handed into the import file:
id_prefix = 1 wrote question IDs reading 1_001, and required = "yes"
left the dictionary's required column blank on every row instead of marking
anything. Each now raises an error naming the argument, and no file is
written. breaks still accepts 0 and NULL to turn pagination off. Files
built from valid arguments are byte-for-byte unchanged.
rank_scales()'s prefix argument is now matched literally (breaking).
It was previously compiled as a regular expression anchored to the start of
the column name, which meant a prefix containing ( failed with a regex
error and one containing . could strip a prefix that was never there. A
column name that does not begin with exactly prefix is now carried through
whole. Code relying on a regex prefix must pre-strip the names instead.
norm_pid5() matches prefix the same way.
New reliability_pid5(), reliability_hitopsr(), and reliability_hitopbr()
functions return a per-scale tibble (scale, nItems, alpha, omega).
These replace the alpha and omega arguments of score_pid5(),
score_hitopsr(), and score_hitopbr(), which only printed a reliability
table as a side effect and have been removed
score_pid5(), score_hitopsr(), and score_hitopbr() now take a single
missing argument in place of the previous na.rm (and, for score_pid5(),
apa_scoring) arguments. For score_pid5(), missing = "apa" (the default)
applies the APA missing-data/proration rule, "available" averages the present
items (the old apa_scoring = FALSE, na.rm = TRUE), and "complete" returns
NA for any scale with a missing item (the old na.rm = FALSE).
score_hitopsr()/score_hitopbr() offer "available" (default) and
"complete". Default behavior is unchanged
The tibble argument has been removed from score_pid5(),
score_hitopsr(), score_hitopbr(), validity_pid5(), and rank_scales();
these functions now always return a tibble
Distribution artifacts are now versioned. The new hitop_artifacts
manifest dataset identifies every prebuilt file in inst/extdata/ by
build date and MD5 checksum (one row per build, history kept); the
website's download pages show each instrument's current builds and a
version history; and generated Word documents carry a build stamp in the
footer ("Generated YYYY-MM-DD · hitop X.Y.Z"). A test suite locks the
committed files to the manifest, so no distributed artifact can change
again without a visible version bump. Artifact filenames no longer
carry the instrument version (e.g., pid5_1.0_A4.docx is now
pid5_A4.docx, so previously shared download URLs no longer resolve),
and the generate_docx_* default file arguments dropped _1.0
accordingly
interval_hitopsr() puts a confidence interval around a HiTOP-SR scale
score. Give it scored columns and it returns three per scale: _est, a
regression-based estimate of the respondent's true score, and _lo and _hi,
the bounds of a confidence interval around it at a level you choose
(0.95 by default). The method is the regression approach with scale
correction from Schmukle (2026, Assessment, 33(5), 817-825), Equations (10)
to (12): the estimate pulls the observed score toward the reference mean,
because with imperfect measurement a true score tends to sit nearer the mean
than the observed score does, and the scale correction returns it to the
metric the observed score is on. The reference mean, standard deviation and
Cronbach's alpha come from a new exported dataset, hitopsr_devstats, which
carries the statistics printed for each of the 93 HiTOP-SR primary scales and
subscales in Table 1 of the instrument's introduction paper. That reference
group is the paper's Development Sample 2, N = 780: a development sample, and
not a community norm -- no census weighting was applied and no raw-score to
T-score table exists -- so an interval says where a score sits relative to the sample the
instrument was developed on, not what percentile it occupies in a population.
Two limits are worth knowing: the interval is the same width for every
respondent on a scale and is not clipped to the 1-4 response range, so on a
strongly skewed scale a bound can fall outside it; and the coverage the
method demonstrates holds across a population of respondents rather than for
any one of them.
A chosen set of an instrument's scales is called a module. The
entries below describe that feature under its final names. For development-
version users only: these were briefly called hitop_subset() and subset
before release. Both still work and warn, every function taking a module
also accepts a descriptor built by hitop_subset(), and supplying both
module and subset in one call is an error. One further consequence of
the argument rename: in score_hitopsr(), the abbreviation m = is now
ambiguous between module and missing and errors — write mo = and
mi = , or spell the arguments out.
Generate a HiTOP-SR module from selected scales. The new
hitop_module() describes a chosen set of an instrument's scales, and
generate_docx_hitopsr(), generate_qualtrics_hitopsr(), and
generate_redcap_hitopsr() each take it as a module argument to emit a
form containing only those scales' items. The Qualtrics and REDCap exports
keep each item's original HiTOP-SR number, because there an item number
names a collected data column; the Word form numbers its items 1 to n
down the page (see the entry above).
Scale names may be given as printed on the instrument ("Antisocial Behavior") or as the camelCase stems used in scored output
("antisocialBehavior"), in any mixture and ignoring case. Subsetting is
currently available for the HiTOP-SR only.
Scoring modules. score_hitopsr() and reliability_hitopsr() gain a
module argument taking the same description that hitop_module() builds and
the generate_*_hitopsr() functions consume. Give it the item columns you
actually collected and it scores only that module's scales, returning the same
values a full 405-item administration would have produced for them. Without
the argument both functions behave exactly as before. See
vignette("hitopsr_scoring").
HiTOP-SR module Word forms are numbered 1 to n, and can be shuffled.
generate_docx_hitopsr() gains renumber (default TRUE), so a module's
paper form no longer prints the full instrument's gapped numbers; pass
renumber = FALSE for the previous behavior. It also gains randomize
(default FALSE), which prints the items in a random order and appends a
crosswalk from each printed number back to its original HiTOP-SR number, so
a shuffled form is still scoreable from the paper alone. Use set.seed()
to make an order reproducible. Every call's invisible return value now
carries an item_order attribute holding the original item numbers in
printed order. Data collected on a shuffled form must be reordered through
that attribute before score_hitopsr(), which addresses a module's items in
ascending original order; ?generate_docx_hitopsr shows the idiom.
generate_qualtrics_hitopsr() and generate_redcap_hitopsr() are
deliberately unchanged: there an item number names a collected data column.
No distributed form under inst/extdata/ changed, since each is the full
instrument and already numbered from one. The two new arguments sit between
module and subset in the signature, so any call passing arguments
positionally past font_family must be respelled by name.
A HiTOP-SR Word form built from a module says so in its header. With no
title of your own, generate_docx_hitopsr() now heads a module form
"HiTOP-SR Module (v1.0)" and a full-instrument form "HiTOP-SR (v1.0)", so
a paper holding a handful of scales is no longer titled as the whole 405-item
instrument. Passing title still prints exactly what you pass, including on
a module form. The item text, response options, and administration
instructions are untouched, and no distributed form under inst/extdata/
changed, since each is the full instrument.
write_module() and read_module() save a module to a file and read it
back. Keep the file beside the forms you generate, and at scoring time
hand read_module() to the module argument instead of retyping every
scale name. The file is small, plain JSON you can read, edit, and send to a
collaborator; it carries a format version so later releases can grow it.
What it records is scale names, never a scoring key: the items and their
reverse-keying flags are rebuilt from the package's own tables on read, and
the item numbers the file records are checked against that rebuild, so a
descriptor written against tables that have since changed stops with an
error rather than scoring quietly. Each failure carries its own condition
class, listed on ?read_module; a descriptor that is malformed rather than
merely wrong is refused by one of those same classes, never by a bare R
coercion error. The order the item numbers are written in carries no
meaning, so a hand-edited file may list them any way round.
The three HiTOP-SR generators can save a module descriptor beside the file
they build. Pass descriptor = "module.json" to
generate_docx_hitopsr(), generate_qualtrics_hitopsr(), or
generate_redcap_hitopsr() and one call produces both the form you field
and the file that scores the data it comes back as. A call passing no
module writes a descriptor naming every scale, so a full administration is
described too. On a shuffled Word form (randomize = TRUE) the descriptor
also records the printed order, returned on the read module's item_order
attribute — the record a shuffled whole-instrument form leaves nowhere else,
since no crosswalk is printed for one. The descriptor is written before the
instrument file, so an unwritable path is reported before any form is
produced, and it is removed again if the form itself cannot be written.
Once both files are on disk, the console names the descriptor it saved, after
the message naming the form itself; a call that passes no descriptor says
nothing about one.
write_module() now writes an item_order attribute as the file's
itemOrder field, so a descriptor read and written again keeps the order it
recorded.
available_scales() lists the scales you can build a module from, with
the name printed on the form, the camelCase stem that names the scored
output column, the item count, and — in a new fourth column, Brief — the
scale's brief clinician-facing definition, so you no longer need to know
which dataset to open before choosing scales. The definition is matched to
the scale on that camelCase stem, never on a printed name, and a stem with
no definition behind it is an error rather than a blank.
hitopsr_definitions gains a camelCase column, naming whatever each
row defines: the subscale where there is one, otherwise the scale. It is the
key available_scales() joins on, and it lines up with
hitopsr_scales$camelCase and hitopsr_subscales$camelCase.
A new web app builds HiTOP-SR modules in your browser. Tick the scales
you want at
jmgirard.github.io/hitop-builder
and download a Word, Qualtrics, or REDCap instrument containing only those
items. The page downloads R and this package into your browser and generates
the files there, so nothing you select or produce is sent anywhere; it builds
blank questionnaires and scores nothing. Linked from the Instruments menu on
the package website. A Word item order box there shuffles the printed order
of the Word form's items, with an on-page warning that the collected columns
must be put back into the instrument's own order before scoring; the
Qualtrics and REDCap downloads are unaffected by it. A Word item numbering
group chooses between numbering the printed items 1 to n -- the default --
and keeping the HiTOP-SR's own item numbers, which are the names the same
page's Qualtrics and REDCap exports give the collected variables, so paper
responses can be typed into a project built there without translating them;
it too leaves those two downloads unchanged. Ticking every scale now builds
the whole instrument rather than a module, so that Word form is headed
HiTOP-SR (v1.0) and the three downloads are named for the instrument.
The browser module builder shows those definitions while you pick. Pointing at a scale, or reaching its checkbox with the Tab key, brings up that scale's definition; Escape dismisses it. The page reads the text from the installed package rather than keeping a copy, so a version that does not supply it shows the list exactly as before.
Norm-referenced profile plots. New plot_pid5() draws one respondent's
normed PID-5 scores as a profile against the published normative tables —
the five domains (plus the brief form's total), or all 25 facets grouped by
domain, on a T-score or percentile axis. It presents scores against norms and
characterizes none of them: there are no severity bands, no elevation
thresholds, and no annotation about what a score means. The score axis spans
the range the tables actually print, so two profiles on the same version are
directly comparable. Returns an ordinary ggplot object, which stays in
Suggests — install {ggplot2} to use it. Worked profile examples for all three
forms: vignette("pid5_scoring"), vignette("pid5sf_scoring"), and
vignette("pid5bf_scoring").
PID-5 normative tables. The new pid_norms dataset carries the published
normative score distributions for the PID-5, PID-5-SF, and PID-5-BF: the raw
score and percentile at each T score for the five domain scales, for all 25
facet scales of the full and short forms, and for the brief form's total
score; and the percentile at each raw score for the INC, INC-S, ORS, and PRD
validity scales. Scale names match the columns score_pid5() and
validity_pid5() return. Every value comes from Markon et al. (2024) and is
verified cell by cell against that source. Note that most facet columns print
raw scores above the 3.00 a mean of 0–3 items can reach, and 19 of them repeat
a printed 4.00 across several T rows; those rows ship exactly as published and
are simply unattainable.
PID-5 score conversion. The new norm_pid5() converts scored PID-5,
PID-5-SF, and PID-5-BF columns to normative T scores and percentiles from
pid_norms, adding a _t column for every converted scale whose normative
rows carry a T score and a _ptl column for every converted scale. Every
returned value is a printed cell of Markon et al. (2024): the nearest printed
row is selected and nothing is interpolated. Scores outside a printed range are
capped to the nearest end with a warning rather than extrapolated, and scales
the tables do not cover return NA with a warning naming them. Scores
collected on any four-option response coding are accepted: a coding shifted
off the official 0-3 range (1-4, say) is reconciled to it before lookup, per
scale — item means by the coding's low value, PRD by that value times its
item count, and INC, INC-S, and ORS left alone as coding-invariant — and
a warning names which scales were adjusted and which were not. A coding
implying some other number of response options has no mapping onto the
four-option tables and returns NA in every conversion column with a warning.
Note that validity_pid5()'s published cut scores are still not adapted to
a shifted coding, so a reconciled percentile and an unreconciled validity flag
can appear together; see ?norm_pid5. Every report the function makes is a
warning condition, so one suppressWarnings() call silences it entirely. All
25 facets convert on the full and short forms as well as the five domains; on
the brief form, and for SD-TD on any form, the tables carry nothing and the
conversion columns come back NA with the warning above. The PID-5, PID-5-SF,
and PID-5-BF vignettes each gain a section demonstrating the conversion.
rank_scales() gains a name argument (default "top_scales") naming its
output column, which was previously hard-coded as "out". It also gains
reverse and srange arguments: scales named in reverse are reflected via
sum(srange) - value before ranking, so a reverse-directioned scale (e.g. a
well-being scale, where higher = healthier) ranks on the same "higher = more
elevated" metric as the other scales
Clearer errors for bad arguments. Every argument check across the package
now reports which argument was wrong, what was supplied, and which function
was called, instead of printing the internal test that failed. This affects
data, prefix, name, append, calc_se, alpha, omega, and top
throughout the scoring, reliability, norming, labelling, and ranking
functions. A bad
dir in rank_scales() now lists the permitted values and suggests the
closest match. No function accepts or rejects anything it did not before —
only the messages changed.
norm_pid5() now checks its scores argument before converting anything.
Naming the same score column twice is an error rather than a silently
duplicated pair of output columns, and a factor or character score column is
an error rather than being coerced — a factor's integer codes are not its
scores, and a character column coerces to NA. Logical columns still
convert. Every complaint about the argument names scores, not the items
or scales of the shared validators behind it. That error now gives each
offending column its own line with its full class (an ordered factor reads as
<ordered/factor> rather than as ordered), and errors raised while
reconciling a shifted response coding are attributed to norm_pid5() rather
than to the internal helper that raised them.
plot_pid5(labels = FALSE) no longer reserves the extra room a value label
would need on the score axis. That padding is the label's, and reserving it
when no label is drawn spent width on empty margin — exactly the width
labels = FALSE is asked for to save. Profiles drawn with labels are
unchanged.
plot_pid5() now reports a non-numeric normed column the way norm_pid5()
already did: one bullet per offending column, each naming that column and its
own class, rather than a single line listing the names with no types, and a
closing line saying what to do about it. The two functions now share one
guard, so the two messages cannot drift apart.
plot_pid5() now places each value label to the right of its point rather
than above it, and pads the score axis to hold it. Offsetting upward took the
room out of the panel's height, where it ran out on a smaller figure and the
top label in each panel was clipped. The labels fit on figures about 7 inches
wide or more; see labels below for narrower ones.
plot_pid5() gains a labels argument. The value labels need a figure about
7 inches wide or more; set labels = FALSE for a narrower one and the points
and profile line are drawn without them.
Qualtrics question IDs are now zero-padded to the width of the largest item number rather than the number of items. Output for every full instrument is unchanged; the change keeps IDs uniform in a module file.
PID-5 Word forms print the response options on two lines. The response
scale printed above the items on the PID-5, PID-5-SF, and PID-5-BF Word forms
now runs across two lines — 0 and 1 on the first, 2 and 3 on the
second — so that no option phrase is broken partway through by the column
width. The option values and wording are unchanged, and the HiTOP-SR and
HiTOP-BR forms keep their single-line scale. All six PID Word files
(US and A4) were regenerated, with new hitop_artifacts entries.
The REDCap generators no longer need an external zip program. They
built the instrument archive by running the system's zip command, which
silently failed wherever no such program was installed -- commonly on
Windows. The archive is now written by the {zip} package, in R, with no
outside program involved. The file REDCap receives is unchanged.
The jsonlite package moved from Suggests to Imports, so it is now
installed with hitop rather than optionally. write_module() needs it, and
the browser module builder runs in an environment where suggested packages
are not installed.
The download buttons on the instrument pages now serve the files from the package website itself, so a browser saves each one under its own name. The Qualtrics survey files used to open as text in a new tab, because GitHub serves them as plain text; they now download ready to import. The GitHub links keep working for anyone who saved them.
The HiTOP-HSUM Qualtrics survey file (hitophsum_qualtrics.qsf) now imports
into Qualtrics. The previous build was exported through the Qualtrics API,
which writes absent values as empty objects rather than as JSON null; the
importer rejects that file with an internal error. The file now carries the
same encoding as a survey exported from the Qualtrics interface. Its
content — questions, response choices, and skip/display logic — is unchanged.
HiTOP-HSUM aligned to its authoritative source (the HiTOP Society's
"revised SUD module-August 2024" development worksheet):
hitophsum_items item text now matches the worksheet's substance-specific
wording (alcohol items use drink-specific phrasing; nicotine and other-drug
items corrected; obvious worksheet typos repaired and logged), the
free-text nicotine quantity item now shows only for non-cigarette,
non-cigar forms, and hitophsum_choices gains the alcohol/cigarette/cigar
quantity choice sets. In the REDCap export, the cigar quantity item is now
a valid dropdown (it previously imported with an empty choice list), and
"Prefer not to say" frequency responses no longer satisfy any symptom
gate. New other_drug_rule argument on generate_redcap_hitophsum():
the default ("most_frequent") follows the worksheet's looping rule —
symptom items appear only for the most frequently used other drug used at
least monthly (ties show all tied drugs) — while "per_drug" reproduces
the previous looser behavior of gating every other drug independently.
The overview DOCX now says "Street opioids" (previously "Heroin/opiates")
and "Goose bumps", and its item matrix matches the corrected wording; the
prebuilt DOCX and REDCap files were regenerated
The HiTOP-HSUM Qualtrics import file was rebuilt from the corrected item data. The previous file predated the source alignment above and also contained an empty cigar-quantity dropdown and a duplicate copy of every question. Note one platform difference: Qualtrics display logic cannot compare answers across questions, so the Qualtrics survey shows symptom items for every other drug used at least monthly (the source module's sanctioned loosening), whereas the REDCap export defaults to the most-frequently-used other drug only
Standardized item-text punctuation in hitopsr_items (7 items) and
hitopbr_items (1 item): every item now ends in a period. The affected items
(HSR 5, 27, 30, 284, 314, 332, 382 and HBR 41) lack the period in the source
instrument itself, where 398 of 405 HiTOP-SR items have one; the omissions are
treated as typographical oversights. The derived *_scales/*_subscales
tables and the prebuilt DOCX/Qualtrics/REDCap artifacts in inst/extdata/
were regenerated to match
A new article, Building HiTOP-SR Modules, walks the whole module workflow: choosing scales, describing the module, generating the paper, Qualtrics, and REDCap files, selecting the collected item columns, and scoring plus reliability. The HiTOP-SR scoring vignette now links to it rather than carrying its own shorter copy.
The modules article and the three HiTOP-SR generator help pages describe
the module behavior the generators have. Building HiTOP-SR
Modules now
names the HiTOP-SR Module (v1.0) header a module Word form carries, and
title = as the way to override it; says that a form built with
renumber = FALSE prints no printed-number crosswalk; and says that
include_subscales = TRUE cannot be combined with module. The recipe for
putting columns collected on a shuffled form back into instrument order,
collected[order(item_order)], now states -- in the article and in
?generate_docx_hitopsr -- that it applies only to columns that are in the
order the form printed. And the descriptor argument of all three
generate_*_hitopsr() functions now says the descriptor's path is announced
on the console once both files are written. No behavior changed.
?hitop_module now says that a module naming every scale holds exactly the
instrument's own items but is still framed as a module by
generate_docx_hitopsr() -- the HiTOP-SR Module header, and a crosswalk
when the form is shuffled -- so a caller wanting the full instrument's
framing passes no module at all.
The calc_se help text on score_pid5(), score_hitopsr(), and
score_hitopbr() now states what these standard errors are computed over and
says plainly that they are not standard errors of measurement. Each is the SD
of the items a respondent actually answered over the square root of how many
they answered (for a PID-5 full- or short-form domain, over its three
contributing facet scores), so no reliability estimate enters it: it describes
how much a respondent's answers varied within a scale, not how precisely the
scale measures. The reliability functions are named for that. The vignettes
already said this; the help pages said only "the standard error of each scale
score", which reads as a standard error of measurement.
The scoring vignettes described the calc_se standard errors incorrectly and
now describe what is actually computed. The divisor is the number of items a
respondent answered, not the number of items on the scale. A PID-5 short-form
domain score is a mean of three facet scores rather than of items, so its
standard error is taken over those three facet scores. The vignettes also no
longer suggest converting these standard errors into confidence intervals:
they summarize how much a respondent's answers varied within a scale, not how
precisely the scale measures the trait.
The Qualtrics import instructions note that a browser may save the survey
file with a .txt extension, and that renaming it back to .qsf is safe.
New instrument overview page. A single "HiTOP Instruments" page presents the three self-report measures — HiTOP-SR, HiTOP-BR, and HiTOP-HSUM — as at-a-glance summary cards, each linking to its full download page. It is the first entry in the website's "Instruments" menu. Its HiTOP-BR card now describes the eight scales at their true hierarchy levels — six spectra plus the Externalizing superspectrum and a general p-factor — rather than calling all eight "spectra".
Redesigned instrument download pages. Each download button on the
website's instrument pages now shows its file's build date, and the
version tables are replaced by a collapsible "Current builds & version
history" panel rendered from the hitop_artifacts manifest. The
manifest's change notes were reworded for a general audience (data
unchanged otherwise).
Centralized import instructions. A new "Importing into Qualtrics &
REDCap" article gives step-by-step instructions for all three import
formats — Qualtrics survey files (.qsf), Qualtrics questions files
(.txt), and REDCap instrument ZIPs — and every instrument download page
now links its Qualtrics and REDCap cards to it. The REDCap import steps
previously embedded in each generate_redcap_*() help page now live in
that article, which the functions point to via "See also".
Documentation accuracy and polish across the scoring tutorials and pkgdown instrument pages: corrected stale column/dataset names in the HiTOP-SR tutorial (leftovers from an earlier "HiTOP-PRO" draft), updated the HiTOP-BR scale count (8, not 7) and the PID-5 appended-column count (now includes the 5 domains), finished the previously "work in progress" PID-5-BF tutorial, added the missing Scale Reliability sections to the HiTOP-BR and PID-5-BF tutorials, fixed a mis-targeted REDCap "Import Instructions" link on the PID-5 download page, and reconciled the instrument download pages so each describes only the resources it actually links
score_pid5() now returns the 5 personality-trait domain scores for the FULL
and SF versions (APA scoring key Step 3), appended after the 25 facet scorespid_domains dataset (the domain to primary-facet map used for FULL/SF
domain scoring)score_pid5() gains an apa_scoring argument (default TRUE) that applies the
published APA missing-data and proration rule: a facet (or BF domain) with more
than 25% of its items unanswered is set to NA; otherwise the raw score is
prorated to the full item count and rounded before averaging, and a FULL/SF
domain is NA if any contributing facet is NA. This changes the default
scored output under missing data (previously rowMeans(na.rm = TRUE) averaged
whatever items were present). Pass apa_scoring = FALSE to restore the previous
behavior. Under apa_scoring = TRUE, na.rm is ignored (with a warning if set
to FALSE), and any standard error is NA wherever its scale score is NAvalidity_pid5() erroring on single-row input for the FULL and SF formsscore_pid5(calc_se = TRUE) erroring on single-row inputgenerate_docx_*, generate_qualtrics_*, and
generate_redcap_* export families, verifying each generated file against the
source instrument datasets (including the HiTOP-HSUM REDCap branching logic)score_pid5(), score_hitopsr(), score_hitopbr(), and validity_pid5() now
guard against two ways a bad items mapping silently produces wrong scores:
they error on duplicated items entries and warn when items column names
share a common prefix and trailing number but those numbers are not in
ascending (instrument) ordervalidity_pid5() now warns when srange is not c(0, 3), because the
published PRD and SD-TD cut scores are raw sums against fixed thresholds that
assume 0-3 item coding and do not adapt to other codings@examples to every exported functionpid_items
and hitopbr_items @format blocks, document the pid_scales format, and fix
the sim_hitopbr item-column names (hitopbr_1 to hitopbr_45)score_hitopbr() gains alpha and omega arguments (default FALSE) that
print a per-scale reliability summary, matching score_pid5() and
score_hitopsr()score_pid5(), score_hitopsr(), and score_hitopbr() now
share a single internal scoring engine instead of three hand-maintained copies
of the same pipeline (no change to scored output)items of the wrong length now reports the expected
count and what was supplied, and supplying items names or positions that are
not columns of data now raises an actionable error (naming the offenders)
instead of a cryptic base-R subscript errorrank_scales() functions are now attributed to the function you called rather
than to an internal helper