How to Scrollytell in R: Automation and Its Impact on Jobs
October 26, 2019
I’ve always been interested in data visualization, and my most recent sub-passion has been scrollytelling. I’ve seen numerous examples of amazing scrolling articles, from sites such as The Pudding, the New York Times, and FiveThirtyEight.
Although most of these sites rely on the same high-powered visualization tools (such as D3.js) to create their stories, the learning curve for those are a bit too steep for a full-time college student to pursue (although I am trying!).
Thankfully, I came across some examples of scrollytelling in my language of choice, R.
Once the data is already loaded and cleaned, we construct a plot object that will update as the user scrolls. (Other R users make multiple plots; either way is fine.)
But wait, what’s that second line of code?
This may make 0 sense right now, but here’s why we have it.
The important part (and somewhat difficult thing to understand) about this step is that your plot object has some data which corresponds to an updating variable. For me, I added a variable called reveal for each step of the visualization. For me, this meant having reveal correspond to a typical level of education for a given job.
What this means is that every time the post observes an event (a scroll), it will update according to the number in the reveal column.
So, when my plot object has a line of code which says:
That can be read as “Update the plot object to include all data points up until the current point encoded in reveal.”
But, it also depends on what level of add we are at; if add == 8 (the last data point, e.g. the concluding plot), then we want to show all data points.
“Okay, okay. But what is add???”
Good question. add is the variable I constructed to correspond to the user’s input (in this case, the scroll!). When we put it all together, we’re going to wrap our plot object into a rendering function inside the server function. Confusing? It looks like this:
What’s happening here? Within the server function, we’re doing two things:
First, we’re creating the plot object. Because we have the command add <- input$scrinside the function renderPlotly, our plot object will be dynamically updated along with input$scr.
This makes more sense when you connect it to the ui. In our ui function, we include a scrolly_container from the scrollytell package. Within that, we make our outputId correspond to the name of our updating input (in this case, scr).
So, the simplest way to think about this so far is:
Construct a plot object with some updating variable (in my case, reveal).
Make that variable correspond with some input variable (in my case add, which is created from the input$scr).
Render your plot object in scrolly_graph, and provide input updates via each scrolly_section.
Render your plots in your server function.
That’s (most of) it for the plot section. You can play around with other customizations too (for example, the alphas for my circles correspond to an ifelse around reveal, so old circles are faded out compared to new ones).
Create a Series of Text Reveals
Next, we create text sections using HTML and some helper functions which beautify them.
First, we create the text boxes for each section following a similar naming convention:
Then, we create a function render_text which beautifies that HTML with CSS:
For all 8 of the above text_s, we’ve created a switch function which calls them depnding on the number passed to render_text. Then, we apply paragraph format, put them in their own div, and apply the text class in our CSS sheet.
How does this work?
Recall that above, in our ui section, we had the following:
Here, our scrolly_sections take two inputs: our ID and our corresponding text. Rather than write out lengthy text boxes in each scrolly_section, we can construct them in a single file and then knit them all into the same format using render_text. This makes life a lot easier.
So, on each scroll (or observation), the app will update 1) the plot object, by adding add and updating according to reveal, and 2) the text, by running render_text which calls text which beautifies our HTML text.
Surprisingly, that’s about it. The rest of the app is built on fluidPage, and organizing via rows and columns in your shiny app. Your app should have a CSS style sheet (in the www folder). You may also include other plots (for my post, I have an introductory plot with a bit more context), which you will have to separately create and render in your server function.