Code
library(tidyverse)
library(gt)Supplemental Table S1
library(tidyverse)
library(gt)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"
)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 |
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. | ||||