Documentation for fxl

Concurrent Chains: Multi-Column Single-case Figures

Written by Shawn P. Gilroy (Last Updated: 2024-06-08)Concurrent ChainsFacetingHybrid Figures

This post covers an increasingly common type of plotting convention–the concurrent chains method of visualizing choice over time. The procedure is not particularly complex, thought it can be difficult to visualize as conditions change. This is particularly distinct because there is a focus on cumulative responses over time (i.e., running total), rather than responses specific to a session/observation.

The fxl R packages simplifies the charting of these data by extending plotting methods to include cumulative extensions of plotting methods–scr_cumsum_lines and scr_cumsum_points. Although one could manually plot such lines/points by hand-calculating values, this naturally invites human error, and the fxl works by calculating a running tally of observations (i.e., 1 = response, 0 = non-response for each opportunity).

Concurrent Chains Figure Conventions

This post features data published by Lozy et al. (2021), in which the authors evaluated efficacy of, and individual preference for, multiple approaches for supporting early literacy. This post focuses on how to replicate a complex concurrent chains visual with multiple participants, with choices differing across multiple phases (i.e., changes in conditions).

The final result of this figure using the package is displayed below:

Format for Choice Data

Numerous approaches for representing data are possible, though in practice, I find it best to have data remain data and not modify data to visualize an outcome (i.e., separation of roles [Model/View]). In this data set, choices are registered as 1s and 0s, with 1s representing a selection and 0s representing a non-selection. In this manner, the software can determine when or when not to increment, and this reduces risks of human error (especially for complex arrangements).

A preview of of this data structure is shown below:


head(csv_data)
##   X Session Participant KM TD Phase
## 1 1       1         Eli  0  1     1
## 2 2       2         Eli  0  1     1
## 3 3       3         Eli  0  1     1
## 4 4       4         Eli  0  1     1
## 5 5       5         Eli  0  1     1
## 6 6       6         Eli  0  1     1

Initial Base Plot of Selections

At base defaults, the figure presented below could be improved substantially. There is a lot of variability in the number of selections provided (X-axis) and the range of selections registered (Y-axis).

The initial code for this plot is shown below:


scr_plot(data = csv_data,
         aesthetics = var_map(x = Session,
                              y = KM,
                              p = Phase,
                              facet = Participant),
         mai = c(0.3, 0.3, 0.1, 0.1),
         omi = c(0.25, 0.25, 0.1, 0.1),
         family = "Times New Roman") |>
  scr_cumsum_lines() |>
  scr_cumsum_points(pch = 24,
                    fill = "white",
                    cex = 1.75) |>
  scr_cumsum_lines(
    mapping = list(
      x = Session,
      y = TD
    )) |>
  scr_cumsum_points(
    pch = 22,
    fill = "white",
    cex = 1.75,
    mapping = list(
      x = Session,
      y = TD
    )
  ) |>
  scr_yoverride(c(0, 15)) |>
  scr_ylabel("Cumulative Number of Selections",
             adj = 0.55) |>
  scr_legend(
    position = "right",
    panel = "Eli",
    legend = c(
      "KM",
      "TD"
    ),
    col = c(
      "black",
      "black"
    ),
    pt_bg = c(
      "white",
      "white"
    ),
    lty = c(
      1,
      1
    ),
    pch = c(
      24,
      22
    ),
    bty = "y",
    pt_cex = 2.25,
    cex = 1.25,
    text_col = "black",
    horiz = FALSE,
    box_lty = 1
  )

Faceting across Rows and Columns

We can condense some of the information provided in this figure by adding additional columns to the figure (Note: by default 1 column is presumed). This is easily adjusted using the ncols argument in the scr_plot call.

See below:


scr_plot(data = csv_data,
         aesthetics = var_map(x = Session,
                              y = KM,
                              p = Phase,
                              facet = Participant),
         # Note: ncols = 2 means the facets will wrap across columns
         ncol = 2,
         mai = c(0.3, 0.3, 0.1, 0.1),
         omi = c(0.25, 0.25, 0.1, 0.1),
         family = "Times New Roman") |>
  ...

Overriding Y Axis for Specific Participants

The individual plots (i.e., faceted by Participant) can be adjusted by keying in specific ranges and tick marks for each participant.

See the list supplied to scr_yoverride, shown below:


  ... |>
  scr_yoverride(
    list(
      "Eli" = list(
        y0 = -1,
        y1 = 15,
        yticks = c(0, 5, 10, 15)
      ),
      "Ari" = list(
        y0 = -1,
        y1 = 15,
        yticks = c(0, 5, 10, 15)
      ),
      "Al" = list(
        y0 = -0.5,
        y1 = 8,
        yticks = c(0, 2, 4, 6, 8)
      ),
      "Ry" = list(
        y0 = -0.5,
        y1 = 8,
        yticks = c(0, 2, 4, 6, 8)
      ),
      "Eva" = list(
        y0 = -0.5,
        y1 = 8,
        yticks = c(0, 2, 4, 6, 8)
      ),
      "Cali" = list(
        y0 = -0.5,
        y1 = 8,
        yticks = c(0, 2, 4, 6, 8)
      )
    )
  ) |> ...

Overriding X Axis for Specific Participants

Likewise, we can also adjust the plotting settings with keyed styles for the X-axis. This is done in a similar manner to the Y-axis adjustments, though the scr_xoverride function is used instead. Various arguments can be supplied to address the large proportion of whitespace featured for individual participants.

Code supplying the relevant arguments to scr_xoverride is listed below:


  ... |>
  scr_xoverride(
    c(0, 30),
    xdraws = c(
      "Eva",
      "Cali",
      "Ari"
    ),
    xticks = list(
      "Eli" = c(1, 5, 10, 15),
      "Ari" = c(1, 10, 20, 30),
      "Al" = c(1, 5, 10, 15),
      "Ry" = c(1, 5, 10, 15),
      "Eva" = c(1, 5, 10, 15),
      "Cali" = c(1, 5, 10, 15)
    )
  ) |>
  ...

Adding Necessary Phase Change Lines

Individual participants have varying choice conditions in these data. We can key in individual phase changes lines as we have done before (Note: dashed line to reflect changes in context, but modifications to programmed contingencies).

See below:


  ... |>
  scr_plines(
    lty = 3,
    lines = list(
      "Ari" = list(
        "A" = list(
          x1 = 11.5,
          y1 = 15
        )
      ),
      "Al" = list(
        "A" = list(
          x1 = 4.5,
          y1 = 8
        )
      ),
      "Cali" = list(
        "A" = list(
          x1 = 8.5,
          y1 = 8
        )
      ),
      "Ry" = list(
        "A" = list(
          x1 = 9.5,
          y1 = 8
        )
      ),
      "Eva" = list(
        "A" = list(
          x1 = 4.5,
          y1 = 8
        ),
        "B" = list(
          x1 = 8.5,
          y1 = 8
        )
      )
    )
  ) |>
  ...

Adding Remaining Figure Labels

From here, we can add in the remaining text labels needed to describe the phases and facets displayed in the figure.


  ... |>
  # Note: These are *across* facets
  scr_label_facet(
    cex = 1.5,
    adj = 1,
    x = 15,
    y = 1,
    labels = list(
      "Eli" = list(
        y = 2
      ),
      "Ari" = list(
        x = 30,
        y = 2
      ),
      "Al",
      "Cali",
      "Ry",
      "Eva"
    )
  ) |>
  # Note: These are *within* facets
  scr_label_phase(
    facet = "Eli",
    cex = 1.25,
    adj = 0.5,
    x = 6,
    y = 15,
    labels = list(
      "Choice 1"
    )
  ) |>
  scr_label_phase(
    facet = "Ari",
    cex = 1.25,
    adj = 0.5,
    y = 15,
    labels = list(
      "Choice 1" = list(
        x = 5
      ),
      "Choice 2" = list(
        x = 20
      )
    )
  ) |>
  scr_label_phase(
    facet = "Al",
    cex = 1.25,
    adj = 0.5,
    y = 8,
    labels = list(
      "Choice 1" = list(
        x = 2.25
      ),
      "Choice 2" = list(
        x = 11
      )
    )
  ) |>
  scr_label_phase(
    facet = "Ry",
    cex = 1.25,
    adj = 0.5,
    y = 8,
    labels = list(
      "Choice 1" = list(
        x = 5
      ),
      "Choice 2" = list(
        x = 13
      )
    )
  ) |>
  scr_label_phase(
    facet = "Eva",
    cex = 1.25,
    adj = 0.5,
    y = 8,
    labels = list(
      "Choice 1" = list(
        x = 2.25
      ),
      "Choice 2" = list(
        x = 6.5
      ),
      "Choice 3" = list(
        x = 11
      )
    )
  ) |>
  scr_label_phase(
    facet = "Cali",
    cex = 1.25,
    adj = 0.5,
    y = 8,
    labels = list(
      "Choice 1" = list(
        x = 4
      ),
      "Choice 2" = list(
        x = 11
      )
    )
  ) |>
  ...

Summary

The steps included in the design of this figure illustrate the flexibility and complexity of figures commonly used in the single-case research tradition. Historically, much of this was design by hand in individual plots, which had to be manually resized and arranged to construct the broader figure. The introduction of faceting and keyed styles allows for the automating of all this work in a single call, which has great implications for efficiency and reusability.