Recall Task: Connect The UI And Server

Open your completed Week 6 slider_app.R and click Run App.

UI: input controls and output locations

The controls let users supply values. plotOutput() creates a named placeholder for the server to fill (Wickham (2021), section 2.3).

selectInput("modality", "Output group",
  choices = c("Spoken output" = "speech",
              "Written output" = "writing"))
sliderInput("binwidth", "Bin width (ms)",
  min = 25, max = 300, value = 100)
plotOutput("rt_plot")

Server: read inputs and calculate outputs

input$ reads control values; output$ defines displayed results using render functions. Dependent outputs update when inputs change.

output$rt_plot <- renderPlot({
  selected_data <- filter(
    d_participants,
    modality == input$modality
  )
  ggplot(selected_data, aes(x = mean_rt)) +
    geom_histogram(
      binwidth = input$binwidth)
})

Your task: match the two controls and plot location to their server counterparts. Identify the matching names. Predict and test each control’s effect.

Distinguish The Data Column From The Input

The data column in d_participants and control input$modality share a name. filter() explicitly connects the column to the selected value.

filter(d_participants, modality == input$modality)

Inside filter(), modality is the data column; input$modality is the chosen value. If it is "speech", the comparison keeps the 33 spoken-output participants.

ppt_id modality mean_rt
40 speech 1358.1
41 speech 1273.9
1 writing 2146.4
2 writing 1485.8

ppt_id: participant code.

modality: categorical data column containing "speech" or "writing".

mean_rt: numeric participant mean reaction time.

Today: Keep Several Views Consistent

By the end of this session, you should be able to:

  • Share one filtering step across several outputs using reactive().
  • Obtain the current data with selected_data().
  • Explain which controls affect which outputs.
  • Organise the same app using a sidebar and clear explanations.

What we will build: a complementary plot of individual values, and a participant count, all using one group selection.

Download The Week 7 App And Worksheet

Unzip the whole Week 7 folder from NOW into your existing module project.

Your existing module project/
  week-07-layout-observers/
    intro_app.R
    starter_app.R
    layout_app.R
    01_shared_data_and_layout.Rmd
    data/
      participant_reaction_times.csv

Keep the scripts and data/ together.

Open the Rmd and click Knit to read its HTML guide.

Knit The Guide, Then Follow Each Stage

Open 01_shared_data_and_layout.Rmd and click Knit. Read the HTML beside your app script; use its contents menu to find each section.

Guide section Activity
Inspect three views of one selection Inspect the outputs and repeated filtering
One shared filter supplies the outputs Trace the shared filter into the outputs
Exercise 1: connect shared data to the outputs Complete and test shared-data connections
Exercise 2: give the same app a sidebar Complete and test the sidebar layout

Where to complete code: read instructions in the Rmd’s HTML guide; complete the exercises in the named app .R script. Edit it, save, then click Run App.

Two Views Reveal Different Features

Both plots show the same 33 participant means for spoken output, not individual trials.

  • Histogram: reveals distribution shape through bin counts; bin width affects its appearance.
  • Boxplot: highlights the median and the spread of the middle 50% of values.
  • Points: reveal individual values, clusters, and extremes that a summary can conceal.

Predict Which Outputs Should Change

Together: run intro_app.R. It has the same group selector and width slider as Week 6.

  • Select spoken output and a width of 100 ms: the count should be 33.
  • Predict what should happen when we select written output.
  • Then keep that group fixed and change only the width.

Which of these should change: histogram, boxplot with points, participant count?

Test the predictions together.

Three Outputs Repeat The Same Preparation

In intro_app.R, this filtering step appears inside each render block:

selected_data <- filter(d_participants, modality == input$modality)
Render block What it does with the selected rows
renderText() counts the participants
First renderPlot() draws a histogram
Second renderPlot() draws a boxplot with individual points

Discuss: when we change group, how many times do we select the same rows? When only bin width changes, does the histogram run its filter again?

The app works: each output filters separately. Changing bin width reruns the histogram’s whole render block, including its filter. Changing preparation means editing every copy.

Reuse Selected Rows Until Group Changes

reactive() lets the outputs share a cached result: Shiny remembers the selected data frame until its input changes.

User action Separate filters in each output One shared reactive()
Change group Each output filters separately Filter once when needed; share the result
Change bin width Histogram filters again and redraws Reuse the selected rows; redraw the histogram

Easier to change: edit the filtering code in one place, rather than in three output blocks.

Less repeated work: reuse the result instead of filtering separately for each output. Automatic updates already work in the original app.

With 72 rows, expect little visible speed difference. Sharing becomes more useful with larger data or expensive preparation.

This follows Wickham (2021), section 1.6: Reducing duplication with reactive expressions.

One Shared Filter Supplies The Outputs

server <- function(input, output) {
  # 1. Prepare the selected rows once.
  selected_data <- reactive({
    filter(d_participants,
           modality == input$modality)
  })

  # 2. Draw the histogram from those rows.
  output$rt_plot <- renderPlot({
    ggplot(selected_data(),
           aes(x = mean_rt)) +
      geom_histogram(
        binwidth = input$binwidth)
  })

  # 3. Count the same rows.
  output$selection_note <- renderText({
    paste("Participants shown:",
          nrow(selected_data()))
  })
}

1. One shared preparation

reactive() creates a function, not a data frame. selected_data names that function; selected_data() calls it and returns the current data frame. Hence the parentheses (Wickham (2021), section 1.6).

2. A plot uses the result

The result is cached until group changes. The histogram also reads input$binwidth.

3. The count uses the same result

nrow() counts participants; paste() joins the label and count. Neither reads bin width. The boxplot also uses selected_data().

Predict: if input$modality stays fixed and bin width changes, which outputs should update?

Share The Work And Change The Code Once

server <- function(input, output) {
  # 1. Prepare the selected rows once.
  selected_data <- reactive({
    filter(d_participants,
           modality == input$modality)
  })

  # 2. Draw the histogram from those rows.
  output$rt_plot <- renderPlot({
    ggplot(selected_data(),
           aes(x = mean_rt)) +
      geom_histogram(
        binwidth = input$binwidth)
  })

  # 3. Count the same rows.
  output$selection_note <- renderText({
    paste("Participants shown:",
          nrow(selected_data()))
  })
}

Change the rule in one place

Edit the filter inside reactive(), rather than in three output blocks. All outputs use the same rule.

Calculate once and share the result

When group changes, filtering runs once when needed. The histogram, boxplot, and count reuse the selected rows.

Keep results when other inputs change

Changing only bin width redraws the histogram using the cached rows. It does not rerun the filter.

Wickham (2021), section 1.6.

Keep this example open during Exercise 1.

Exercise 1: Connect The Shared Data

Open starter_app.R; follow Exercise 1 in the Rmd (about 20–25 minutes).

Complete six blanks:

  • One function name to create the reactive expression.
  • Three calls obtaining the selected data: two plots and one count.
  • Two matching output names in the UI.

Complete all blanks, then predict and test each control change. Use the annotated shared-filter example when needed.

After code edits: save, stop, and run again. When testing controls: keep the app running.

Check Your Predictions And Repair A Connection

Test Count Plots
Spoken output, width 100 33 both show spoken-output participants
Written output, width 100 39 both show written-output participants
Written output, width 250 still 39 only the histogram changes

Explain: point to the line that makes each result happen. Why does bin width leave the count and boxplot unchanged?

Repair: change only the UI’s text-output name. Predict the effect, test it, then restore the matching name. The guide has a troubleshooting checklist.

Which Layout Is Clearer?

Both versions contain the same data, controls, and outputs.

A: One long columnThe working introductory app with controls and plots in one long column
B: Controls beside the outputsThe same app arranged with controls in a sidebar and both plots in the main area

Where do you look for controls? Which arrangement helps you compare the plots and see what is selected?

Move UI Elements And Keep Their Connections

UI structure; existing elements omitted here

ui <- fluidPage(
  titlePanel("Participant reaction times"),
  p("Participant means, in milliseconds."),
  sidebarLayout(
    sidebarPanel(
      # Existing controls and help text.
    ),
    mainPanel(
      # Existing count, headings and plots.
    )
  )
)

Outer wrapper: sidebarLayout() holds both areas.

Controls: sidebarPanel() holds the selector, slider and their helpText("...").

Outputs: mainPanel() holds the count, headings and plots.

Keep input and output names unchanged when moving them. The server can stay the same.

Use p("...") to introduce the data and units; explain each control beside it. Write your own wording in the supplied blanks.

Predict: what will change when a plot moves to the main area? What will stay the same?

Exercise 2: Give The Same App A Sidebar

Open layout_app.R; follow Exercise 2 in the Rmd (about 20–25 minutes).

Complete five blanks:

  • Three layout function names: the wrapper, control area, and output area.
  • One sentence identifying the data and units.
  • One sentence explaining which outputs each control changes.

The completed server is supplied.

Choose which plot should come first. Move its heading with its plot location; keep the output names and server unchanged. Predict the result, then test both controls.

Compare The Design And Test The Behaviour

Compare your layout and help text with a neighbour’s:

  • Which plot do you want a reader to inspect first, and why?
  • Does the help text explain what changes and what stays fixed?
  • Are units and the participant count easy to find?

Revise one sentence or one placement if needed.

Then repeat the control tests: group changes update all views; bin-width changes update only the histogram.

Together: check: which changes were only in the UI? Why could the server stay the same?

What We Have Added This Week

  • One shared preparation step with reactive().
  • Two complementary plots of the same selected participants.
  • A count that identifies the selected sample.
  • Controls and explanations organised beside the outputs.

Automatic updates still work. No update or reset buttons are needed.

Next week: organise plots into tabs, choose controls, and explain empty selections. Session 9 introduces Plotly before adapting the ideas to another dataset.

After Class: Read About Shared Reactive Data

Everyone: Wickham (2021), section 1.6.

Where is the shared preparation in the example, and which outputs use it? No written response is required.

Optional reference: Chapter 6, section 6.2, for sidebar layouts.

Keep your working app. Continue exploring possible summative datasets; it is fine not to have chosen one yet. No transfer to your own data is required this week.

Independent Practice: Choose One Optional Exercise

Use the remaining time to work independently on one of two optional exercises. Choose the one you would most like to explore:

  • Compare both groups: use optional_groups_app.R to select one or both groups, compare them in the boxplot, and pool them in the histogram. Follow Optional: show both groups in the guide.
  • Add a summary table: use the example in extension_app.R to add participant count, median, and interquartile range to your own app. Follow Optional: add a summary table in the guide.

The exercises are independent: neither requires completing the other. Read the instructions, make the changes in your app script, and test the controls.

If you finish one, you are welcome to try the other or continue after class.

Optional: Compare Both Groups In One View

Use Optional: show both groups in the guide and optional_groups_app.R. Complete its four blanks, save, then click Run App.

  • Use checkboxGroupInput() so users can tick one or both groups.
  • Filter with modality %in% input$modality: keep rows matching any selected group. == was for one selected value.
  • Map the boxplot’s x axis to modality to show separate groups and points.
  • Keep the histogram as aes(x = mean_rt): it pools both groups.
  • Write help text explaining the difference between the two views.

Check: counts should be 33, 39, and 72. Bin width changes only the histogram.

What might a pooled histogram conceal that the separate boxplots reveal? The guide explains the choices vector and %in%; no if / else is needed.

Optional: Add A Summary Table

For independent practice, or if there is time, use the guide section Optional: add a summary table and extension_app.R.

  • Add tableOutput("summary_table") in the main UI area.
  • Put the matching renderTable() block inside the server, alongside the plot blocks.
  • Use selected_data() to report count, median, and interquartile range.
  • Check that group selection updates the table; bin width should not affect it.

The guide explains the placement and checks. Tabs will be taught as a core skill in Session 8.

After Completing Your App: Compare And Explain

Now open worked_example_app.R to compare with your own finished app.

Trace the group selector through selected_data() to the count and both plots. Explain why bin width affects only the histogram.

Compare the layout and wording. Keep your own version and use the reference to check your reasoning.

References

Wickham, H. (2021). Mastering shiny. O’Reilly Media.