Getting started with shinytest2
Shiny
Shiny is a package that makes it easy to create interactive web apps using R and Python.
Refactoring notes
I worked on a refactor of an R package at work the other day. Here’s some notes about that after doing the work. This IS NOT a best practices post - it’s just a collection of thoughts.
For context, the package is an API client.
It made sense to break the work for any given exported function into the following components, as applicable depending on the endpoint being handled (some endpoints needed just a few lines of code, so those funtions were left unchanged):
Prompt and empower your LLM, the tidy way
The tidyprompt package allows users to prompt and empower their large language models (LLMs) in a tidy way. It provides a framework to construct LLM prompts using tidyverse-inspired piping syntax, with a library of pre-built prompt wrappers and the option to build custom ones. Additionally, it supports structured LLM output extraction and validation, with automatic feedback and retries if necessary. Moreover, it enables specific LLM reasoning modes, autonomous R function calling for LLMs, and compatibility with any LLM provider.
REST API in R with plumber
API and R Nowadays, it’s pretty much expected that software comes with an HTTP API interface. Every programming language out there offers a way to expose APIs or make GET/POST/PUT requests, including R. In this post, I’ll show you how to create an API using the plumber package. Plus, I’ll give you tips on how to make it more production ready - I’ll tackle scalability, statelessness, caching, and load balancing. You’ll even see how to consume your API with other tools like python, curl, and the R own httr package.
Nowadays, it’s pretty much expected that software comes with an HTTP API interface. Every programming language out there offers a way to expose APIs or make GET/POST/PUT requests, including R. In this post, I’ll show you how to create an API using the plumber package. Plus, I’ll give you tips on how to make it more production ready - I’ll tackle scalability, statelessness, caching, and load balancing. You’ll even see how to consume your API with other tools like python, curl, and the R own httr package
# When an API is started it might take some time to initialize # this function stops the main execution and wait until # plumber API is ready to take queries. wait_for_api <- function(log_path, timeout = 60, check_every = 1) { times <- timeout / check_every for(i in seq_len(times)) { Sys.sleep(check_every) if(any(grepl(readLines(log_path), pattern = "Running plumber API"))) { return(invisible()) } } stop("Waiting timed!") }
Oh, in some examples I am using redis. So, before you dive in, make sure to fire up a simple redis server. At the end of the script, I’ll be turning redis off, so you don’t want to be using it for anything else at the same time. I just want to remind you that this code isn’t meant to be run on a production server.
redis is launched in a background, , so you might want to wait a little bit to make sure it’s fully up and running before moving on.
wait_for_redis <- function(timeout = 60, check_every = 1) { times <- timeout / check_every for(i in seq_len(times)) { Sys.sleep(check_every) status <- suppressWarnings(system2("redis-cli", "PING", stdout = TRUE, stderr = TRUE) == "PONG") if(status) { return(invisible()) } } stop("Redis waiting timed!") }
First off, let’s talk about logging. I try to log as much as possible, especially in critical areas like database accesses, and interactions with other systems. This way, if there’s an issue in the future (and trust me, there will be), I should be able to diagnose the problem just by looking at the logs alone. Logging is like “print debugging” (putting print(“I am here”), print(“I am here 2”) everywhere), but done ahead of time. I always try to think about what information might be needed to make a correct diagnosis, so logging variable values is a must. The logger and glue packages are your best friends in that area.
Next, it might also be useful to add a unique request identifier ((I am doing that in setuuid filter)) to be able to track it across the whole pipeline (since a single request might be passed across many functions). You might also want to add some other identifiers, such as MACHINE_ID - your API might be deployed on many machines, so it could be helpful for diagnosing if the problem is associated with a specific instance or if it’s a global issue.
In general you shouldn’t worry too much about the size of the logs. Even if you generate ~10KB per request, it will take 100000 requests to generate 1GB. And for the plumber API, 100000 requests generated in a short time is A LOT. In such scenario you should look into other languages. And if you have that many requests, you probably have a budget for storing those logs:)
It might also be a good idea to setup some automatic system to monitor those logs (e.g. Amazon CloudWatch if you are on AWS). In my example I would definitely monitor Error when reading key from cache string. That would give me an indication of any ongoing problems with API cache.
Speaking of cache, you might use it to save a lot of resources. Caching is a very broad topic with many pitfalls (what to cache, stale cache, etc) so I won’t spend too much time on it, but you might want to read at least a little bit about it. In my example, I am using redis key-value store, which allows me to save the result for a given request, and if there is another requests that asks for the same data, I can read it from redis much faster.
Note that you could use memoise package to achieve similar thing using R only. However, redis might be useful when you are using multiple workers. Then, one cached request becomes available for all other R processes. But if you need to deploy just one process, memoise is fine, and it does not introduce another dependency - which is always a plus.
info <- function(req, ...) { do.call( log_info, c( list("MachineId: {MACHINE_ID}, ReqId: {req$request_id}"), list(...), .sep = ", " ), envir = parent.frame(1) ) }
#* Log some information about the incoming request #* https://www.rplumber.io/articles/routing-and-input.html - this is a must read! #* @filter setuuid function(req) { req$request_id <- UUIDgenerate(n = 1) plumber::forward() }
#* Log some information about the incoming request #* @filter logger function(req) { if(!grepl(req$PATH_INFO, pattern = "PATH_INFO")) { info( req, "REQUEST_METHOD: {req$REQUEST_METHOD}", "PATH_INFO: {req$PATH_INFO}", "HTTP_USER_AGENT: {req$HTTP_USER_AGENT}", "REMOTE_ADDR: {req$REMOTE_ADDR}" ) } plumber::forward() }
To run the API in background, one additional file is needed. Here I am creating it using a simple bash script.
library(plumber) library(optparse) library(uuid) library(logger) MACHINE_ID <- "MAIN_1" PORT_NUMBER <- 8761 log_level(logger::TRACE) pr("tmp/api_v1.R") %>% pr_run(port = PORT_NUMBER)
Opinionated Backend and Reusability Focused Considerations for Shiny
Purposefully simple helper functions, tools, and a framework for creating reusable applications fit for production in clinical systems.
ChatGPT
A conversational AI system that listens, learns, and challenges
ChatGPT
A conversational AI system that listens, learns, and challenges
Capturing Screenshots Programmatically With R
As part of our work documenting R-Universe, we’re adding screenshots of the interface to the documentation website. Taking screenshots manually could quickly become very cumbersome, especially as we expect they’ll need updating in future: we might want to change the universes we feature, the interface might improve yet again and therefore look slightly different. Therefore, we decided to opt for a programmatic approach. In this post we shall present our learnings from using the R packages chromote and magick to produce screenshots.
Display Idiomatic Code to Construct Most R Objects
Prints code that can be used to recreate R objects. In a sense it is similar to base::dput() or base::deparse() but constructive strives to use idiomatic constructors.
Use a Join Controller to Document Your Work
This note describes a useful replyr tool we call a "join controller" (and is part of our "R and Big Data" series, please see here for the introduction, and here for one our big …
rquery/extras/JoinController.md at master · WinVector/rquery
Data Wrangling and Query Generating Operators for R. Distributed under choice of GPL-2 or GPL-3 license. - WinVector/rquery
How to enhance your R shiny application with httpOnly Cookies
httpOnly Cookies are crucial for security, protecting against cross-site scripting attacks in R Shiny apps. Read more about them here.
Draft for adding OAuth support to shiny by thohan88 · Pull Request #518 · r-lib/httr2
Info: This is a draft for discussion purposes. It's not a polished PR and currently includes minimal error handling and documentation. It may be big enough to warrant a separate package, bu...
Roxygen R6 Guide
mlr3: Machine Learning in R - next generation. Contribute to mlr-org/mlr3 development by creating an account on GitHub.
niffler/R/module2app.R at main · dataheld/niffler
Shiny development helpers.
shiny/R/history.R at main · rstudio/shiny
Shiny Query String and Hash Manipulation
shiny/R/graph.R at main · rstudio/shiny
Shiny reactlog visualizer and R6 class
The R Graph Gallery – Help and inspiration for R charts
The R graph gallery displays hundreds of charts made with R, always providing the reproducible code.
R Color Palette Finder
The ultimate tool for finding the perfect color palette for data visualization with R and paletteer. Explore over 2000 palettes, see them in action on various charts, simulate color blindness, and export ready-to-use R code snippets.
Create a table of shiny inputs
Create an interactive table of Shiny inputs by providing data and a table definition.
Handling Errors & Warnings in R | List of Typical Messages & How to Solve
How to fix error & warning messages in R - List of most common errors & warnings - Tutorials & examples on how to fix in RStudio
Wrapping APIs
httr2
gexijin/RTutor: Chat with your data via AI. https://RTutor.ai
Chat with your data via AI. https://RTutor.ai.
Shiny
Shiny is a package that makes it easy to create interactive web apps using R and Python.
Shiny was designed with an emphasis on distinct input and output components in the UI. Inputs send values from the client to the server, and when the server has values for the client to display, they are received and rendered by outputs.
You want the server to trigger logic on the client that doesn’t naturally relate to any single output.
You want the server to update a specific (custom) output on the client, but not by totally invalidating the output and replacing the value, just making a targeted modification.
You have some client JavaScript that isn’t related to any particular input, yet wants to trigger some behavior in R. For example, binding keyboard shortcuts on the web page to R functions on the server, or alerting R when the size of the browser window has changed.
agnostic, idiomatic data filter module for shiny
a small data filter module for shiny.
Rectangling
Rectangling is the art and craft of taking a deeply nested list (often
sourced from wild caught JSON or XML) and taming it into a tidy data set of
rows and columns. This vignette introduces you to the main rectangling tools
provided by tidyr: `unnest_longer()`, `unnest_wider()`, and `hoist()`.
How to Wrangle JSON Data in R with jsonlite, purr and dplyr - Robot Wealth
Working with modern APIs you will often have to wrangle with data in JSON format. This article presents some tools and recipes for working with JSON data with R in the tidyverse. We’ll use purrr::map functions to extract and transform our JSON data. And we’ll provide intuitive examples of the cross-overs and differences between purrr ... Read more
R - JSON Files
R - JSON Files - JSON file stores data as text in human-readable format. Json stands for JavaScript Object Notation. R can read JSON files using the rjson package.
How to read multilevel json data and convert to Data frame in R
this is exactly the data i have when i load data from mongolite no i need to remove the list with string concatenation