Documentation for fxl

Drawing Reversal Designs in R: FCT + Schedule Thinning

Written by Shawn P. Gilroy (Last Updated: 2024-05-29)Reversal DesignsArrowsTextPhase Changes

The goal of this post is to demonstrate a common combination of intervention elements (Functional Communication Training + Extinction for Problem Behavior). In the simplest design possible here, we would use a reversal design to evaluate covariation between condition type and levels of unsafe behavior.

An representative example of this type of evaluation is provided below.

Key Features for Figure

There are a combination of features necessary to achieve plots characteristic of figures used in this specific context. These include phase line/condition markers, line and marker styling that varies based on to phase, and the ability to create annotations for labeling how/when ecological features vary.

Data Structure

There are two distinct change measures here – Problem Behavior and the Functional Communication Response (FCR). There isn’t a need for multiple Problem Behavior columns since we can more simply separate related data by phase (styling them as useful).

A snapshot of representative data is shown below:


head(csv_data)
##   Participant Session        Phase ProblemBx FCR
## 1           A       1     Baseline       4.3  NA
## 2           A       2     Baseline       3.3  NA
## 3           A       3     Baseline       3.1  NA
## 4           A       4     Baseline       4.2  NA
## 5           A       5 Intervention       1.1 3.2
## 6           A       6 Intervention       0.2 2.4

Plot Elements

Plot construction begins by first establishing the primary change measure (Session and Problem Behavior). This consists of creating the scr_plot with the associated aesthetic mappings with associated points and lines.


scr_plot(csv_data,
    aesthetics = var_map(
      x = Session,
      y = ProblemBx,
      p = Phase
      # Note: Not necessary to specific faceting if just 1 panel exists
      #facet = Participant
    ),
    mai = c(0.375, 0.375, 0.25, .25),
    omi = c(0.25, 0.25, 0.25, 0.25),

    # Note: User must ensure relevant fonts are install on machine
    family = "Times New Roman"
  ) |>
  scr_lines() |>
  scr_points()

Axis and Tick Styling

By default, the axes labels and ticks will be inferred and are unlikely to map closely with the desired final product. These can be overridden to align more closely with the desired output.


scr_plot(csv_data,
    aesthetics = var_map(
      x = Session,
      y = ProblemBx,
      p = Phase
      #facet = Participant
    ),
    mai = c(0.375, 0.375, 0.25, .25),
    omi = c(0.25, 0.25, 0.25, 0.25),
    family = "Times New Roman"
  ) |>
  scr_yoverride(c(-0.25, 5),
                yticks = c(0, 1, 2, 3, 4, 5)) |>
  scr_xoverride(c(-0.25, 30),
                xticks = c(1, 5, 10, 15, 20, 25, 30)) |>
  scr_xlabel("Session",
             cex = 1.2,
             adj = 0.525) |>
  scr_ylabel("Behavior (Rate Per Minute)",
             cex = 1.2,
             adj = 0.55) |>
  scr_lines() |>
  scr_points()

Line and Marker Styling

At present, little visual information communicates differences associated with conditions and point styling would benefit from edits to make the contrast more salient. The code above is easily expanded to map styles for points for respective Phase.


scr_plot(csv_data,
    aesthetics = var_map(
      x = Session,
      y = ProblemBx,
      p = Phase
      #facet = Participant
    ),
    mai = c(0.375, 0.375, 0.25, .25),
    omi = c(0.25, 0.25, 0.25, 0.25),
    family = "Times New Roman"
  ) |>
  scr_yoverride(c(-0.25, 5),
                yticks = c(0, 1, 2, 3, 4, 5)) |>
  scr_xoverride(c(-0.25, 30),
                xticks = c(1, 5, 10, 15, 20, 25, 30)) |>
  scr_xlabel("Session",
             cex = 1.2,
             adj = 0.525) |>
  scr_ylabel("Behavior (Rate Per Minute)",
             cex = 1.2,
             adj = 0.55) |>
  scr_lines() |>
  scr_points(
    cex = 1.5,
    pch = list(
      "Baseline" = 21,
      "Intervention" = 21,
      "Baseline2" = 21,
      "Intervention2" = 21
    ),
    fill = list(
      "Baseline" = "black",
      "Intervention" = "white",
      "Baseline2" = "black",
      "Intervention2" = "white"
    )
  )

Adding Additional Series

The figure is beginning to come together, but it is critical to also reflect the relationship between the Functional Communication Response (FCR) and Problem Behavior. The figure is easily expanded by adding another series with a custom mapping, see below:


scr_plot(csv_data,
    aesthetics = var_map(
      x = Session,
      y = ProblemBx,
      p = Phase
      #facet = Participant
    ),
    mai = c(0.375, 0.375, 0.25, .25),
    omi = c(0.25, 0.25, 0.25, 0.25),
    family = "Times New Roman"
  ) |>
  scr_yoverride(c(-0.25, 5),
                yticks = c(0, 1, 2, 3, 4, 5)) |>
  scr_xoverride(c(-0.25, 30),
                xticks = c(1, 5, 10, 15, 20, 25, 30)) |>
  scr_xlabel("Session",
             cex = 1.2,
             adj = 0.525) |>
  scr_ylabel("Behavior (Rate Per Minute)",
             cex = 1.2,
             adj = 0.55) |>
  scr_lines() |>
  scr_points(
    cex = 1.5,
    pch = list(
      "Baseline" = 21,
      "Intervention" = 21,
      "Baseline2" = 21,
      "Intervention2" = 21
    ),
    fill = list(
      "Baseline" = "black",
      "Intervention" = "white",
      "Baseline2" = "black",
      "Intervention2" = "white"
    )
  ) |>
  scr_lines(color = 'darkcyan',
            mapping = list( x = Session, y = FCR)) |>
  scr_points(pch = 23,
             cex = 1.5,
             fill = 'darkcyan',
             mapping = list(x = Session, y = FCR))

Annotations

The majority of empirical information is not present in the form, but additional information is necessary to understand exactly what is being contrasted and to what degree those differences are large and clear.

Phase Change Lines

Let us first add phase change lines to make these conditions appear clearly separated. We will add three phase change lines using the scr_plines function, with a line added between on end of one phase and the onset of another. In the simplest of cases, one would only need to key in the X (x1) and upper Y (y1) values for a line, but since we added a bit of space (-0.25) to not have 0 on the Y axis overlap with the X axis, we would specify that we want the lower limit (y2) to be -0.25 instead of the default of 0.


scr_plot(csv_data,
    aesthetics = var_map(
      x = Session,
      y = ProblemBx,
      p = Phase
      #facet = Participant
    ),
    mai = c(0.375, 0.375, 0.25, .25),
    omi = c(0.25, 0.25, 0.25, 0.25),
    family = "Times New Roman"
  ) |>
  # Note: We add -0.25 instead of 0 to add some space
  scr_yoverride(c(-0.25, 5),
                yticks = c(0, 1, 2, 3, 4, 5)) |>
  scr_xoverride(c(-0.25, 30),
                xticks = c(1, 5, 10, 15, 20, 25, 30)) |>
  scr_xlabel("Session",
             cex = 1.2,
             adj = 0.525) |>
  scr_ylabel("Behavior (Rate Per Minute)",
             cex = 1.2,
             adj = 0.55) |>
  scr_lines() |>
  scr_points(
    cex = 1.5,
    pch = list(
      "Baseline" = 21,
      "Intervention" = 21,
      "Baseline2" = 21,
      "Intervention2" = 21
    ),
    fill = list(
      "Baseline" = "black",
      "Intervention" = "white",
      "Baseline2" = "black",
      "Intervention2" = "white"
    )
  ) |>
  scr_lines(color = 'darkcyan',
            mapping = list( x = Session, y = FCR)) |>
  scr_points(pch = 23,
             cex = 1.5,
             fill = 'darkcyan',
             mapping = list(x = Session, y = FCR))  |>
  scr_plines(lty = 1,
             lines = list(
               "1" = list(x1 = 4.5,
                          y1 = 5,
                          # Note: y2 (end) is -0.25 and not 0 because we added additional whitespace
                          y2 = -0.25),
               "2" = list(x1 = 8.5,
                          y1 = 5,
                          y2 = -0.25),
               "3" = list(x1 = 11.5,
                          y1 = 5,
                          y2 = -0.25)))

Phase Labels (Text)

We can make this figure even more clear by adding text annotations to help the reader infer what conditions the data correspond with. The scr_label_phase function can be use to specify labels within a specific facet or panel, which is what is relevant here.

The scr_label_phase function allows for some working defaults (e.g., shared height [y]) as well as more specific overrides for keyed labels. As a general rule, the key will be used as the relevant text unless otherwise specified. For Phase labels (e.g., “BL”), each of these are mostly cleanly portrayed at the same height, so a common y is specified and keyed entries vary only by their inferred label (from key) and x value.


scr_plot(csv_data,
    aesthetics = var_map(
      x = Session,
      y = ProblemBx,
      p = Phase
      #facet = Participant
    ),
    mai = c(0.375, 0.375, 0.25, .25),
    omi = c(0.25, 0.25, 0.25, 0.25),
    family = "Times New Roman"
  ) |>
  # Note: We add -0.25 instead of 0 to add some space
  scr_yoverride(c(-0.25, 5),
                yticks = c(0, 1, 2, 3, 4, 5)) |>
  scr_xoverride(c(-0.25, 30),
                xticks = c(1, 5, 10, 15, 20, 25, 30)) |>
  scr_xlabel("Session",
             cex = 1.2,
             adj = 0.525) |>
  scr_ylabel("Behavior (Rate Per Minute)",
             cex = 1.2,
             adj = 0.55) |>
  scr_lines() |>
  scr_points(
    cex = 1.5,
    pch = list(
      "Baseline" = 21,
      "Intervention" = 21,
      "Baseline2" = 21,
      "Intervention2" = 21
    ),
    fill = list(
      "Baseline" = "black",
      "Intervention" = "white",
      "Baseline2" = "black",
      "Intervention2" = "white"
    )
  ) |>
  scr_lines(color = 'darkcyan',
            mapping = list( x = Session, y = FCR)) |>
  scr_points(pch = 23,
             cex = 1.5,
             fill = 'darkcyan',
             mapping = list(x = Session, y = FCR))  |>
  scr_plines(lty = 1,
             lines = list(
               "1" = list(x1 = 4.5,
                          y1 = 5,
                          y2 = -0.25),
               "2" = list(x1 = 8.5,
                          y1 = 5,
                          y2 = -0.25),
               "3" = list(x1 = 11.5,
                          y1 = 5,
                          y2 = -0.25))) |>
  scr_label_phase(cex = 0.9,
                  adj = 0.5,
                  y = 5.25,
                  labels = list(
                    "BL" = list(
                      x = 2
                    ),
                    "FCT + EXT" = list(
                      x = 6.5
                    ),
                    "BL" = list(
                      x = 10
                    ),
                    "FCT + EXT" = list(
                      x = 13.5
                    )))

Data Annotation (Text + Arrows)

The final details needed to replicate the final figure are to add additional text labels with associated arrows to orient the viewer that what is represented by the data.

Additional text is added in relevant areas by expanding upon the labels provided to the previous scr_label_phase function.

Arrows are added to the figure by using the scr_anno_arrows function. Like other functions, the call takes a list of keyed entries (i.e., arrows to be drawn) with some general defaults to reduce redundancy (i.e., length = arrow head size). Each keyed entry takes a pair of X/Y values (e.g., x0 = start X, x1 = end X) to represent the start and end points. These should be matched to representative labels to give context to what is being referenced and why.


scr_plot(csv_data,
  aesthetics = var_map(
    x = Session,
    y = ProblemBx,
    p = Phase
    #facet = Participant
  ),
  mai = c(0.375, 0.375, 0.25, .25),
  omi = c(0.25, 0.25, 0.25, 0.25),
  family = "Times New Roman"
) |>
  scr_yoverride(c(-0.25, 5),
                yticks = c(0, 1, 2, 3, 4, 5)) |>
  scr_xoverride(c(-0.25, 30),
                xticks = c(1, 5, 10, 15, 20, 25, 30)) |>
  scr_lines(size = 1) |>
  scr_points(
    cex = 1.5,
    pch = list(
      "Baseline" = 21,
      "Intervention" = 21,
      "Baseline2" = 21,
      "Intervention2" = 21
    ),
    fill = list(
      "Baseline" = "black",
      "Intervention" = "white",
      "Baseline2" = "black",
      "Intervention2" = "white"
    )
  ) |>
  scr_lines(color = 'darkcyan',
            mapping = list( x = Session, y = FCR)) |>
  scr_points(pch = 23,
             cex = 1.5,
             fill = 'darkcyan',
             mapping = list(x = Session, y = FCR))  |>
  scr_anno_arrows(length = 0.1,
                  arrows = list(
                    "BL" = list(x0 = 2.5,
                                x1 = 2.5,
                                y0 = 2.5,
                                y1 = 3.0),
                    "FCR" = list(x0 = 6.5,
                                 x1 = 6.5,
                                 y0 = 3.5,
                                 y1 = 3),
                    "SCT" = list(x0 = 15,
                                 x1 = 15,
                                 y0 = 1.75,
                                 y1 = 2.25),
                    "PT" = list(x0 = 27,
                                x1 = 27,
                                y0 = 1.65,
                                y1 = 1.125)
                  )) |>
  scr_plines(lty = 1,
             lines = list(
               "1" = list(x1 = 4.5,
                          y1 = 5,
                          y2 = -0.25),
               "2" = list(x1 = 8.5,
                          y1 = 5,
                          y2 = -0.25),
               "3" = list(x1 = 11.5,
                          y1 = 5,
                          y2 = -0.25))) |>
  scr_label_phase(cex = 0.9,
                  adj = 0.5,
                  y = 5.25,
                  labels = list(
                    "BL" = list(
                      x = 2
                    ),
                    "FCT + EXT" = list(
                      x = 6.5
                    ),
                    "BL" = list(
                      x = 10
                    ),
                    "FCT + EXT" = list(
                      x = 13.5
                    ),
                    "Problem\nBehavior" = list(
                      x = 2.5,
                      y = 2.25
                    ),
                    "FCR" = list(
                      x = 6.5,
                      y = 3.625
                    ),
                    "Schedule\nThinning" = list(
                      x = 15,
                      y = 1.5
                    ),
                    "5s" = list(
                      x = 15,
                      y = 2.75
                    ),
                    "15s" = list(
                      x = 17,
                      y = 2
                    ),
                    "30s" = list(
                      x = 19,
                      y = 1.75
                    ),
                    "60s" = list(
                      x = 21,
                      y = 1.75
                    ),
                    "90s" = list(
                      x = 23,
                      y = 1.625
                    ),
                    "180s" = list(
                      x = 25,
                      y = 1.25
                    ),
                    "Parent\nImplementation" = list(
                      x = 27,
                      y = 2
                    ))) |>
  scr_xlabel("Session",
             cex = 1.2,
             adj = 0.525) |>
  scr_ylabel("Behavior (Rate Per Minute)",
             cex = 1.2,
             adj = 0.55)

Final Object and File Export

As a final step, figures can be exported in any number of formats and dimensions. R is quite capable for producing rich visual outputs, so as a working default, I generally prefer to use vector graphics to maximize quality (i.e., no blurriness) and compatibility (e.g., png vs. jpg vs. webp).

Figures can be saved by adding a scr_save function call (use at the end to ensure all previous layers are included). You will need to give a name (and potentially a non working directory location) along with height, width, and format. Rasters (e.g., png output) can be adjusted for quality using the dpi argument to adjust density (i.e., higher = better quality, larger size).


  ... (Previous Code) |>
  scr_save(
    name = "FCT Treatment Evaluation.svg",
    format = "svg",
    height = 6,
    width = 9
  )