This is the second post following the the previous one, where we used tracking logs from an online course platform to create animated visualizations.
In this post, we explore three aspects of the user engagement: (1) global distribution of the users over time, (2) resource usage over time, and (3) the peak hours of accessing the platform by device types (desktop versus mobile).
With the capabilities of the slider, we are able to pick a certain date or select a date range to explore the graphs interactively; we can also run animation sequences with the slider controls.
We use the same data as in the earlier post, where we have created an animated graph with gganimate
.
head(map)
## date latitude longitude city n
## 1 2016-01-24 22.2643 114.1880 Hong Kong 158
## 2 2016-01-24 40.7323 -73.9874 New York 49
## 3 2016-01-24 36.8529 -75.9780 Virginia Beach 23
## 4 2016-01-25 51.0951 -113.9440 Calgary (Northeast Calgary) 13
## 5 2016-01-25 22.2643 114.1880 Hong Kong 1005
## 6 2016-01-25 40.7323 -73.9874 New York 123
Here we want to (1) view the map on a selected date, and (2) run the animation sequences once we hit the play button, as shown below.
To create something as shown above, on the ui
side, for the slider, we need to specify the display range for the sliderInput()
: min
sets the minimum value to use in the slider bar, and max
sets the maximum value; both should be date objects in our case. With the animate
argument and the interval
option, we set the pace of the iterations in the animation. We set the step
to be 1 so that the increment between the values will be one day and in the animation the map will reflect the change daily. value
sets the default value to show in the slider bar when the app is first loaded.
library(shiny)
library(ggthemes)
library(maps)
library(ggplot2)
library(dplyr)
load("map")
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "distribution",
label = "Dates:",
min = as.Date("2016-01-24","%Y-%m-%d"),
max = as.Date("2016-04-02","%Y-%m-%d"),
value = as.Date("2016-02-29"), timeFormat="%Y-%m-%d",
step = 1,
animate = animationOptions(interval = 1800))),
mainPanel(plotOutput(outputId = "map", height = "70vh"))))
On the server
side, we plot the graph on the subset of data whose date
range is within the min
and max
values by setting date == input$distribution
. distribution
is the inputId
of the slider.
server <- function(input, output){
output$map <- renderPlot({
map %>%
filter(date == input$distribution) %>%
ggplot() +
borders("world", colour = "gray90", fill = "gray85") +
theme_map() +
geom_point(aes(x = longitude, y = latitude, size = n),
colour = "#351C4D", alpha = 0.55) +
labs(size = "Users") +
ggtitle("Distribution of Users Online")
})
}
shinyApp(ui = ui, server = server)
Not only can we input single values in the slider, but also ranges. To explore how users accessed the platform within a date range, we customize the slider so that we can extract data within a period of time.
Below is the sample data, same as what we used to create the animated line graph.
head(resource_day_sum)
## date resource sum
## 1 2016-01-24 Slides 69.09
## 2 2016-01-24 Watch 31.61
## 3 2016-01-25 Intro 41.59
## 4 2016-01-25 Read 60.95
## 5 2016-01-25 Slides 429.25
## 6 2016-01-25 Task 17.10
On the ui
side, same as what we have done in the section above, we need to set the sliderInput()
for display range with arguments min
and max
.
library(shiny)
library(ggplot2)
library(dplyr)
load("resource_day_sum")
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "trajectory",
label = "Date Range:",
min = as.POSIXct("2016-01-24","%Y-%m-%d"),
max = as.POSIXct("2016-04-02","%Y-%m-%d"),
value = c(as.POSIXct("2016-02-01"), as.POSIXct("2016-03-21")),
timeFormat="%Y-%m-%d", step = 1)),
mainPanel(plotOutput(outputId = "lines", height = "70vh"))))
On the server
side, to match the date range we set for the slider, we plot the graph on the subset of data where date >= input$trajectory[1] & date <= input$trajectory[2]
. trajectory
is the inputId
of the slider.
server <- function(input, output){
color <- c("#765285","#D1A827","#709FB0", "#849974", "#A0C1B8")
due <- c(as.POSIXct("2016-01-25 UTC"), as.POSIXct("2016-02-01 UTC"),
as.POSIXct("2016-02-15 UTC"), as.POSIXct("2016-02-22 UTC"),
as.POSIXct("2016-02-29 UTC"), as.POSIXct("2016-03-07 UTC"),
as.POSIXct("2016-03-14 UTC"), as.POSIXct("2016-03-21 UTC"),
as.POSIXct("2016-03-28 UTC"))
output$lines <- renderPlot({
resource_day_sum %>%
filter(date >= input$trajectory[1] & date <= input$trajectory[2]) %>%
ggplot(aes(date, sum, group = resource, colour = resource)) +
geom_line(alpha = 0.9, size = 0.65) +
scale_x_datetime(breaks = seq(as.POSIXct("2016-01-26 UTC"), as.POSIXct("2016-04-02 UTC"), "7 days"), date_labels = "%b %d") +
geom_vline(xintercept = due, alpha = 0.6, size = 0.65, colour = "grey55") +
scale_colour_manual(values = color, name = "Resource") +
labs(title = "Time Spent Online Learning", x = "Date", y = "Total Minutes per Day")
})
}
shinyApp(ui = ui, server = server)
Finally, we would like to create a heatmap to reveal the peak hours of users accessing the online resources. We showed how to make a static heatmap previously. Here we add the feature that enables people to interactively view the heatmap by device types.
We have added the device data to the data of local hours.
head(local, 15)
## localdate localhour n device
## 1 2016-01-24 0 1 all
## 2 2016-01-24 6 2 all
## 3 2016-01-24 18 6 all
## 4 2016-01-24 20 1 all
## 5 2016-01-24 21 2 all
## 6 2016-01-24 23 2 all
## 7 2016-01-24 0 1 desktop
## 8 2016-01-24 6 1 desktop
## 9 2016-01-24 18 5 desktop
## 10 2016-01-24 20 1 desktop
## 11 2016-01-24 21 2 desktop
## 12 2016-01-24 23 2 desktop
## 13 2016-01-24 6 1 mobile
## 14 2016-01-24 18 1 mobile
## 15 2016-01-25 0 1 all
The tick marks on the X axis indicate the due dates. Clearly, when the due dates approached, there were more students accessing online resources, ususally lasting from evening to midnight. Inspecting the peak hours by device types, it is apparent that the majority accessed the online resources via desktop devices, while very few accessed the resources via mobile devices.
We have shown how to build a simple dashboard like this before.
library(shiny)
library(shinythemes)
library(dplyr)
library(ggplot2)
load("local")
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
radioButtons(
inputId = "peak",
label = "Devicce:",
choices = c("All types", "Desktop", "Mobile"))),
mainPanel(plotOutput(outputId = "heatmap", height = "70vh"))))
server <- function(input, output){
dataInput <- reactive({
switch(input$peak,
"All types" = local %>% filter(device == "all"),
"Desktop" = local %>% filter(device == "desktop"),
"Mobile" = local %>% filter(device == "mobile"))
})
output$heatmap <- renderPlot({
ggplot(dataInput(), aes(localdate, localhour2)) +
geom_tile(aes(fill = n), colour = "white") +
scale_fill_gradient(low = "#f3da4c", high = "blue", breaks = seq(0, 13, 2)) +
scale_x_date(breaks = seq(as.Date("2016-01-26 UTC"), as.Date("2016-04-02 UTC"), "7 days"), date_labels = "%b %d") +
scale_y_continuous(breaks = seq(0, 23, 1)) +
labs(x = "Date", y = "Hour", fill = "Freq of Online Access") +
ggtitle("Peak Hours of Studying Online (Local Time)")
})
}
shinyApp(ui = ui, server = server)