Hello Plotteus + RStats!

Author

boB Rudis (@hrbrmstr)

This is an R + OJS (Observable-flavored JavaScript) Quarto version of this Observable notebook.

You must go through that notebook first before going through this. I will not be explaining too much about Plotteus, just how to wrangle data in R to get it to work with Plotteus.

Plotteus is a JS datavis library to help craft interactive stories. It kind of works best in a scrollytelling setting, but it can be used outside of said setting quite well.

We’ll work through how to get R data frame data to Plotteus so you can tell stories with it in Quarto docs. I’ve folded most of the code blocks for readability.

The following code block sets up some common labels and settings we’ll use throughout the three OJS charting blocks. Reveal it to see what’s set.

Reveal code
title = "High School Aces"
palette = "vivid"
showLegend = true
verticalAxis = ({
  title: "Number of A+ scores"
})

This is the intial data frame of the students (you saw this as an ugly JS array within the code block of the chart in the pure Observable version):

Reveal code
data.frame(
  student = c("Alicja", "Borys", "Celina", "Dorian", "Ewa", "Filip", "Greg"),
  subject = rep("Math", 7),
  num_scores = c(3L, 4L, 7L, 6L, 5L, 2L, 1L)
) -> students

students
  student subject num_scores
1  Alicja    Math          3
2   Borys    Math          4
3  Celina    Math          7
4  Dorian    Math          6
5     Ewa    Math          5
6   Filip    Math          2
7    Greg    Math          1

We’re going to get it into the shape you saw in the Observable notebook (an excerpt from that is below). I like to wrangle the data into a JSON string and then just parse that on the OJS side. I have the most control over what its shape will be that way.

We actually make two data frames: the original and one sorted by the number of “A+” grades. We could just wrangle that in JS-land, but we’re using R for a reason: we are better at data ops in R.

Each block outputs the JSON that will be parsed.

Reveal code
students |> 
  dplyr::rename(
    key = subject,
    value = num_scores
  ) |> 
    dplyr::group_by(
        student
    ) |> 
  tidyr::nest(
    data = c(key, value)
  ) |> 
  dplyr::ungroup() |> 
  dplyr::rename(key = student) |> 
  jsonlite::toJSON() -> students_json

students_json
[{"key":"Alicja","data":[{"key":"Math","value":3}]},{"key":"Borys","data":[{"key":"Math","value":4}]},{"key":"Celina","data":[{"key":"Math","value":7}]},{"key":"Dorian","data":[{"key":"Math","value":6}]},{"key":"Ewa","data":[{"key":"Math","value":5}]},{"key":"Filip","data":[{"key":"Math","value":2}]},{"key":"Greg","data":[{"key":"Math","value":1}]}] 
Reveal code
students |> 
  dplyr::arrange(
        desc(num_scores)
    ) |> 
    dplyr::rename(
    key = subject,
    value = num_scores
  ) |> 
    dplyr::group_by(
        student
    ) |> 
  tidyr::nest(
    data = c(key, value)
  ) |> 
  dplyr::rename(key = student) |> 
  dplyr::ungroup() |> 
  jsonlite::toJSON() -> students_sorted_json

students_sorted_json
[{"key":"Celina","data":[{"key":"Math","value":7}]},{"key":"Dorian","data":[{"key":"Math","value":6}]},{"key":"Ewa","data":[{"key":"Math","value":5}]},{"key":"Borys","data":[{"key":"Math","value":4}]},{"key":"Alicja","data":[{"key":"Math","value":3}]},{"key":"Filip","data":[{"key":"Math","value":2}]},{"key":"Greg","data":[{"key":"Math","value":1}]}] 

Now we make those text strings available to the OJS runtime:

Reveal code
ojs_define(students_json = students_json)
ojs_define(students_sorted_json = students_sorted_json)

And, we parse the text into proper objects.

Reveal code
students = JSON.parse(students_json)
students_sorted = JSON.parse(students_sorted_json)

This is the format Plotteus expects, and you can see it matches what we built.

Reveal code
[
  {
    key: "Alicja",
    data: [{ key: "Math", value: 3 }]
  },
  {
    key: "Borys",
    data: [{ key: "Math", value: 4 }]
  }
]

This is the first plot (please use the “Code Tools” at the top of the document top see the OJS code that makes the chart):

We will have another data frame for the second example since the transition in part two has two subjects. This is what the JS Object looks like:

Reveal code
[
  {
    key: "Greg",
    data: [
      { key: "Math", value: 1 },
      { key: "Chemistry", value: 6 }
    ]
  },
      {
    key: "Filip",
    data: [
      { key: "Math", value: 2 },
      { key: "Chemistry", value: 4 }
    ]
  }
]

And this is the R code that makes the new data frame and JSON we’ll send to OJS:

Reveal code
read.csv(
  text = '"student","subject","num_scores"
"Alicja","Chemistry",4
"Alicja","Math",4
"Borys","Chemistry",1
"Borys","Math",5
"Celina","Math",7
"Dorian","Chemistry",4
"Dorian","Math",6
"Ewa","Chemistry",2
"Ewa","Math",3
"Filip","Chemistry",4
"Filip","Math",2
"Greg","Chemistry",6
"Greg","Math",1
"Celina","Chemistry",2',
  header = TRUE
) |>
  dplyr::mutate(
    student = forcats::fct_reorder(student, num_scores, sum)
  ) |>
  dplyr::arrange(
    student,
        subject
  ) -> two_subjects

two_subjects |>
  dplyr::rename(
    key = subject,
    value = num_scores
  ) |>
  dplyr::group_by(
    student
  ) |>
  tidyr::nest(
    data = c(key, value)
  ) |>
  dplyr::ungroup() |>
  dplyr::rename(key = student) |>
  jsonlite::toJSON() -> students2_json

ojs_define(students2_json = students2_json)

And, we make this second one available to OJS as well:

Reveal code
students2 = JSON.parse(students2_json)

This is the second example chart. Again, please use the “Code Tools” up top to view the chart OJS source:

We’ll just do one more, the one that makes the horrible pie charts. It does show how cool and flexible the Plotteus transition framework is, though. First, we filter the second data frame down to just three students and pass it to OJS:

Reveal code
two_subjects |>
  dplyr::filter(
        student %in% c("Alicja", "Celina", "Dorian")
    )  |> 
  dplyr::rename(
    key = subject,
    value = num_scores
  ) |>
  dplyr::group_by(
    student
  ) |>
  tidyr::nest(
    data = c(key, value)
  ) |>
  dplyr::ungroup() |>
  dplyr::rename(key = student) |>
  jsonlite::toJSON() -> just_three_json

ojs_define(just_three_json = just_three_json)
Reveal code
just_three = JSON.parse(just_three_json)

And, now we render the final chart:

There is invisible setup code below. Please use the “Code Tools” to view it, and read the Observable notebook version for more information.