This is a step-by-step guide to illustrate the contents of the paper “Quantifying the therapeutic alliance: A methodological framework for conducting observational moment-by-moment research”.
The analyses in this tutorial rely on four custom R functions
(permanent_group, sequence_descriptives,
sequence_heatmap, and sequence_trajectory),
all defined in dyadObs_functions.r. The chunk below
downloads this file to your R working directory and loads it. To find or
edit the file afterwards, run getwd() in the R console — it
will be saved there as dyadObs_functions.r.
#download.file(
# "https://raw.githubusercontent.com/dfernandezregueras/dyadObs/main/dyadObs_functions.r",
# destfile = "dyadObs_functions.r"
#)
source("dyadObs_functions.r")
This tutorial requires four data files. Download each one using the
links below and save all of them to the folder shown by
getwd() in R.
Sometimes, data from different observations are stored in separate
files. To prepare the data, we will merge them into a single file. The
Observer XT provides .xlsx data files that contain several
variables. For the data analysis, only a few of them are relevant:
case1_session1). This variable
represents repeated measures in a long format, as it includes all coded
events within a session.The researcher may choose to retain the remaining variables. However, for simplicity, we will remove them since they are not needed for our analyses. If the researcher chooses to retain the additional variables and the data frames contain different variables (as in our case), the merge function should be used.
library(readxl)
# Load the data files
data_1 <- read_excel("data_1.xlsx")[, c("Observation", "Subject", "Behavior")]
data_2 <- read_excel("data_2.xlsx")[, c("Observation", "Subject", "Behavior")]
data_3 <- read_excel("dyad 31.xlsx")[, c("Observation", "Subject", "Behavior")]
data_4 <- read_excel("dyad 32.xlsx")[, c("Observation", "Subject", "Behavior")]
# Merge the data (only three variables)
data <- rbind(data_1, data_2, data_3, data_4)
library(writexl)
write_xlsx(data, "Previous data.xlsx")
# Maintain the remaining variables
data <- Reduce(function(x, y) merge(x, y, by = c("Observation", "Subject", "Behavior"), all = T),
list(data_1, data_2, data_3, data_4))
library(readxl)
data <- read_excel("Previous data.xlsx")
# ASSIGN ID & Treatment phase
# Visualize the list of Observation sessions
unique_sessions <- unique(data$Observation)
unique_sessions <- as.data.frame(unique_sessions)
# Assign ID and Treatment phases based on the list of Observation sessions
session_lookup <- data.frame(
Observation = unique(data$Observation),
ID = rep(1:32, times = c(5, 5, 5, 5, 5, 5, 3, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 3, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5)),
Treatment_Phase = c("Middle of intervention", "End of intervention", "Assessment", "Treatment planning", "Beginning of intervention",
"End of intervention","Assessment","Treatment planning","Beginning of intervention","Middle of intervention",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"Beginning of intervention","Middle of intervention", "Assessment", "End of intervention", "Treatment planning",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"Beginning of intervention","Middle of intervention","End of intervention","Assessment","Treatment planning",
"Assessment","Treatment planning","Beginning of intervention",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"Assessment","Treatment planning","Beginning of intervention", "End of intervention",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"Beginning of intervention","Middle of intervention","End of intervention","Assessment","Treatment planning",
"Middle of intervention","Assessment", "End of intervention", "Treatment planning","Beginning of intervention",
"Beginning of intervention","Middle of intervention","End of intervention","Assessment","Treatment planning",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"Middle of intervention","End of intervention","Assessment","Beginning of intervention","Treatment planning",
"Beginning of intervention","Middle of intervention","End of intervention","Assessment","Treatment planning",
"Assessment","Treatment planning","Beginning of intervention",
"End of intervention","Assessment","Treatment planning","Beginning of intervention","Middle of intervention",
"Beginning of intervention","Middle of intervention","Assessment","End of intervention","Treatment planning",
"Beginning of intervention","Middle of intervention","Assessment","End of intervention","Treatment planning",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"Assessment","Treatment planning","Beginning of intervention","End of intervention",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"End of intervention","Assessment","Treatment planning","Beginning of intervention","Middle of intervention",
"Beginning of intervention","Middle of intervention","End of intervention","Assessment","Treatment planning",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"End of intervention","Assessment","Treatment planning","Beginning of intervention","Middle of intervention",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention",
"Middle of intervention","End of intervention","Assessment","Treatment planning","Beginning of intervention"
)
)
# Merge the assigned IDs and Treatment phases with the rest of data
library(dplyr)
data_merged <- left_join(data, session_lookup, by = "Observation")
# Check phases' names for potential typos
#unique(data_merged$Treatment_Phase)
##################################################################################
# ASSIGN GROUPS
# Visualize the list of IDs
unique_ID <- unique(data_merged$ID)
unique_ID <- as.data.frame(unique_ID)
# Assign Groups based on the list of IDs
ID_lookup <- data.frame(
ID = unique(data_merged$ID),
Group = c("Poor", "Poor", "Good", "Poor", "Poor", "Good", "Poor", "Good", "Good", "Poor", "Good", "Good", "Poor", "Good", "Good", "Poor", "Poor", "Good", "Poor", "Good", "Average", "Average", "Average", "Average", "Average", "Average", "Average", "Average", "Average", "Average", "Average", "Average")
)
# Merge the assigned Grups with the rest of data
data_complete <- left_join(data_merged, ID_lookup, by = "ID")
# Check Groups' names for potential typos
#unique(data_complete$Group)
head(data_complete)
library(writexl)
write_xlsx(data_complete, "Preprocessed data - phase names.xlsx")
Some behaviors, either for theoretical reasons or due to their low
frequency, need to be grouped. In future sections, we will present a
more flexible approach for grouping behaviors for specific analyses
without the need to permanently modify the dataset. However, some
researchers may prefer to group behaviors permanently beforehand if they
are certain that these behaviors should remain unified under the same
label. In such cases, researchers may find the
permanent_group function useful.
permanent_group allows researchers to group behaviors
under a single label before conducting analyses. Its simple usage
requires researchers to input the names of the behaviors to be grouped,
separated by a + symbol, as well as the name of the
variable that store the behaviors and the name of the new grouped
behavior created.library(readxl)
data <- read_excel("Preprocessed data - phase names.xlsx")
As an example, the client behaviors Partial agreement
and Disagreement, which are theoretically related, will be
unified into the new behavior Group_1.
data <- permanent_group(data, "Behavior", "Partial agreement C + Disagreement C", "Group_1")
After preprocessing the data and obtaining the final dataset, it is always good practice to store it safely to avoid losing information and repeating the entire process.
library(writexl)
write_xlsx(data, "Preprocessed data.xlsx")
Descriptive data will be useful for future decisions regarding the
analyses. To provide a comprehensive description of the data, the
function sequence_descriptives is able to provide
descriptive statistics for both individual behaviors and sequences of an
indefinite number of behaviors. The contents of this section will,
therefore, apply to both individual behaviors and sequences.
The sequence_descriptives function is designed to
analyze the relative frequencies of behavior sequences across multiple
phases of treatment. It provides descriptive statistics, tests for
normality (Shapiro-Wilk test), and generates histograms with density
distributions to visualize the distribution of behaviors/sequences. The
function is highly flexible, allowing researchers to group behaviors
using the + symbol without permanently altering the
dataset.
Flexible Behavior Grouping:
+ symbol
(e.g., "BehaviorA + BehaviorB") to treat them as the same
during analysis.Descriptive Statistics:
p-value and a variable indicating whether the
distribution is Normal or Not normal.Visualizations:
Group Analysis:
Relative or Absolute Frequencies:
Requested Libraries Handling:
dplyr,
ggplot2, gridExtra, e1071 and
stats [included in the in base R]) and provides a warning
if they are not installed.| Argument | Description |
|---|---|
data |
The dataset containing the observations. It should be a
data.frame where each row represents a subject’s
observation within a phase of the treatment. |
treatment_phase_var |
The name of the variable that defines the treatment phase in the
dataset (e.g., "Treatment_Phase"). This variable is used to
group observations by phase. |
phase_order |
A vector specifying the order of the treatment phases to be
analyzed. Example: c("Phase1", "Phase2", "Phase3"). If
NULL, phases are ordered as they appear in the data. |
behavior_var |
The name of the variable containing the observed behaviors for each
subject (e.g., "Behavior"). This variable should include
behavior sequences to analyze. |
sequence |
A vector or list of strings specifying the behavior sequence to
analyze. Use + to group behaviors (e.g.,
c("BehaviorA + BehaviorB", "BehaviorC"). |
combine_plots |
A logical value (TRUE or FALSE). If
TRUE, boxes for all groups are combined into a single
display. If FALSE, plots are displayed separately for each
group. |
group_var |
The name of the grouping variable used to divide the data into
different groups (e.g., "Group"). If NULL, the
data is analyzed as a whole. |
selected_groups |
A vector of group names to filter and analyze. Example:
c("Group1", "Group2"). If NULL, all groups in
group_var are analyzed. |
use_relative_frequency |
A logical value (TRUE or FALSE). If
TRUE, relative frequencies will be computed. If
FALSE, absolute frequencies will be computed. |
library(readxl)
data <- read_excel("Preprocessed data - phase names.xlsx")
results <- sequence_descriptives(
data = data,
treatment_phase_var = "Treatment_Phase",
phase_order = c("Assessment", "Treatment planning", "Beginning of intervention",
"Middle of intervention", "End of intervention"),
behavior_var = "Behavior",
sequence = c("Empathy T", "Total agreement C"),
combine_plots = T, # FALSE for separate plots per group
group_var = "Group",
selected_groups = c("Good", "Poor"),
use_relative_frequency = T
)
library(DT)
stats_df <- results$statistics
numeric_cols <- names(stats_df)[sapply(stats_df, is.numeric)]
datatable(stats_df,
options = list(
pageLength = 5,
scrollX = TRUE,
dom = 'Bfrtip'
)) %>%
formatRound(columns = setdiff(numeric_cols, "Sample_Size"), digits = 3) %>%
formatRound(columns = "Sample_Size", digits = 0)
The sequence_heatmap function is designed to analyze and
visualize individual or sequences of behaviors. It generates a heatmap
that displays the absolute or relative frequency of specific behaviors
or sequences of behaviors across different moments (e.g., treatment
phases). The function is highly customizable, allowing users to define
behaviors/sequences and treatment phases, as well as specify the type of
data desired (absolute or relative frequency). The researcher will also
be able to flexibly group behaviors that they want to be treated as the
same using a +, without permanently changing the data
frame. It also allows for group comparison.
"BehaviorA + BehaviorB"), treating them as one single
behavior.ggplot2, with
behaviors/sequences on the y-axis and treatment phases on the
x-axis.custom_sequences."Good",
"Poor") if a group_var is provided.dplyr,
ggplot2, tidyr, purrr,
stringr) and installs them if they are not installed.| Argument | Description |
|---|---|
data |
The input dataset containing behavioral data. This should be a data frame or with columns for behaviors, treatment phases, session identifiers and, optionally, groups. |
behavior_col |
The column name in data that contains behaviors
(default: "Behavior"). |
phase_col |
The column name in data that contains treatment phases
(default: "Treatment_Phase"). This column should categorize
observations into distinct phases. |
session_col |
The column name in data that contains session
identifiers (default: "Observation"). This column should
uniquely identify each session or observation. |
custom_sequences |
A vector of behaviors or sequences to analyze. Provide a
list of behaviors and/or sequences using the
c() command. Example:
list(c("BehaviorA + BehaviorB", "BehaviorC"), c("BehaviorD", "BehaviorE"), c("Behavior F + Behavior G")). |
phase_levels |
A vector specifying the order of treatment phases (default:
NULL). Introduce a c() with the ordered names
of the phases. Example: c("Phase1", "Phase2", "Phase3"). If
NULL, phases are ordered as they appear in the data. |
freq_type |
The type of frequency to compute: "absolute" (default)
or "relative". Use "absolute" for raw counts
or "relative" for proportions relative to total
behaviors/sequences in each session. It computes the mean value across
all sessions in each treatment phase. |
custom_order |
The ordering of behaviors/sequences in the heatmap:
"personalized" (default) or "frequency". Use
"personalized" to maintain the order in which
behaviors/sequences were introduced in custom_sequences.
Use "frequency" to order behaviors/sequences from higher to
lower total frequency across all phases. |
group_var |
The column name in data that contains group identifiers
(default: NULL). If provided, the function will generate
separate heatmaps for each group. Example: "Group". |
selected_groups |
A vector of groups to include in the analysis (default:
NULL). If provided, only the specified groups will be
analyzed. Example: c("Good", "Poor"). |
The function returns a heatmap or various heatmaps depending if the group argument includes a group variable. The following callings show different possible outputs.
sequence_heatmap(
data = data,
behavior_col = "Behavior",
phase_col = "Treatment_Phase",
session_col = "Observation",
custom_sequences = list(
c("Task with justification T", "Total agreement C"),
c("Task with justification T", "Disagreement C + Partial agreement C"),
c("Task with justification T", "Partial agreement C"),
c("Task without justification T", "Total agreement C"),
c("Feedback task T", "Total agreement C"),
c("Empathy T", "Total agreement C"),
c("Present success T", "Total agreement C")
),
phase_levels = c("Assessment", "Treatment planning", "Beginning of intervention", "Middle of intervention", "End of intervention"),
freq_type = "relative",
custom_order = "personalized",
group_var = "Group",
selected_groups = c("Good", "Poor")
)
sequence_heatmap(
data = data,
behavior_col = "Behavior",
phase_col = "Treatment_Phase",
session_col = "Observation",
custom_sequences = list(
c("Total agreement T"),
c("Empathy T"),
c("Partial agreement C"),
c("Task without justification T", "Total agreement C"),
c("Partial agreement C + Disagreement C")),
phase_levels = c("Assessment", "Treatment planning", "Beginning of intervention", "Middle of intervention", "End of intervention"),
freq_type = "absolute",
custom_order = "frequency",
group_var = NULL,
selected_groups = c("Good", "Poor") # Only works if group_var is not NULL
)
The sequence_trajectory function models how specific
behavior sequences evolve across treatment phases using a standardized
metric to allow comparison between different behaviors/sequences and
different groups. It enables researchers to explore patterns of change
(e.g., increasing, decreasing, stable) in the relative frequency of
sequences, optional comparisons between groups. The function supports
multiple polynomial shapes (linear, quadratic, cubic) to fit and
visualize behavioral trends.
"Good" vs
"Poor"), enabling comparative analysis.+
operator (e.g., "BehaviorA + BehaviorB") for evaluating
equivalent/similar behaviors.ggplot2 with clear
phase separation and optional faceting by group.| Argument | Description |
|---|---|
data |
The input data frame containing behavioral observation data. |
sequence |
A character vector (or list of vectors) representing the target behavior and/or sequence(s) to analyze. Each sequence is matched as an exact temporal pattern. |
behavior_var |
The column name containing behavior codes (default:
"Behavior"). |
phase_var |
The column name indicating treatment phases (default:
"Treatment_Phase"). |
phase_order |
Optional. A vector specifying the desired order of phases. If
NULL, the function uses the factor levels in the data. |
observation_var |
Column identifying individual observations/sessions (default:
"Observation"). |
id_var |
Column identifying unique subjects or participants (default:
"ID"). |
group_variable |
Optional. The column name for a grouping variable (e.g.,
"Group"). Required if comparing across groups. |
groups_compared |
Optional. A vector of group labels to include in the analysis (e.g.,
c("Treatment", "Control")). Required if
group_variable is specified. |
shapes |
A character vector indicating the polynomial shapes to fit:
"linear", "quadratic", and/or
"cubic" (default includes all three). |
ci_method |
The method used to compute confidence intervals in the plots.
Options are "t" (default, using the
t-distribution) and "bootstrap" (using bootstrap
percentile confidence intervals). |
boot_seed |
Optional integer used to make bootstrap confidence intervals
reproducible. Only relevant when ci_method = "bootstrap".
If NULL (default), the random-number stream is not seeded
and error bars will vary slightly between runs. |
The function returns a list with plots and model information:
all_sequences_plot) showing
the relative frequency trajectories across phases.comparison_plot showing trajectory plots side-by-side
across groups for all sequences.Each plot includes: - Individual lines for each subject. - Group average trajectory with confidence intervals. - Best-fit polynomial model (in red) with AIC info.
The output shown answers the research question How do
behaviors/sequences evolve over time?, so the
group_variable argument is NULL.
# Two single behaviors compared
result <- sequence_trajectory(
data = data,
sequence = list(c("Task with justification T"), c("Empathy T")),
behavior_var = "Behavior",
phase_var = "Treatment_Phase",
phase_order = c("Assessment", "Treatment planning", "Beginning of intervention", "Middle of intervention", "End of intervention"),
observation_var = "Observation",
id_var = "ID",
group_variable = NULL,
groups_compared = NULL,
shapes = c("linear", "quadratic", "cubic")
)
result[["all_sequences_plot"]]
The sequence_trajectory allows for comparisons between
groups using the argument group_variable, which needs to be
filled with the name of the variable that contains the groups’ names. In
the argument groups_compared, we need to indicate which
groups from the group_variable we want to compare.
result <- sequence_trajectory(
data = data,
sequence = list(c("Task with justification T", "Total agreement C")),
behavior_var = "Behavior",
phase_var = "Treatment_Phase",
phase_order = c("Assessment", "Treatment planning", "Beginning of intervention", "Middle of intervention", "End of intervention"),
observation_var = "Observation",
id_var = "ID",
group_variable = "Group",
groups_compared = c("Good", "Poor"),
shapes = c("linear", "quadratic", "cubic")
)
result[["comparison_plot"]]
result <- sequence_trajectory(
data = data,
sequence = list(c("Summarizing T + Clarifying T + Finishing sentence T", "Total agreement C"), c("Empathy T", "Total agreement C")),
behavior_var = "Behavior",
phase_var = "Treatment_Phase",
phase_order = c("Assessment", "Treatment planning", "Beginning of intervention", "Middle of intervention", "End of intervention"),
observation_var = "Observation",
id_var = "ID",
group_variable = "Group",
groups_compared = c("Good", "Poor"),
shapes = c("linear", "quadratic", "cubic")
)
result[["comparison_plot"]]