R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
138: Real-Time Data Streaming in Shiny
When you first move from static data analysis to building "live" dashboards, there is one mistake I see almost every developer make. They treat the Shiny server like a standard script. They think, "I want this plot to update every second, so I'll just wrap the data fetching logic in a while(TRUE) loop inside the server function."
The "Infinite Loop" Trap
It seems logical on paper, but in practice, it's a disaster. If you write a while loop inside your Shiny server, you aren't "streaming" data; you are hijacking the entire R process. Because R is single-threaded, that loop will occupy the CPU completely. Shiny's event loop—the mechanism that actually listens for user clicks and sends updates to the browser—never gets a chance to run. The result? Your UI freezes, the browser eventually times out, and your app becomes a very expensive space heater.
# DON'T DO THIS
server <- function(input, output, session) {
output$plot <- renderPlot({
while(TRUE) {
data <- fetch_sensor_data()
plot(data)
Sys.sleep(1) # You think this helps, but it still blocks the server!
}
})
}
Letting Shiny Breathe with invalidateLater
The trick to real-time streaming in Shiny is to stop trying to push data and start letting Shiny pull data on a schedule. We do this using invalidateLater(). Instead of a loop that never ends, you create a reactive expression that tells Shiny: "I'm done for now, but please mark me as invalid (expired) in exactly 1,000 milliseconds."
When the timer expires, Shiny realizes the data is stale and re-executes only that specific reactive block. This allows the server to handle other user inputs in between updates, keeping the app responsive. I usually prefer this over reactiveTimer because it's more flexible when you need to dynamically change the update frequency based on a user's input.
Building a Non-Blocking Data Stream
Let's look at a concrete example. Suppose we're monitoring a simulated temperature sensor that fluctuates over time. We want a live plot that scrolls as new data arrives without locking up the browser.
library(shiny)
library(ggplot2)
ui <- fluidPage(
titlePanel("Live Sensor Feed"),
plotOutput("sensorPlot")
)
server <- function(input, output, session) {
# We use a reactiveVal to store the accumulating stream
sensor_data <- reactiveVal(data.frame(time = Sys.time(), temp = 20))
# This reactive expression handles the "streaming" logic
observe({
# Tell Shiny to re-run this block every 500ms
invalidateLater(500, session)
# Simulate fetching a new reading from an API or Database
new_reading <- data.frame(
time = Sys.time(),
temp = 20 + rnorm(1, mean = 0, sd = 2)
)
# Append the new reading to our existing data
updated_df <- rbind(sensor_data(), new_reading)
# Keep only the last 50 points so the plot doesn't get cluttered
if(nrow(updated_df) > 50) {
updated_df <- tail(updated_df, 50)
}
sensor_data(updated_df)
})
output$sensorPlot <- renderPlot({
ggplot(sensor_data(), aes(x = time, y = temp)) +
geom_line(color = "steelblue", size = 1) +
theme_minimal() +
labs(title = "Real-time Temperature Stream", x = "Time", y = "Celsius")
})
}
shinyApp(ui, server)
Notice how the observe block acts as a heartbeat. It updates the reactiveVal, which in turn triggers the renderPlot. The key here is that between that 500ms window, R is completely free to handle other tasks. If you had a slider to change the sensor's threshold, it would feel snappy and responsive because the server isn't trapped in a while loop.
📋 Practical Task
Build a Real-Time Cryptocurrency Price Simulator
Your task is to build a Shiny application that simulates a live price feed for a cryptocurrency (e.g., "ShinyCoin").
- The Data: Create a
reactiveValthat starts with a single price point of 100. - The Stream: Use
invalidateLaterto update the price every 200 milliseconds. The new price should be the previous price plus a small random change (e.g.,rnorm(1, 0, 0.5)). - The UI:
- Display the current price in a large
textOutput. - Display a
plotOutputshowing the price trend over the last 100 iterations.
- Display the current price in a large
- The Twist: Add a
numericInputthat allows the user to change the update frequency (the milliseconds ininvalidateLater) in real-time without restarting the app.
There are no comments for now.