A comprehensive, production-ready Shiny application for visualizing clinical trial data following CDISC SDTM standards. This application demonstrates advanced features including cross-filtering, drill-down navigation, faceting, bookmarking, and user preferences.
- Modular Architecture: Scalable, maintainable codebase with reusable modules
- SDTM Compliance: Supports standard CDISC domains (DM, AE, LB, VS)
- TLF Visualization: Tables, Listings, and Figures optimized for clinical data
- Cross-Filtering: Filters cascade across all modules automatically
- Drill-Down Navigation: Click tables/plots to explore detailed data
- Faceting: Dynamic grouping and small multiples for comparative analysis
- Bookmarking: Save and share application state via URL
- User Preferences: Customize appearance, behavior, and defaults
- State Management: Centralized reactive state for consistency
clinical_tlf_app/
├── app.R # Main application file
├── global.R # Global environment setup
├── ARCHITECTURE.md # Detailed architecture documentation
├── README.md # This file
│
├── config/
│ └── app_config.R # Configuration settings
│
├── data/
│ ├── create_sdtm_data.R # Data generation script
│ └── sdtm/ # SDTM datasets (RDS/CSV)
│ ├── dm.rds
│ ├── ae.rds
│ ├── lb.rds
│ └── vs.rds
│
├── utils/
│ ├── state_management.R # Reactive state management
│ ├── bookmark_utils.R # URL bookmarking utilities
│ └── data_utils.R # Data processing functions
│
├── modules/
│ ├── core/
│ │ ├── filter_module.R # Cross-domain filtering
│ │ └── preferences_module.R # User preferences
│ │
│ └── tlf/
│ ├── tlf_table_module.R # Summary tables
│ ├── tlf_listing_module.R # Data listings
│ └── tlf_figure_module.R # Interactive figures
│
└── www/
└── custom.css # Custom styling
- R (>= 4.0.0)
- RStudio (recommended)
install.packages(c(
"shiny", # Web application framework
"bslib", # Modern UI theming
"DT", # Interactive tables
"plotly", # Interactive plots
"ggplot2", # Static plots
"dplyr", # Data manipulation
"tidyr", # Data tidying
"shinyWidgets", # Enhanced widgets
"jsonlite" # JSON parsing
))- Clone or download the repository
- Navigate to the
clinical_tlf_appdirectory - Generate sample data:
source("data/create_sdtm_data.R") - Run the application:
shiny::runApp()
- Overview Tab: View study summary and quick statistics
- Filters: Use the sidebar to apply filters across all domains
- Tables: Generate summary tables grouped by various variables
- Listings: Explore detailed record-level data
- Figures: Create interactive visualizations with faceting
- Navigate to any tab (Tables, Listings, or Figures)
- Open the filter sidebar
- Select filters from any domain (DM, AE, LB, VS)
- Click "Apply" to update all modules
- Filters persist across tab navigation
Example:
- Filter to "Drug 50mg" treatment arm
- Filter to "SEVERE" adverse events
- All tables, listings, and figures update automatically
- Select rows in summary tables
- View automatically filters to selected subgroups
- Click any row to see full record details
- Detail panel shows all fields for the selected record
- Click plot points to filter to specific subgroups
- Use plotly tools to zoom, pan, and explore
- Navigate to the Figures tab
- Enable "Faceting" checkbox
- Select facet variable (ARM, SEX, SITEID)
- Choose facet layout (Wrap or Grid)
- Plot automatically splits into small multiples
- Apply desired filters and navigate to desired view
- Click "Bookmark" button in header
- Copy the generated URL
- Share URL with colleagues or save for later
- Opening the URL restores exact application state
- Click "Preferences" button in header
- Customize:
- Theme (light/dark)
- Default page length
- Default grouping variable
- Export settings
- Click "Save Preferences"
- Settings persist across sessions
- Subject-level baseline characteristics
- Variables: USUBJID, AGE, SEX, RACE, ARM, SITEID
- Used for: Demographics tables, baseline summaries
- Safety event data
- Variables: AETERM, AESEV, AESER, AEREL, AEOUT
- Used for: Safety tables, AE visualizations
- Clinical laboratory results
- Variables: LBTESTCD, LBSTRESN, LBNRIND, VISIT
- Used for: Lab tables, time-series plots, shift tables
- Physiological measurements
- Variables: VSTESTCD, VSSTRESN, VISIT
- Used for: Vital sign trends, safety monitoring
Customize application behavior:
- Data paths and formats
- UI theme and styling
- Feature flags (enable/disable features)
- Domain-specific settings
- Plot colors and defaults
Example:
app_config$ui$theme <- "darkly" # Dark theme
app_config$features$enable_bookmarking <- FALSE # Disable bookmarking- Create data generation function in
create_sdtm_data.R - Add domain configuration in
app_config.R - Update
load_sdtm_data()indata_utils.R - Create module in
modules/tlf/(optional) - Add filters in
filter_module.R
Add custom analyses by:
- Creating new module in
modules/ - Sourcing module in
global.R - Adding UI/server in
app.R - Connecting to
app_statefor cross-filtering
Replace file-based data with database:
- Modify
load_sdtm_data()indata_utils.R - Use
DBIpackage to connect to database - Implement lazy loading for large datasets
- Add caching for performance
Extend export capabilities:
- Add export buttons to modules
- Use
writexlfor Excel export - Use
rmarkdownfor PDF reports - Include filter metadata in exports
- Data Size: Tested with 150 subjects, ~15,000 records
- Scalability: Use database backend for >100,000 records
- Caching: Implement caching for expensive computations
- Lazy Loading: Load modules on-demand
- Async: Use
promisesfor long operations
Unit tests can be added using:
shinytest2for module testingtestthatfor utility function testing
Deploy to:
- Shiny Server (open source)
- RStudio Connect (enterprise)
- ShinyApps.io (cloud)
- Docker (containerized)
To extend the application:
- Follow modular architecture patterns
- Use state management for cross-module communication
- Document new features in ARCHITECTURE.md
- Add configuration options in app_config.R
Application won't start:
- Check all packages are installed
- Run
source("global.R")to check for errors - Verify data files exist in
data/sdtm/
Filters not working:
- Ensure filter module is connected to
app_state - Check
get_filtered_data()is called in modules - Verify trigger_update is incrementing
Bookmarks not restoring:
- Check URL parameters are present
- Verify
restore_from_url()is called - Ensure
enableBookmarking()is set
This is a demonstration project for portfolio/professional purposes.
For questions or issues, please refer to the project documentation or contact the development team.
Version: 1.0.0 Last Updated: 2025-10-20 Built with: R Shiny, bslib, plotly, DT