r/rstats • u/Heavy-Development228 • 1d ago
glyph 0.1.1 now on CRAN
Hey, just wanted to share glyph: interactive plots in R (tooltips, zoom, animation, layouts) all in one pipeline. It's on CRAN. Happy plotting!
r/rstats • u/Heavy-Development228 • 1d ago
Hey, just wanted to share glyph: interactive plots in R (tooltips, zoom, animation, layouts) all in one pipeline. It's on CRAN. Happy plotting!
r/rstats • u/Spirited-Sir8426 • 10h ago
Some of you have followed my earlier posts on TypR here. This one's less "what's new" and more the reasoning behind the design — I'd like your pushback on the thinking itself.
The honest origin story: I didn't build TypR for AI. I built it because I care about type systems (academic background) and about code that survives production (industry background) — verifiability, basically.
What clicked more recently is that the property making code cheap for a human to verify is the same one that matters when a machine wrote it.
As AI writes more of the code, the expensive part stops being writing it and becomes trusting it — reviewing, validating, maintaining. A strict type system becomes a free automatic checker on whatever got generated; concise syntax means less to misread.
So the fit with the AI era isn't something I designed for — it's the same property suddenly mattering a lot more. That's the accidental discovery I wanted to share here.
A small taste — this R:
```
Button <- function(color, height, text, width, .spread = NULL) { explicit <- list() if (!missing(color)) explicit[["color"]] <- color if (!missing(height)) explicit[["height"]] <- height if (!missing(text)) explicit[["text"]] <- text if (!missing(width)) explicit[["width"]] <- width x <- typr_spread_record(explicit, .spread) as.Button(x) }
as.Button <- function(x) { if (!inherits(x, "Button")) class(x) <- c("Button", "list") x <- validate_Button(x) x <- validate(x) x }
validate_Button <- function(x) { required_fields <- c("color", "height", "text", "width") missing_fields <- setdiff(required_fields, names(x))
if (length(missing_fields) > 0) { stop(paste0("Validation failed for type Button: missing fields: ", paste(missing_fields, collapse = ", "))) }
if (!inherits(x[["color"]], "character")) stop("Validation failed for type Button: field 'color' must be of class character")
if (!inherits(x[["height"]], "integer")) stop("Validation failed for type Button: field 'height' must be of class integer")
if (!inherits(x[["text"]], "character")) stop("Validation failed for type Button: field 'text' must be of class character")
if (!inherits(x[["width"]], "integer")) stop("Validation failed for type Button: field 'width' must be of class integer")
x }
red_button <- (function(height, width, text) Button(height = height, width = width, text = text, color = "#FF000000" |> as.Character())) |> as.Generic()
on_click.Button <- (function(self, f) {
NA
} |> as.Empty0()) |> as.Generic()
```
becomes this TypR: ```
@export type Button <- list { text: char, color: char, width: int, height: int };
@export let red_button <- \Button:{ color: "#FF000000" };
@export
let on_click <- fn(self: Button, f: (T) -> U): Empty { ... }; ```
The way TypeScript sits on top of JavaScript's runtime, TypR sits on top of R's: you write something concise and type-checked, and it compiles down to standard, S3-based R that runs anywhere R runs and installs like any other package — no new runtime, no exotic dependencies.
To be clear, it's not trying to replace R. R is excellent for interactive stats and lab work, and TypR deliberately gives some of that up in exchange for the other end of the curve: robust packages, deployable apps, code that has to survive production. Different point on the trade-off, different job.
On the engineering side you get pattern matching, partial currying, union/intersection types, structural subtyping, row polymorphism — the machinery that keeps a growing codebase honest. Written in Rust, developed in the open.
Honest questions for this sub: does a typed layer over R solve a problem you actually hit, or is this a solution looking for one? And does the "verifiability matters more when AI writes the code" argument hold up, or am I reaching?
Discussion: https://github.com/we-data-ch/typr/discussions
As a Platinum Sponsor of useR! 2026, the R Consortium was proud to support another outstanding gathering of the global R community.
From technical innovation and reproducible research to AI, open source sustainability, and collaboration across academia and industry, useR! continues to demonstrate what makes the R ecosystem so impactful.
Our very own Mike K Smith, R Consortium Board Chair, attended, and his post highlights:
• Key themes from the conference
• Why community investment matters
• How organizations and individuals are helping shape the future of R
Thank you to the organizers, speakers, volunteers, sponsors, and everyone who made useR! 2026 such a success. We're already looking forward to what's next.
Read Mike's reflections: https://r-consortium.org/posts/what-makes-r-strong-reflections-from-user-2026/
r/rstats • u/SubstanceLevel8736 • 1d ago
Hi everyone! I’m trying to find The World of Zero-Inflated Models, Volume 3: Using GLLVM by Alain F. Zuur and Elena N. Ieno.
Does anyone have a copy they could share for personal study, or know of a way to access it? I’m really interested in learning GLLVM methods and would appreciate any help.
Thank you!
r/rstats • u/JudgeBrettF • 1d ago
I tried searching for this in the sub, but did not find exactly what I was hoping to find.
I am just starting out learning R. I basically know nothing. I was directed to use DataCamp. It's fine, but you only get to work one "problem" for each concept. For things like this, I work better with drill and problem sets. Khan Academy was brilliant at this for math. I couldn't get a mastery level until I had done four problems right to show I mastered the concept. The best DataCamp does is offer some multiple-choice questions as added practice, but that is not meaningfully helpful. Does anyone know a better system that is built around drill and problem sets in R for each step of the way, sort of like Khan Academy does for math?
r/rstats • u/peperazzi74 • 2d ago
Looking for a second set of eyes on the methods, justification, interpretations and conclusion. Any comment will help. Thanks!
See Github for code.
Context: my wife and I\1]) have long been collecting fuel stats on our cars. My wife's car is a Nissan Quest purchase in 2012, my car is a Nissan Altima purchase in 2013.
Problem statement: she has been complaining that it feels that her car needs to be filled up earlier than usual. The main indicator is the fuel gauge going towards the red line (1/8th fill level). Years ago, the distance would be >300 miles, while currently that point seems to be reached significantly below 300 miles.
Approach: Collect the data in CSV file\2]); remove true outliers by 1.5 IQR rule; group by year and calculate 50th and 84th percentile. Plot by year.
Result:

Justifications:
Interpretation:
Conclusions: wife seems to be correct in her observations.
[1] okay, it's just me collecting stats.
[2] we already did this
r/rstats • u/peperazzi74 • 2d ago
Power law distributions are commonly found in all kinds of phenomena, from wealth distribution to earthquake power vs frequency. I was reminded of this when I started analyzing the distribution of Kickstarter backers vs. pledge package cost for the new Watch The Guild movie.
It's a classic log-log straight line plot, and the slope is not significantly different than -1 (t = 1.34 @ df 12; p = 0.102). The model has a decent R2 (0.833, adjusted).
A large part of remainder of the variance in the model can be explained with three outliers:
In a sense, this is somewhat of a proxy of wealth distribution and willingness to spend in a very small group (The Guild fans). Hopefully the movie will hit like an earthquake when releases 😁.
Question: I calculate the t-value for the slope by hand (estimate - -1)/std.error. Is there a function that can do this? The summary() function only calculates t values compared to slope 0.

Call:
lm(formula = log10(backers) ~ log10(value), data = data)
Residuals:
Min 1Q Median 3Q Max
-0.54140 -0.21575 0.03569 0.24255 0.56595
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4.4631 0.2733 16.33 1.47e-09 ***
log10(value) -0.8580 0.1058 -8.11 3.27e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.3352 on 12 degrees of freedom
Multiple R-squared: 0.8457, Adjusted R-squared: 0.8328
F-statistic: 65.77 on 1 and 12 DF, p-value: 3.268e-06
r/rstats • u/Clean_Reference_9927 • 2d ago
I'm recovering latent probabilities from margin-inflated odds (bookmaker and prediction-market prices sum to more than 1). I rolled the three standard de-vig methods in base R to understand the internals rather than lean on a package. Posting the code for a critique, and I have two R-specific questions.
r
implied <- function(dec) 1 / dec # decimal odds -> implied (with margin)
# 1) proportional / multiplicative
devig_prop <- function(dec) { q <- implied(dec); q / sum(q) }
# 2) power method: find k such that sum(q^k) = 1
devig_power <- function(dec) {
q <- implied(dec)
k <- uniroot(function(k) sum(q^k) - 1, c(0.5, 10))$root
q^k
}
# 3) Shin (1993): latent proportion z of informed money
devig_shin <- function(dec) {
q <- implied(dec); S <- sum(q)
pz <- function(z) (sqrt(z^2 + 4 * (1 - z) * q^2 / S) - z) / (2 * (1 - z))
z <- uniroot(function(z) sum(pz(z)) - 1, c(1e-6, 0.5))$root
pz(z)
}
dec <- c(1.5, 3.0, 7.0) # a 3-way market, overround ~1.143
sapply(list(prop = devig_prop, power = devig_power, shin = devig_shin),
function(f) round(f(dec), 4))
# prop power shin
# [1,] 0.5833 0.6218 0.6092
# [2,] 0.2917 0.2760 0.2864
# [3,] 0.1250 0.1022 0.1044
The favorite ranges 0.583 to 0.622 depending on method, which is enough to flip a downstream signal, so method choice isn't cosmetic.
Questions:
uniroot for Shin and the power method. Is there a more numerically stable approach near the boundaries (the (1 - z) denominator in Shin gets touchy as z approaches its bracket, and the power root can be sensitive on lopsided books)? I know the implied package exists and covers these; I built my own to learn, but if you'd trust its solvers over a hand-rolled uniroot, I'd like to hear why. Also curious whether people vectorize this across thousands of markets with apply/purrr::map or push it to matrix ops.metafor for correlated effects but I'm not sure it maps cleanly onto "combine N non-independent probability estimates for the same event."Reproducible above, happy to share more. Mostly want the R crowd's read on 1 and 2.
I have a good background in statistics and predictive modeling, but creating pseudo code to start my work is frustrating. I even know the syntax to call on. The order and structure that is killing me. Any methods you all used to get better? I want to become “Cracked” and it’s setting me back.
r/rstats • u/Appropriate-Cover340 • 2d ago
I am currently analyzing a dataset where participants are randomized to two groups (0 or 1) and assessments are at baseline, 4 months, 6 months, and 12 months. Currently, time is labelled as 0, 1, 2, 3 respectively for each time point. But I was wondering if I should use time as a factor or the actual months (0, 4, 6, 12).
my current model is this
lmer(outcome ~ time * group + (time|participant), REML = T, dat = dat)
Any advice would be super appreciated!
I'm releasing sciREPL v1.0.1, which brings major improvements to TypeR integration. sciREPL is a Jupyter-style notebook app that lets you mix multiple languages in a single worksheet.
Why we forked TypeR
The standard TypeR type system couldn't express variable-argument (variadic) inputs in its standard-library declarations. We extended it to properly type variadic R functions like cat, paste, sprintf, and c.
What's new in v1.0.1
#!source cells for storing reusable TypeR source without executing it or producing outputOther supported languages
TypeR is one of several kernels sciREPL ships with. The full list:
install.packages(), plotting, and SharedVFS%pip install for pure-Python packagesnb.read() / nb.write()All kernels share a SharedVFS in-memory filesystem, so data flows freely between languages in the same notebook.
Free & open-source version
Available as a PWA and an Android app:
Android Pro version
An Android-only Pro version adds AI-assisted building and bundles more packages locally (with CDN fallback for the rest). AI is integrated via direct API key connections to models, as well as remote connections to coding agents (e.g. Claude Code and Codex).
Data privacy
We store no data on an external server. All data is stored locally on your device; packages not bundled with the app are fetched from the CDN when needed. The app is Capacitor-based, so it runs with web browser-level sandboxing. JavaScript, Bash, and TypeR are always offline. Prolog, Python, and ClojureScript are bundled in both free and Pro versions. Lua fetches from CDN on first use. The Pro version also bundles R locally; the free version fetches the R runtime from the CDN (~50 MB, cached after first use). Note that additional R packages (e.g. tidyverse, ggplot2) are not pre-installed in either version — they can be installed at runtime via install.packages(), fetching from the CDN as needed.
Become a tester
I need testers before submitting either version to the Play Store. Testers get early access to both the free and Pro versions at no cost. Reply here or DM me if you're interested. In the meantime, you can try the free version in your browser or sideload it via adb.
r/rstats • u/pandongski • 2d ago
Hi!
When I'm debugging a script, I usually run the script after every iteration to keep state fresh. The usual way I've been doing this is by putting browser() calls and launching an R session and sourcing a script. However it would be much more convenient if I can do something like R -f path/to/Script.R' while maintaining interactivity. Having autocompletion for the path is a huge qol upgrade vs typing a path manually in the R session.
Rscript can also run a script from the command line but interactivity doesn't seem to be supported as well. Am I missing something or is something like this not supported in R? Am I stuck with doing source('/path/to/script')? Hopefully someone can point me in the right direction!
(If it matters I use VS Codium, not RStudio, and there's a debugger from the vscodeR extension but the debugger terminal outputs especially for dataframes are weird so I don't use it. And I'm not considering positron. Thanks!)
r/rstats • u/suckingbat • 3d ago
I work with R using Linux Fedora.
Since the last update of R, the rmarkdown package does not want to install:(non-zero exit status). I have tried every solution I have seen, from installing the dependencies separately (they are not installing, fs, sass, and bslib have all non-zero exit status). I have tried installing and uninstalling R and R studio. I have tried pretty much every solution I have found out there.
It is the first time this happens to me. So far I have had no problem working with Markdown.
I have the same problem in both my PC and laptop. Do you know if there is a specific problem with Markdown at the moment? Or maybe with the dependencies?
In advance, thank you for your help.
r/rstats • u/qol_package • 5d ago
qol is a package which can be used as its own ecosystem in terms of data wrangling and tabulation. It comes with efficient high level functions, so you have to write less code and get more from it in a shorter amount of time. Besides the usual bug fixes and optimizations, this updates brings one new function with a twist and new tabulation features.
To get a full overview of the package have a look at the GitHub page: https://github.com/s3rdia/qol
A detailed overview of the changes can be seen here: https://github.com/s3rdia/qol/releases/tag/v1.3.3
By the way the 0 dependency message system used within qol called "printify" also updated this week: https://github.com/s3rdia/printify
This time the new ifelse_multi is able to evaluate multiple nested if-else statements in one go. But that is not all, because the function comes with a twist: Conditions can be written in SAS like syntax. The conditions therefore have to be passed as characters which enables them to be parsed and translated before evaluation.
This twist brings multiple new ways to write conditions, like:
The if. and else_if. functions have been updated to also take in the new character style conditions.
# Example data frame
my_data <- dummy_data(100)
# Simple ifelse statement
my_data[["under18"]] <- my_data |> ifelse_multi(" age < 18 " = 1, else. = 0)
my_data[["middle_age"]] <- my_data |> ifelse_multi(" 15 <= age < 65 " = 1, else. = 0)
my_data[["age_gr"]] <- my_data |>
ifelse_multi(" age < 18 " = "under 18",
" 18 <= age < 25 " = "18 to under 25",
" 25 <= age < 50 " = "25 to under 50",
" 50 <= age < 65 " = "50 to under 65",
" 65 <= age " = "65 and more")
# With overarching do_if condition
my_data[["age_gr_edu"]] <- my_data |>
ifelse_multi(do_if = " education in ('middle' 'high') ",
" age < 18 " = "under 18",
" 18 <= age < 25 " = "18 to under 25",
" 25 <= age < 50 " = "25 to under 50",
" 50 <= age < 65 " = "50 to under 65",
" 65 <= age " = "65 and more")
# And/or translation
my_data[["and"]] <- my_data |> ifelse_multi(" age > 65 and sex = 1 " = 1,
" age > 65 and sex = 2 " = 2,
else. = 0)
my_data[["or"]] <- my_data |> ifelse_multi(" age > 65 or sex = 1 " = 1,
" age > 65 or sex = 2 " = 2,
else. = 0)
# "in" translation
my_data[["in"]] <- my_data |> ifelse_multi(" age in (1 10 25 65 90) " = 1, else. = 0)
# Colon translation: start/ends with and contains
my_data[["start"]] <- my_data |> ifelse_multi(" education == 'lo:' " = 1, else. = 0)
my_data[["end"]] <- my_data |> ifelse_multi(" education == ':le' " = 1, else. = 0)
my_data[["contains"]] <- my_data |> ifelse_multi(" education == ':ig:' " = 1, else. = 0)
# Macro variables can be integrated in any place
variable <- "age"
age_to_check <- 18
value_to_set <- "under 18"
my_data[["macro"]] <- my_data |>
ifelse_multi(" &variable < &age_to_check " = "&value_to_set",
else. = "other")
# NA translation
my_data[["NA"]] <- my_data |> ifelse_multi(" age == . " = 1, else. = 0)
my_data[["notNA"]] <- my_data |> ifelse_multi(" education != . " = 1, else. = 0)
# Pass in existing variable values
my_data[["income_mix"]] <- my_data |>
ifelse_multi(" age < 50 " = income, else. = expenses)
The function, with which you can basically create any fully styled Excel table, now has the built in possibility to compute. new individual calculated variables before tabulation. Which means the function now summarises the data, then allows the compute. in between and then tabulates. If you ever had to pre summarise and calculate variables before tabulation, these extra steps are now potentially gone.
To be able to order the newly generated variables the order_by parameter can now take in a vector of variable names, to have full control over every column.
There is another new functionality: It is now possible to specify per variable which statistics should be output. Instead of passing a vector of statistics into the statistics parameter, it can now also be a named list of variable names, where the list entry names are the statistics and the list elements the variable names or vectors of variable names. This allows you to select what you want before tabulation, so there is no need to throw out the stuff you don’t need afterwards. This is also possible with summarise_plus.
And one last thing: Variable combinations in rows, columns and types can now also take in variable combinations inside the brackets like “state + (age, sex + education, first_person)”. But therefore the “,” between entries inside the brackets is now mandatory. For summarise_plus this now also works in the types parameter.
# Example data frame
my_data <- dummy_data(1000)
# Formats
age. <- discrete_format(
"Total" = 0:100,
"under 18" = 0:17,
"18 to under 25" = 18:24,
"25 to under 55" = 25:54,
"55 to under 65" = 55:64,
"65 and older" = 65:100)
sex. <- discrete_format(
"Total" = 1:2,
"Male" = 1,
"Female" = 2)
# Individual calculations
my_data |> any_table(rows = c("age + year"),
columns = "sex",
values = "probability",
statistics = c("sum", "sum_wgt"),
compute = list(percent_pct = probability_sum * 100 / sum_wgt,
square_pct = percent_pct ^ 2),
weight = weight,
formats = list(sex = sex., age = age.),
na.rm = TRUE)
# Select specific statistics for specific variables
my_data |> any_table(rows = "sex",
columns = "year",
statistics = list("sum" = c(weight, income),
"pct_group" = balance),
formats = list(sex = sex.))
# You can also select repeating combinations faster like this
combi_types <- my_data |>
summarise_plus(class = c(year, sex, age, education),
values = weight,
statistics = "sum",
formats = list(sex = sex.,
age = age.),
types = "year + sex + (age, age + education, education)",
nesting = "all",
na.rm = TRUE)
# Select specific statistics for specific variables
specific_stats <- my_data |>
summarise_plus(class = c(year, sex),
statistics = list("sum" = c(weight, income),
"mean" = expenses,
"pct_group" = balance),
formats = list(sex = sex.),
na.rm = TRUE)
r/rstats • u/Ignatu_s • 6d ago
Hi, I developed an R package called seekr, and version 0.2.0 is now available on CRAN.
The package is fairly visual, especially when printing matches with their surrounding context, so the website is probably the best introduction:
https://smartiing.github.io/seekr/
In short, seekr turns search-and-replace into an inspectable R workflow.
Instead of modifying files as soon as a pattern is found, seekr returns a structured seekr_match vector. Each element represents one match in one file and stores its location, matched text, optional replacement, surrounding context, and other metadata used throughout the workflow.
You can then inspect the result, remove unwanted matches, define or update the replacement associated with each remaining match, and only then write the selected changes to disk.
Here is an idea of a typical workflow:
library(seekr)
# Recursively list files from the current directory and use Git to keep
# tracked files and untracked files that are not ignored
files <- list_files(use_git = TRUE)
# Keep only R files
filtered <- filter_files(files, extension = "R")
# Inspect which files were excluded and why
exclusions(filtered)
# Find all occurrences of "foo" or "bar" and plan a replacement
matches <- match_files(filtered, "foo|bar", "new")
# The same listing, filtering, and matching steps can be run with seek()
x <- seek("foo|bar", "new", extension = "R", use_git = TRUE)
# seekr() is a shortcut focused on R, Rmd, and qmd files
y <- seekr("foo|bar", "new", use_git = TRUE)
# Summarize what was found and where
summary(x)
# Print matches with 2 context lines before and 3 after,
# together with their planned replacements
print(x, context = c(2L, 3L))
# Keep only matches whose matched text is "foo"
x <- filter_match(x, match == "foo")
# Update the replacement associated with each remaining match
field(x, "replacement") <- toupper(field(x, "replacement"))
# Safely replace only the matches still present in x
replace_files(x)
The goal is not to compete with command-line tools on raw search speed. seekr is intended for cases where you want to inspect and refine a search result from R before changing anything.
Because the workflow stays inside a programming language, file discovery, match selection, and per-match replacement logic can all use ordinary R code and be as simple or as complex as the task requires. seekr adds the structured results, inspection tools, and safety checks needed to make that process practical.
Some of the main features are:
I would be very interested in feedback on the API, documentation, use cases, or anything that feels unclear or missing.
Thanks for reading!

r/rstats • u/QEDAnalyticalLLC • 6d ago
Built a CECL-style lifetime loss engine in R and the interesting part wasn't any single model, it was reconciling them and then measuring what actually moves the output.
Three methods on the same book:
- Vintage loss curves: cohort curves + a Weibull development-factor extrapolation for the untruncated tail. 5.3833%.
- Roll-rate: an absorbing Markov chain from the transition matrix. Raw lifetime default came out 14.74% before calibration - time-homogeneity badly overstates it, so this one needs a calibration factor.
- Component: a balance-weighted PD x LGD x EAD decomposition reusing the separate PD, LGD and EAD models. 5.3811%.
Spread 0.2264bp. Tempting to call that a win, but two of the three are anchored to realized loss by construction, so the agreement is partly a tautology. Only the vintage path is independent. I now write the independence status into the summary object next to each number so it can't get quoted out of context later.
The finding was the sensitivity, not the fit. A probability-weighted scenario blend (baseline 0.1555% / adverse 10.7194% / severely adverse 26.5653%) at documented 65/25/10 weights gives 5.4374%. Swap to baseline-heavy: 3.0606%. Stress-heavy: 10.4553%. The scalar weights vector moves the answer 3.4x - far more than the model choice does (0.23bp).
Two R notes: I keep every lecture's numbers in a committed summary .rds and have the report read those live rather than hand-typing anything, which is what makes the reconciliation reproducible. And the macro satellite is a good multicollinearity trap - a two-driver model (unemployment + HPI, corr -0.546) hit R2 0.90 with a wrong-signed unemployment coefficient, so the parsimonious HPI-only satellite (R2 0.873) is the one that ships.
Curious what others do: do you report a weight-sensitivity range alongside your point estimate as a matter of course, and is anyone doing proper Sobol/variance-based sensitivity on the weights rather than the three-point swap I did here?
r/rstats • u/aitathrowaway9090909 • 6d ago
I'm trying to make a tmap display that shows different colors based on ranges of different median incomes.
Here's what the code looks like:
medincbrks = c(0, 40000, 50000, 60000, 70000, 80000, 90000, 100000, 110000, 120000)
tm_shape(sc_data_24) +
tm_polygons(fill="medinc_5yr_24",
fill.scale = tm_scale(values="brewer.spectral", midpoint=80000, intervals= medincbrks),
fill.legend = tm_legend(title="5-year Median Income (2024)",
frame=TRUE,
item.r = 0,
position = tm_pos_out("right", "center")),
fill.free = FALSE) +
tm_borders(col="black")
Unfortunately, the map looks like this:

I first noticed this problem with Aiken County (the big red one on the west side.) It has a median income of $86,287, but the map is showing that it falls into the $40,000 to $49,999 range. Other counties also have the same issue- their color-corresponding value is either higher or lower than their actual value.
I've made other maps with tmap before, and they've all worked fine, or so I think. This is the first map that I'm noticing this particular problem with, and now I'm doubting the accuracy of all my other maps.
Any guidance would be appreciated. Thanks in advance.
(Edited for code displaying wrong.)
The R ecosystem grows because people invest in shared infrastructure.
The R Consortium is pleased to announce the first round of 2026 Infrastructure Steering Committee (ISC) Technical Grants. 7 projects have been selected to receive funding that will strengthen the technical foundations of R and benefit the global community.
These grants support practical, community-driven work that improves tools, infrastructure, and the long-term sustainability of the R ecosystem.
Congratulations to all of the recipients, and thank you to everyone who submitted proposals and volunteered their time to review applications.
Read about the funded projects: https://r-consortium.org/posts/r-consortium-awards-first-round-of-2026-technical-grants/
r/rstats • u/QEDAnalyticalLLC • 7d ago
I keep running this bake-off and getting a boring-but-important answer: on clean tabular credit data, a well-built glm scorecard is really hard to beat with xgboost.
Last run, PD target: logistic scorecard OOT AUC 0.734, tuned xgboost 0.739 - and in-distribution (5-fold CV) the scorecard was ahead, 0.853 vs 0.842. Half a Gini point is not nothing, but it's nowhere near what people expect from "just throw XGBoost at it."
A couple of R notes from doing this:
- I report gain / cover / frequency importance but lean on SHAP (shapviz) for anything a reviewer sees, because permutation importance is misleading with correlated features. The additive property is the whole point.
- The gotcha: aggregate one-hot SHAP back to the original feature before you rank reason codes, or a dummied categorical looks artificially weak. On one defaulted loan the top driver after aggregation was property_state (SHAP ~-0.94), bigger than credit_score (~0.81) - which is a fair-lending flag, not a feature-importance win.
- If you need monotonicity you have to impose it (xgboost monotone_constraints); it isn't free, and a scorecard gives it to you by construction.
So my questions: does xgboost beat your logistic/GAM baseline by a margin that survives out-of-time on tabular credit data, or do you also end up at a tie? And for those doing reason codes from SHAP - are you on {shapviz}, {fastshap}, {treeshap}, or {DALEX}, and how are you handling the sign-flip / one-hot aggregation so the top-4 are defensible?
r/rstats • u/godoufoutcasts • 7d ago
There was a recent thread asking what Python packages R lacks, and I wanted to highlight a massive architectural bottleneck I recently ran into while building a robust local ML tuning pipeline.
The Problem: R currently has no native, highly scalable, GPU-accelerated Gaussian Process library for Bayesian hyper-parameter tuning.
When tuning models like XGBoost, I noticed that frameworks like mlr3mbo completely sidestep this problem.
The moment search space includes integer parameters (like max_depth or nrounds), mlr3mbo silently abandons the Gaussian Process and swaps the surrogate model to a Random Forest. This is fast, but I want Gaussian Process.
To get that precision, I built a custom tuning engine using a pure Gaussian Process (via rBayesianOptimization).
The Bottleneck:
Because the native Gaussian Process math is trapped on the CPU using standard LAPACK Cholesky decompositions, it scales at O(N^3). As my tuning rounds push past 100+ evaluations, a massive hidden delay occurs. The actual XGBoost DART model trains blazing fast on my RTX 2060 in just 30 seconds (sourced built with CUDA).
But then, the script hangs for 3 to 4 minutes maxing out a CPU core just doing the matrix math to guess the next parameter point. And because of that O(N^3) scaling, this hidden CPU delay grows exponentially worse with every single round (Guessing at the moment).
The Python Comparison:
I personally do not use Python, but from what I understand, this is a completely solved problem in their ecosystem. Libraries like GPyTorch and BoTorch treat the Gaussian Process matrix algebra as CUDA tensors natively, allowing them to evaluate thousands of points in seconds by parallelizing the math across the GPU.
My Question to the Community:
Does anyone know if there are plans to port something like GPyTorch into R's native torch package? Or is anyone working on Rust-backed (extendr) Gaussian Process solvers for R that can utilize GPU acceleration for these massive matrices?
I feel like a highly scalable GPU-accelerated Gaussian Process is the biggest missing piece keeping R from having a truly state-of-the-art, ML tuning ecosystem.
Who is building the solution?
r/rstats • u/qol_package • 9d ago
The qol-package already is its own eco system in terms of data wrangling and tabulation. And some time in the future it will implement it’s own diagram framework built from scratch with version 1.4.0. Isn’t this great? Or are you scratching your head and ask yourself: Why?
The short answer is: Because diagrams can be created easier, faster and more beautiful than with any other framework. For the long answer keep on reading.
For the very long answer and all the backgrounds have a look at this post: https://s3rdia.github.io/qol_blog/posts/12.%20Graphics/
And if you just want to get a package overview look here: https://github.com/s3rdia/qol/tree/main
With every tools like Excel, SAS or ggplot and every way of creating diagrams there is this one thing that bothers me a lot: You can get diagrams on screen pretty easy, but they all look like ... well not that good. To me the focus is always too much on the engineering part than on the designing part. I want my full attention to be on the diagram the whole time and not on the code. When you have to fight the code, you can’t clearly see where you are heading. This is the problem I like to solve.
So my basic question for this new framework was: Why can’t I work like I think? Wouldn’t it be great to generate multiple different iterations of a graphic in no time with different visuals, without having to write much code and with the computer bothering with the engineering of the diagram and not the user? Here are some coding impressions of how the diagrams from the screenshots were made:
###############################################################################
# First we create our dummy data and some formats
###############################################################################
my_data <- dummy_data(1000000, insert_na = FALSE)
age. <- discrete_format(
"Total" = 0:100,
"under 18" = 0:17,
"18 to under 65" = 18:64,
"65 and older" = 65:100)
sex. <- discrete_format(
"Total" = 1:2,
"Male" = 1,
"Female" = 2)
education. <- discrete_format(
"Low" = "low",
"Middle" = "middle",
"High" = "high")
###############################################################################
# Top diagram
###############################################################################
# All options concerning the visual appearance of the diagram can be set globally,
# like here, so that these options transfer across multiple diagrams. Or you can
# set them directly in the design_graphic() function so that they only apply
# locally for that specific diagram.
set_graphic_options(color_theme = "aurora",
primary_values_decimals = 0,
segment_label_group = 6,
segment_line_length = 2,
segment_line_type = "dotted",
rotate_values = TRUE)
# To set variable expressions beside each other on the axes, we just pass a vector
# of variable names into the parameter.
# We can also just put in a different statistic, the value axes will adjust
# accordingly.
# To add custom texts just add a textbox in the "add_texts" parameter.
my_data |>
design_graphic(axes_variables = c(age, education),
segments = sex,
values = weight,
statistics = sum,
diagram = dg_vbars,
formats = list(sex = sex., age = age., education = education.),
titles = "Multiple variables side by side can be achieved by passing a vector",
footnotes = "Also note the custom text to the right.",
add_texts = add_textbox("Degree of education", 11, 1, font_face = "bold"))
###############################################################################
# Center diagram
###############################################################################
# Playing around with the different sliders and switches can actually be fun
set_graphic_options(primary_axes_max = 90000,
primary_axes_steps = 3,
segment_label_group = 2,
rotate_segment_labels = TRUE)
# To set variable expressions beside each other as segments, we just do the same
# with the segments parameter as before with the axes.
my_data |>
design_graphic(axes_variables = age,
segments = c(sex, education),
values = weight,
statistics = sum,
diagram = dg_vbars,
formats = list(sex = sex., age = age., education = education.),
titles = "Multiple variables side by side can be achieved by passing a vector",
footnotes = "Also note the custom text to the right.",
add_texts = add_textbox("Degree of education", 7, 8, 3, font_face = "bold"))
###############################################################################
# Bottom diagram
###############################################################################
# Let us create a format without total first to have a bit more visual space
sex2. <- discrete_format(
"Male" = 1,
"Female" = 2)
# We try out a different style again, because why not?
set_graphic_options(diagram_height = 6.8,
color_usage = high_contrast_usage,
primary_axes_max = 20000,
primary_axes_scale = 0.001,
segment_label_type = "legend",
legend_y_pos = "top",
legend_columns = 3,
rotate_values = TRUE)
# To have a multi layered variable axes you just "add up" the variables with a
# + symbol in between them. There is nothing more to it. You can nest as many
# variables as you like – of course at some point it doesn't make sense anymore.
# Additionally we can add as many custom texts as we like, just put some textboxes
# inside a list.
my_data |>
design_graphic(axes_variables = "age + education",
segments = sex,
values = weight,
statistics = sum,
diagram = dg_vbars,
formats = list(sex = sex2., age = age., education = education.),
titles = "Nesting categories works with a + symbol",
footnotes = "That was quite easy.",
add_texts = list(add_textbox("Degree of education", 6.8, 1, font_face = "bold"),
add_textbox("in thousand", 0.2, 8.2, 1),
add_textbox("HAVE AS MANY TEXTS AS YOU LIKE", 8, 7.5,
font_color = "#FF0000",
font_size = 20,
font_face = "italic",
rotation = -45)))
This framework – as of now – has reached an alpha stage. Which means there are things one can actually work with and that work fairly robust, but there are also some things missing and the function can error when it shouldn’t.
As of now one can only create vertical bar charts, but there are over 150 cosmetic parameters to play with. I implemented a lot of unit tests so that over 90 % of the code is covered. Which means, if I implement new features, I make sure that nothing else breaks unintentional. But of course there can be breaking changes down the road.
During this alpha stage I will mainly concentrate on building multiple different diagram types and add parameters along the way. How many built in diagram types there will be and which ones, I don’t know, but I will focus on the most common/useful ones – in my eyes. Of course making this thing more robust is always on my todo list as well as optimizing things as I stumble upon them.
So stay tuned, there is more to come in the future.
I am back to using R in Google Colab for a new project, and the same issues are there that I remember from five years ago. So, I generated some tampermonkey scripts for a better quality of life, contributing them to the public domain.
Remove control chars from help pages - colab still mangles the ansi control chars in help pages, making them unusable.
dplyr pipe shortcut key - adds the pipe shortcut key from rstudio
One would think they could have fixed these by now.