Showing the Many Faces of the Pandemic

Underscoring COVID-19 data with reminders of its human toll

Houston Haynes

7 minute read

More Than Words

Data can tell a compelling story when it’s properly collected, collated and curated. But there are times when data isn’t enough. And it’s not the lack of information that causes the picture to be lost. Sometimes it’s sheer overwhelming volume that can diminish the translation of data into information and ultimately into actionable intelligence. With the scope and scale of COVID-19, even experts can struggle to capture the essence of what they’re seeing in the flood of data.

While sorting through some data sets for this blog, I decided to add a counterpoint to the global, national and state level data by embedding tweets from the “FacesOfCovid” Twitter account. I thought it was just as important to put a face on the crises as it is to get a full grasp of the breadth and scope of the pandemic. It can be all too easy to lose sight of how COVID-19 ripples out into the fabric of society. So I used the method below to keep the analysis grounded.

Repurposing rtweet

rtweet is a handy library and that’s a go-to for a variety of tasks. I’ve used it for collecting and analyzing wide swaths of data for purposes ranging from sentiment analysis to investigating bot and astro-turfing patterns online. But here, instead of using it as a data mining tool, I’m doing a quick and easy pull of tweet status_ids to embed randomized tweets into COVID-related post on this site.

Faces of COVID-19

Getting into the code

Like several other places on the site, I let blogdown and Hugo do the heavy lifting, embedding the selected codes into “shortcodes” that are then processed after the initial R code has populated the pages with Twitter status ids. I then wrap the shortcodes with Bootstrap markup that performs the arrangement of the tweets, three to a row in a “normal” landscape view and then vertically for mobile devices.

It starts with a block of R code at the top of the page that pulls and filters the timeline for the “FacesOfCovid” account. There’s a lot going on “under the hood” of this code, which is part of the beauty of what this library handles on the user’s behalf. One thing that’s not shown here is the setup of the token used to make an authorized request into the Twitter API to collect the data. There are plenty of tutorials online of how to manage that (and embed the token into a .rds file to then be read into an RSession variable with the project. )

Collecting Tweets from “FacesOfCovid” Account
COVIDtweets <- get_timeline("FacesOfCOVID", n = 1000, parse = TRUE, include_rts = FALSE) %>%
  filter(str_detect(text, 'died of COVID'))

The “get_timeline” command first takes the account name (which can also be an account id) and a n value of 1,000 - to get a longer list that then default 100. Filtering significantly pares down the list and getting 1,000 tweets mean I go “back in time” far enough to get a good cross-section from which to randomly select the posts to embed. The parse function does the heavy lifting of putting the API result into a dataframe, and setting include_rts to FALSE keeps retweets out of the list. And finally, the filter command selects for posts containing a string that I use as a key to strive to get announcement posts. The last time I tested this locally I noticed that the initial pared down to slightly less than 400 tweets, which is plenty for the next step.

Random re-order and assign vector
COVIDtweets <- sample(COVIDtweets$status_id)

The last line from the setup block does two things. First it takes status_id column as a vector and randomly re-orders it with the sample function. Then it assigns that new vector into COVIDTweets for use in the embed. Picking values in order gives me a randomized selection, since the sample function did the re-shuffling.

Bootstrap wrapper responsive layout
Faces of COVID-19
`r blogdown::shortcode("tweet", COVIDtweets[1])`
`r blogdown::shortcode("tweet", COVIDtweets[2])`
`r blogdown::shortcode("tweet", COVIDtweets[3])`

I opted to go with three tweets arranged in standard wide/landscape views. There’s certainly an option to go to two by col-md-6 indicators, but putting four on a row was too tight.

And it’s certainly possible to keep going. Notice below how Bootstrap’s “12 column” grid system only requires a single “row” with multiple tweets that in standard landscape orientation the browser displays in a 3X3 grid.

Extended Grid Layout
More Faces of COVID-19
`r blogdown::shortcode("tweet", COVIDtweets[4])`
`r blogdown::shortcode("tweet", COVIDtweets[5])`
`r blogdown::shortcode("tweet", COVIDtweets[6])`
`r blogdown::shortcode("tweet", COVIDtweets[7])`
`r blogdown::shortcode("tweet", COVIDtweets[8])`
`r blogdown::shortcode("tweet", COVIDtweets[9])`
`r blogdown::shortcode("tweet", COVIDtweets[10])`
`r blogdown::shortcode("tweet", COVIDtweets[11])`
`r blogdown::shortcode("tweet", COVIDtweets[12])`

Daily Refresh

Aside from the normal devops process while fleshing out this site, the build/deploy cycle also re-triggers whenever there’s new COVID data available. I’ll be writing about that under separate cover but it bears mentioning here to illustrate how that process also benefits this function. As the COVID data is refreshed and site re-deployed, it also re-runs the rtweet functions. That will not only gather any new posts that have been made over the previous day, but will continue to shuffle and display “new” entries that arraive at the top of that shuffled list. So each day it will rotate the tweets and continue to shine a light on the myriad life stories that were unduly cut short by this tragedy.

More Faces of COVID-19

Key Value
BuildDateTime 2021-07-03 10:22:03 -0700
LastGitUpdate 2021-06-23 23:36:17 -0700
GitHash 1512b77
CommitComment uipdated global NextStrain data