Behavioral Feature Descriptives

Supplemental Table S1

Author

Jeffrey Girard

Published

October 7, 2026

Setup

Code
library(tidyverse)
library(gt)
Code
read_features <- function(modality) {
  read.csv(file.path("..", "data", paste0(modality, ".csv"))) |>
    as_tibble()
}

id_cols <- c("participant", "source", "ptsd", "demo_age", "demo_sex", "demo_nonwhite", "task")

df_features <-
  read_features("verbal") |>
  full_join(read_features("vocal"), by = id_cols) |>
  full_join(read_features("visual"), by = id_cols) |>
  mutate(
    group = factor(ptsd, levels = c(1, 0), labels = c("PTSD", "Control")),
    task = factor(task, levels = c("Account", "Impact"))
  )

# Feature labels, display precision, and modality, in table order
spec <- tribble(
  ~var,          ~label,                                  ~digits, ~modality,
  "WC",          "Total Word Count",                      0,       "Verbal",
  "i",           "1st Person Singular Pronouns (%)",      2,       "Verbal",
  "we",          "1st Person Plural Pronouns (%)",        2,       "Verbal",
  "emo_neg",     "Negative Emotion Words (%)",            2,       "Verbal",
  "emo_pos",     "Positive Emotion Words (%)",            2,       "Verbal",
  "allnone",     "All-or-None Words (%)",                 2,       "Verbal",
  "cogproc",     "Cognitive Process Words (%)",           2,       "Verbal",
  "Perception",  "Perception Words (%)",                  2,       "Verbal",
  "sentiment",   "Sentiment",                             2,       "Verbal",
  "SD_Loudness", "SD Loudness",                           2,       "Vocal",
  "SD_Pitch",    "SD Pitch (Hz)",                         1,       "Vocal",
  "PSR",         "Pseudo-Syllable Rate (per s)",          2,       "Vocal",
  "M_Voiced",    "Mean Voiced Regions (s)",               3,       "Vocal",
  "M_Unvoiced",  "Mean Unvoiced Regions (s)",             3,       "Vocal",
  "SD_Voiced",   "SD Voiced Regions (s)",                 3,       "Vocal",
  "SD_Unvoiced", "SD Unvoiced Regions (s)",               3,       "Vocal",
  "CPPS",        "CPPS (dB)",                             2,       "Vocal",
  "H1H2",        "H1-H2 (dB)",                            2,       "Vocal",
  "AU04",        "AU4 Brow Lowerer",                      2,       "Visual",
  "AU12",        "AU12 Lip Corner Puller",                2,       "Visual",
  "AU15",        "AU15 Lip Corner Depressor",             2,       "Visual",
  "AU17",        "AU17 Chin Raiser",                      2,       "Visual",
  "AU20",        "AU20 Lip Stretcher",                    2,       "Visual",
  "PoseX",       "Head Pose Rx, Pitch (rad)",             2,       "Visual",
  "PoseY",       "Head Pose Ry, Yaw (rad)",               2,       "Visual",
  "DiffX",       "Head Motion Rx, Pitch (rad)",           3,       "Visual",
  "DiffY",       "Head Motion Ry, Yaw (rad)",             3,       "Visual"
)

Sample Size

Code
df_features |>
  summarize(
    .by = c(group, task),
    n = n_distinct(participant)
  ) |>
  pivot_wider(names_from = group, values_from = n) |>
  mutate(task = if_else(task == "Account", "Trauma Account", "Impact Statement")) |>
  gt(rowname_col = "task") |>
  tab_stubhead(label = "Task") |>
  tab_spanner(label = "Participants", columns = c(PTSD, Control)) |>
  cols_align(align = "center", columns = c(PTSD, Control))
Task
Participants
PTSD Control
Trauma Account 48 44
Impact Statement 48 44

Table S1

Code
cell_stats <-
  df_features |>
  pivot_longer(all_of(spec$var), names_to = "var") |>
  summarize(
    .by = c(var, group, task),
    m = mean(value, na.rm = TRUE),
    s = sd(value, na.rm = TRUE)
  ) |>
  left_join(spec, by = "var") |>
  mutate(cell = sprintf("%.*f (%.*f)", digits, m, digits, s))

table_s1 <-
  cell_stats |>
  select(modality, var, label, group, task, cell) |>
  pivot_wider(names_from = c(group, task), values_from = cell, names_sep = "_") |>
  arrange(match(var, spec$var)) |>
  select(modality, label, PTSD_Account, Control_Account, PTSD_Impact, Control_Impact)

n_group <- df_features |> distinct(participant, group) |> count(group) |> deframe()

table_s1 |>
  gt(groupname_col = "modality", rowname_col = "label") |>
  tab_header(
    title = "Table S1",
    subtitle = "Means and Standard Deviations for Behavioral Features by Group and Task"
  ) |>
  tab_spanner(label = "Trauma Account", columns = c(PTSD_Account, Control_Account)) |>
  tab_spanner(label = "Impact Statement", columns = c(PTSD_Impact, Control_Impact)) |>
  cols_label(
    PTSD_Account = sprintf("PTSD (n = %d)", n_group[["PTSD"]]),
    Control_Account = sprintf("Control (n = %d)", n_group[["Control"]]),
    PTSD_Impact = sprintf("PTSD (n = %d)", n_group[["PTSD"]]),
    Control_Impact = sprintf("Control (n = %d)", n_group[["Control"]])
  ) |>
  cols_align(align = "center", columns = -label) |>
  tab_source_note(md(paste(
    "*Note.* Values are *M* (*SD*) in each feature's original units.",
    "LIWC-22 categories are percentages of total words.",
    "Sentiment is the LLM-predicted rating of each transcript (1 = very negative, 7 = very positive).",
    "Action unit (AU) values are OpenFace intensity estimates (0–5) averaged over frames.",
    "Head pose values are mean rotations and head motion values are the root mean square of",
    "successive frame-to-frame differences in rotation, in radians.",
    "The models analyze LIWC percentages divided by 100 and AU intensities divided by 5."
  )))
Table S1
Means and Standard Deviations for Behavioral Features by Group and Task
Trauma Account
Impact Statement
PTSD (n = 48) Control (n = 44) PTSD (n = 48) Control (n = 44)
Verbal
Total Word Count 1636 (590) 1303 (501) 1791 (639) 1339 (689)
1st Person Singular Pronouns (%) 9.42 (1.99) 7.84 (2.94) 10.48 (2.16) 8.00 (2.23)
1st Person Plural Pronouns (%) 0.91 (0.58) 1.27 (1.26) 0.34 (0.22) 0.75 (0.98)
Negative Emotion Words (%) 1.42 (0.89) 1.11 (0.46) 1.05 (0.50) 0.50 (0.47)
Positive Emotion Words (%) 0.38 (0.29) 0.40 (0.32) 0.68 (0.49) 0.43 (0.33)
All-or-None Words (%) 1.27 (0.52) 1.44 (0.76) 1.90 (0.82) 1.60 (1.30)
Cognitive Process Words (%) 13.66 (3.30) 14.14 (2.50) 17.92 (2.20) 18.43 (3.13)
Perception Words (%) 9.30 (2.11) 10.09 (2.32) 7.67 (2.23) 8.52 (2.01)
Sentiment 1.15 (0.41) 1.98 (1.00) 1.67 (0.60) 3.41 (1.28)
Vocal
SD Loudness 1.20 (0.25) 1.05 (0.23) 1.26 (0.23) 1.19 (0.28)
SD Pitch (Hz) 51.0 (19.3) 36.8 (16.9) 53.0 (21.1) 35.8 (15.3)
Pseudo-Syllable Rate (per s) 1.55 (0.37) 1.66 (0.46) 1.55 (0.38) 1.58 (0.43)
Mean Voiced Regions (s) 0.396 (0.151) 0.436 (0.142) 0.382 (0.152) 0.421 (0.147)
Mean Unvoiced Regions (s) 0.579 (0.393) 0.433 (0.371) 0.598 (0.299) 0.512 (0.304)
SD Voiced Regions (s) 0.341 (0.140) 0.378 (0.125) 0.326 (0.139) 0.361 (0.123)
SD Unvoiced Regions (s) 0.507 (0.213) 0.357 (0.190) 0.572 (0.208) 0.507 (0.225)
CPPS (dB) 9.70 (1.69) 9.66 (1.65) 9.57 (1.77) 9.55 (1.58)
H1-H2 (dB) -2.28 (2.75) 0.02 (3.13) -2.52 (2.87) 0.43 (2.96)
Visual
AU4 Brow Lowerer 0.55 (0.51) 0.70 (0.77) 0.55 (0.50) 0.73 (0.86)
AU12 Lip Corner Puller 0.24 (0.23) 0.34 (0.35) 0.31 (0.31) 0.34 (0.34)
AU15 Lip Corner Depressor 0.73 (0.44) 0.68 (0.48) 0.71 (0.43) 0.71 (0.57)
AU17 Chin Raiser 0.33 (0.38) 0.47 (0.53) 0.36 (0.42) 0.48 (0.56)
AU20 Lip Stretcher 0.65 (0.50) 0.75 (0.69) 0.63 (0.47) 0.74 (0.69)
Head Pose Rx, Pitch (rad) -0.04 (0.15) -0.04 (0.16) -0.04 (0.14) -0.04 (0.16)
Head Pose Ry, Yaw (rad) 0.05 (0.11) 0.03 (0.13) 0.06 (0.12) 0.03 (0.14)
Head Motion Rx, Pitch (rad) 0.070 (0.082) 0.050 (0.063) 0.063 (0.078) 0.048 (0.060)
Head Motion Ry, Yaw (rad) 0.075 (0.101) 0.057 (0.089) 0.073 (0.099) 0.058 (0.084)
Note. Values are M (SD) in each feature’s original units. LIWC-22 categories are percentages of total words. Sentiment is the LLM-predicted rating of each transcript (1 = very negative, 7 = very positive). Action unit (AU) values are OpenFace intensity estimates (0–5) averaged over frames. Head pose values are mean rotations and head motion values are the root mean square of successive frame-to-frame differences in rotation, in radians. The models analyze LIWC percentages divided by 100 and AU intensities divided by 5.