library(shiny)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## âś” dplyr     1.1.4     âś” readr     2.1.5
## âś” forcats   1.0.0     âś” stringr   1.5.1
## âś” ggplot2   3.5.1     âś” tibble    3.2.1
## âś” lubridate 1.9.3     âś” tidyr     1.3.1
## âś” purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## âś– dplyr::filter() masks stats::filter()
## âś– dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
library(ggplot2)
library(rsconnect)
## 
## Attaching package: 'rsconnect'
## 
## The following object is masked from 'package:shiny':
## 
##     serverInfo
lcs_pop <- colnames(LifeCycleSavings[,c("pop15","pop75")])
lcs_var <- colnames(LifeCycleSavings[,c("sr","dpi","ddpi")])

ui <- fluidPage(
  titlePanel("Life-Cycle Savings"),
  sidebarLayout(
    sidebarPanel(
      textInput("title",
                "Plot Title",
                "Title"),
      sliderInput("range_x", "X-Axis Range:", 
                  min = min(LifeCycleSavings[, 1], na.rm = TRUE),
                  max = max(LifeCycleSavings[, 1], na.rm = TRUE),
                  value = c(min(LifeCycleSavings[, 1], na.rm = TRUE), max(LifeCycleSavings[, 1], na.rm = TRUE))),
      selectInput("x",
                  "X-Axis Variable",
                  lcs_var),
      selectInput("y",
                  "Y-Axis Variable",
                  lcs_var,
                  lcs_var[2]),
      radioButtons("pop","Population Demographic", lcs_pop),
      checkboxGroupInput("countries", 
                         "Select countries", 
                         rownames(LifeCycleSavings), 
                         rownames(LifeCycleSavings)),
      textInput("title2",
                "Distribution Plot Title",
                "Title"),
      selectInput("x2",
                  "Variable for Distribution Plot",
                  colnames(LifeCycleSavings), colnames(LifeCycleSavings)[1]),
      radioButtons("color", "Select Bin Color", c("gray", "steelblue", "pink2"))
    ),
    mainPanel(plotOutput("plot"),
              uiOutput("text_var"),
              plotOutput("plot2"))
    ))

server <- function(input, output, session) {
  observe({
    updateSliderInput(session, "range_x", 
                      min = min(0, na.rm = TRUE),
                      max = max(LifeCycleSavings[[input$x]], na.rm = TRUE),
                      value = c(min(0, na.rm = TRUE), 
                                max(LifeCycleSavings[[input$x]], na.rm = TRUE)))
  })
  
  output$plot <- renderPlot({
    filtered_lcs <- LifeCycleSavings[rownames(LifeCycleSavings) %in% input$countries, ]
    filtered_lcs <- filtered_lcs %>%
      filter(LifeCycleSavings[[input$x]] >= input$range_x[1], 
             LifeCycleSavings[[input$x]] <= input$range_x[2])
    
    ggplot(filtered_lcs, aes_string(x = input$x, y = input$y)) +
      geom_point(aes_string(size = input$pop), alpha = 0.4) +
      labs(title = input$title, x = input$x, y = input$y) +
      theme_minimal() +
      theme(plot.title = element_text(hjust = 0.5)) +
      scale_size_continuous(name = "Population Percentage \n of a Demographic")
  })
  
  output$text_var <- renderUI({
    HTML(paste(
      "Variable Description<br>",
      "sr - numeric aggregate personal savings<br>",
      "pop15 - numeric % of population under the age of 15<br>",
      "pop75 - numeric % of population over the age of 75<br>",
      "dpi - numeric real per-capita disposable income<br>",
      "ddpi - numeric % growth rate of dpi"
    ))})
  
  output$plot2 <- renderPlot({
    filtered_lcs <- LifeCycleSavings[rownames(LifeCycleSavings) %in% input$countries, ]
    ggplot(filtered_lcs, aes_string(x = input$x2)) +
      geom_histogram(binwidth = 1, fill = input$color, alpha = 0.7, color = "black") +
      labs(title = input$title2, x = input$x2, y = "Frequency") +
      theme_minimal() +
      theme(plot.title = element_text(hjust = 0.5))
  })
}

shinyApp(ui = ui, server = server)
Shiny applications not supported in static R Markdown documents

The purpose of the first static bubble chart is for viewers to interpret the relationship between two variables regarding life-cycle savings across countries and relative population percentages. The “title” text input is so the viewer can input a meaningful title that contributes to the understanding of the plot. The “X-Axis Range” slider acts as a filter for if the viewer wants to focus on a specific range of values along the x-axis.The “x and y variable” select inputs allow for comparison between sr, dpi, and ddpi.The radio button input changes the size of the points in relation to the percent population of the selected demographic. The checkbox input allows the viewer to filter based on the countries they want to examine. Altogether, the viewer is able to choose their points of comparison through their selection of the x-axis, y-axis, and population demographic, and then further filter the data through the x-axis range and country, selection, which enables the viewer to make a variety of comparisons and extrapolate meaning from them.

The UI output provides a description of the variables so viewers have a clear understanding of what each variable measures.

The second plot is a standard distribution plot where the viewer can input the title, select the variable to be examined, filter the countries (same input as the first plot), and choose a bin color. Again, the text input allows the viewer to provide some insight through the title of the plot. The select input enables the viewer to choose what distribution to analyze.The bin color is just to fit the aesthetic preferences of the viewer. Overall, the data is understandable and the variable inputs and filters make sure we are only looking at relevant data.

knitr:: include_graphics("/Users/kylaquimson/Desktop/STAT3280/HW 7/movie_explorer_shinyapp.png")

The app works well in that it is very intuitive and customizable as you can choose the x- and y-axis for the scatterplot and filter almost every aspect of the data used in the plot from the minimum number of Rotten Tomatoes reviews, years released, minimum number of Oscar wins, dollars at box office in millions, genre, director, and cast names. The author also left a note explaining the Tomato Meter and Numeric Rating, so the viewers have a clear understanding of the data metrics. I also like how it is organized where the variables and filters are in different boxes to differentiate their functions. I also think it is effective how the plot is interactive, so you can hover over points to see the film name, release year, and dollars at box office.

I think the app would work better if the plot was larger, so it would be easier to process visually, especially because it has a lot of small points. It would also make sense for the variable input box to be on top of the filter input box rather than underneath because it’s a little more intuitive to select the variables, then filter as needed. In addition, for some variables, the labels on the axes start to overlap, making it appear overcrowded, but this is somewhat mitigated by the hover feature on the points.

I did not find the app to be confusing or difficult in anyway. The inputs and outputs of the app are very straightforward.

  1. The shiny app from question 2 has an interactive element on the actual plot whereas mine does not. This would be a helpful addition to have the ability to hover over points for a text box to appear so viewers can see what country each point corresponds to, as well as the values of the x and y variables and population percentage. This would also help alleviate overtaxing working memory.

Both have variable and filter inputs that utilize sliders, select, and text. Mine, however, has more variety in input types like radio buttons and check boxes. Both also provide clarifications for the variables’ measures. My shiny app has two different types of graphs, while the other has one, although the other one has much more detailed filters than my plots, which I like about the one I found and will be implementing in my future ones.

Another feature I would add is the checkbox input, but instead of by country, I would organize it by continent because there are 50 countries to filter through which can be quite tedious. By filtering by continent, the viewer can more easily compare regional differences. I would also add another slider input to filter the y-axis range as well, so if viewers would like to focus on a particular range of data, they have that option. Lastly, I would also use color gradient to further differentiate demographic population percentages across countries.