LatinR 2023
Photo by Ken Suarez on Unsplash
Felienne Hermans - The Programmer’s Brain
SEXP Cdqrls(SEXP x, SEXP y, SEXP tol, SEXP chk)
{
SEXP ans;
SEXP qr, coefficients, residuals, effects, pivot, qraux;
int n, ny = 0, p, rank, nprotect = 4, pivoted = 0;
double rtol = asReal(tol), *work;
Rboolean check = asLogical(chk);
ans = getAttrib(x, R_DimSymbol);
if(check && length(ans) != 2) error(_("'x' is not a matrix"));
int *dims = INTEGER(ans);
n = dims[0]; p = dims[1];
if(n) ny = (int)(XLENGTH(y)/n); /* y : n x ny, or an n - vector */
if(check && n * ny != XLENGTH(y))
error(_("dimensions of 'x' (%d,%d) and 'y' (%d) do not match"),
n,p, XLENGTH(y));
/* These lose attributes, so do after we have extracted dims */
if (TYPEOF(x) != REALSXP) {
PROTECT(x = coerceVector(x, REALSXP));
nprotect++;
}
if (TYPEOF(y) != REALSXP) {
PROTECT(y = coerceVector(y, REALSXP));
nprotect++;
}
/* < more code > */
}
drop_strata <- function(expr, in_plus = TRUE) {
if (rlang::is_call(expr, "+", n = 2) && in_plus) {
lhs <- drop_strata(expr[[2]], in_plus = in_plus)
rhs <- drop_strata(expr[[3]], in_plus = in_plus)
if (rlang::is_call(lhs, "strata")) {
rhs
} else if (rlang::is_call(rhs, "strata")) {
lhs
} else {
rlang::call2("+", lhs, rhs)
}
} else if (rlang::is_call(expr)) {
expr[-1] <- purrr::map(as.list(expr[-1]), drop_strata, in_plus = FALSE)
expr
} else {
expr
}
}
test_that("`drop_strata()` removes strata term in a series of `+` calls", {
expect_equal(
drop_strata(rlang::expr(a + strata(x))),
rlang::expr(a)
)
expect_equal(
drop_strata(rlang::expr(a + strata(x) + b)),
rlang::expr(a + b)
)
})
test_that("`drop_strata()` does not remove strata in other cases", {
expect_equal(
drop_strata(rlang::expr(a * (b + strata(x)))),
rlang::expr(a * (b + strata(x)))
)
})
drop_strata(a + b)
drop_strata <- function(expr, in_plus = TRUE) {
if (rlang::is_call(expr, "+", n = 2) && in_plus) {
lhs <- drop_strata(expr[[2]], in_plus = in_plus)
rhs <- drop_strata(expr[[3]], in_plus = in_plus)
if (rlang::is_call(lhs, "strata")) {
rhs
} else if (rlang::is_call(rhs, "strata")) {
lhs
} else {
rlang::call2("+", lhs, rhs)
}
} else if (rlang::is_call(expr)) {
expr[-1] <- purrr::map(as.list(expr[-1]),
drop_strata,
in_plus = FALSE)
expr
} else {
expr
}
}
level | line | what |
---|
drop_strata(a + b)
drop_strata <- function(expr, in_plus = TRUE) {
if (rlang::is_call(expr, "+", n = 2) && in_plus) {
lhs <- drop_strata(expr[[2]], in_plus = in_plus)
rhs <- drop_strata(expr[[3]], in_plus = in_plus)
if (rlang::is_call(lhs, "strata")) {
rhs
} else if (rlang::is_call(rhs, "strata")) {
lhs
} else {
rlang::call2("+", lhs, rhs)
}
} else if (rlang::is_call(expr)) {
expr[-1] <- purrr::map(as.list(expr[-1]),
drop_strata,
in_plus = FALSE)
expr
} else {
expr
}
}
level | line | what |
---|---|---|
0 |
drop_strata(a + b)
drop_strata <- function(expr, in_plus = TRUE) {
if (rlang::is_call(expr, "+", n = 2) && in_plus) {
lhs <- drop_strata(expr[[2]], in_plus = in_plus)
rhs <- drop_strata(expr[[3]], in_plus = in_plus)
if (rlang::is_call(lhs, "strata")) {
rhs
} else if (rlang::is_call(rhs, "strata")) {
lhs
} else {
rlang::call2("+", lhs, rhs)
}
} else if (rlang::is_call(expr)) {
expr[-1] <- purrr::map(as.list(expr[-1]),
drop_strata,
in_plus = FALSE)
expr
} else {
expr
}
}
level | line | what |
---|---|---|
0 | 3 |
drop_strata(a + b)
drop_strata <- function(expr, in_plus = TRUE) {
if (rlang::is_call(expr, "+", n = 2) && in_plus) {
lhs <- drop_strata(expr[[2]], in_plus = in_plus)
rhs <- drop_strata(expr[[3]], in_plus = in_plus)
if (rlang::is_call(lhs, "strata")) {
rhs
} else if (rlang::is_call(rhs, "strata")) {
lhs
} else {
rlang::call2("+", lhs, rhs)
}
} else if (rlang::is_call(expr)) {
expr[-1] <- purrr::map(as.list(expr[-1]),
drop_strata,
in_plus = FALSE)
expr
} else {
expr
}
}
level | line | what |
---|---|---|
0 | 3 | |
-1 | in: a |
drop_strata(a + b)
drop_strata <- function(expr, in_plus = TRUE) {
if (rlang::is_call(expr, "+", n = 2) && in_plus) {
lhs <- drop_strata(expr[[2]], in_plus = in_plus)
rhs <- drop_strata(expr[[3]], in_plus = in_plus)
if (rlang::is_call(lhs, "strata")) {
rhs
} else if (rlang::is_call(rhs, "strata")) {
lhs
} else {
rlang::call2("+", lhs, rhs)
}
} else if (rlang::is_call(expr)) {
expr[-1] <- purrr::map(as.list(expr[-1]),
drop_strata,
in_plus = FALSE)
expr
} else {
expr
}
}
level | line | what |
---|---|---|
0 | 3 | |
-1 | in: a |
|
-1 | out: a |
drop_strata(a + b)
drop_strata <- function(expr, in_plus = TRUE) {
if (rlang::is_call(expr, "+", n = 2) && in_plus) {
lhs <- drop_strata(expr[[2]], in_plus = in_plus)
rhs <- drop_strata(expr[[3]], in_plus = in_plus)
if (rlang::is_call(lhs, "strata")) {
rhs
} else if (rlang::is_call(rhs, "strata")) {
lhs
} else {
rlang::call2("+", lhs, rhs)
}
} else if (rlang::is_call(expr)) {
expr[-1] <- purrr::map(as.list(expr[-1]),
drop_strata,
in_plus = FALSE)
expr
} else {
expr
}
}
level | line | what |
---|---|---|
0 | 3 | lhs is a |
drop_strata(a + b)
drop_strata <- function(expr, in_plus = TRUE) {
if (rlang::is_call(expr, "+", n = 2) && in_plus) {
lhs <- drop_strata(expr[[2]], in_plus = in_plus)
rhs <- drop_strata(expr[[3]], in_plus = in_plus)
if (rlang::is_call(lhs, "strata")) {
rhs
} else if (rlang::is_call(rhs, "strata")) {
lhs
} else {
rlang::call2("+", lhs, rhs)
}
} else if (rlang::is_call(expr)) {
expr[-1] <- purrr::map(as.list(expr[-1]),
drop_strata,
in_plus = FALSE)
expr
} else {
expr
}
}
level | line | what |
---|---|---|
0 | 3 | lhs is a |
0 | 4 |
drop_strata(a + b)
drop_strata <- function(expr, in_plus = TRUE) {
if (rlang::is_call(expr, "+", n = 2) && in_plus) {
lhs <- drop_strata(expr[[2]], in_plus = in_plus)
rhs <- drop_strata(expr[[3]], in_plus = in_plus)
if (rlang::is_call(lhs, "strata")) {
rhs
} else if (rlang::is_call(rhs, "strata")) {
lhs
} else {
rlang::call2("+", lhs, rhs)
}
} else if (rlang::is_call(expr)) {
expr[-1] <- purrr::map(as.list(expr[-1]),
drop_strata,
in_plus = FALSE)
expr
} else {
expr
}
}
level | line | what |
---|---|---|
0 | 3 | lhs is a |
0 | 4 | rhs is b |
drop_strata(a + b)
drop_strata <- function(expr, in_plus = TRUE) {
if (rlang::is_call(expr, "+", n = 2) && in_plus) {
lhs <- drop_strata(expr[[2]], in_plus = in_plus)
rhs <- drop_strata(expr[[3]], in_plus = in_plus)
if (rlang::is_call(lhs, "strata")) {
rhs
} else if (rlang::is_call(rhs, "strata")) {
lhs
} else {
rlang::call2("+", lhs, rhs)
}
} else if (rlang::is_call(expr)) {
expr[-1] <- purrr::map(as.list(expr[-1]),
drop_strata,
in_plus = FALSE)
expr
} else {
expr
}
}
level | line | what |
---|---|---|
0 | 3 | lhs is a |
0 | 4 | rhs is b |
0 | 10 | return a + b |
Coming soon:
Survival analysis in tidymodels
predict(survival_model,
type = "survival",
time = 2)
time
: the time points at which the survival probability is estimated
time
time
time
time
-> eval_time
.time
-> eval_time
stat_times
-> eval_time
to avoid overloading your working memory.
Origin: Martin Fowler (1999) Refactoring: Improving the Design of Existing Code
R edition: Jenny Bryan (useR 2018 keynote) Code Smells and Feels
Long method
Long parameter list
Switch statements
Alternative classes with different interfaces
Primitive obsession
Incomplete library class
Large class
Lazy class
Data class
Temporary field
Data clumps
Divergent change
Feature envy
Inappropriate intimacy
Duplicated code
Comments
Message chains
Middle man
Parallel inheritance
Refused bequest
Shotgun surgery
Speculative generality
Long method
Long parameter list
Switch statements
Alternative classes with different interfaces
Primitive obsession
Incomplete library class
Large class
Lazy class
Data class
Temporary field
Data clumps
Divergent change
Feature envy
Inappropriate intimacy
Duplicated code
Comments
Message chains
Middle man
Parallel inheritance
Refused bequest
Shotgun surgery
Speculative generality
predict._elnet <- function(object, new_data, type = NULL, penalty = NULL, ...) {
# See discussion in https://github.com/tidymodels/parsnip/issues/195
if (is.null(penalty)) penalty <- object$spec$args$penalty
object$spec$args$penalty <- .check_glmnet_penalty_predict(penalty, object)
predict.model_fit(object, new_data = new_data, type = type, ...)
}
predict._lognet <- function(object, new_data, type = NULL, penalty = NULL, ...) {
# See discussion in https://github.com/tidymodels/parsnip/issues/195
if (is.null(penalty)) penalty <- object$spec$args$penalty
object$spec$args$penalty <- .check_glmnet_penalty_predict(penalty, object)
predict.model_fit(object, new_data = new_data, type = type, ...)
}
predict._elnet <- function(object, new_data, type = NULL, penalty = NULL, ...) {
# See discussion in https://github.com/tidymodels/parsnip/issues/195
if (is.null(penalty)) penalty <- object$spec$args$penalty
object$spec$args$penalty <- .check_glmnet_penalty_predict(penalty, object)
predict.model_fit(object, new_data = new_data, type = type, ...)
}
predict._lognet <- function(object, new_data, type = NULL, penalty = NULL, ...) {
# See discussion in https://github.com/tidymodels/parsnip/issues/195
if (is.null(penalty)) penalty <- object$spec$args$penalty
object$spec$args$penalty <- .check_glmnet_penalty_predict(penalty, object)
predict.model_fit(object, new_data = new_data, type = type, ...)
}
predict_glmnet <- function(object, new_data, type = NULL, penalty = NULL, ...) {
# See discussion in https://github.com/tidymodels/parsnip/issues/195
if (is.null(penalty)) penalty <- object$spec$args$penalty
object$spec$args$penalty <- .check_glmnet_penalty_predict(penalty, object)
predict.model_fit(object, new_data = new_data, type = type, ...)
}
predict._elnet <- function(object, new_data, type = NULL, penalty = NULL, ...) {
# See discussion in https://github.com/tidymodels/parsnip/issues/195
if (is.null(penalty)) penalty <- object$spec$args$penalty
object$spec$args$penalty <- .check_glmnet_penalty_predict(penalty, object)
predict.model_fit(object, new_data = new_data, type = type, ...)
}
predict._lognet <- function(object, new_data, type = NULL, penalty = NULL, ...) {
# See discussion in https://github.com/tidymodels/parsnip/issues/195
if (is.null(penalty)) penalty <- object$spec$args$penalty
object$spec$args$penalty <- .check_glmnet_penalty_predict(penalty, object)
predict.model_fit(object, new_data = new_data, type = type, ...)
}
predict._elnet <- function(object, new_data, type = NULL, penalty = NULL, ...) {
# See discussion in https://github.com/tidymodels/parsnip/issues/195
if (is.null(penalty)) penalty <- object$spec$args$penalty
object$spec$args$penalty <- .check_glmnet_penalty_predict(penalty, object)
predict.model_fit(object, new_data = new_data, type = type, ...)
}
predict._lognet <- function(object, new_data, type = NULL, penalty = NULL, ...) {
# See discussion in https://github.com/tidymodels/parsnip/issues/195
if (is.null(penalty)) penalty <- object$spec$args$penalty
object$spec$args$penalty <- .check_glmnet_penalty_predict(penalty, object)
object$spec <- eval_args(object$spec)
predict.model_fit(object, new_data = new_data, type = type, ...)
}
Exact copy: Lost opportunity of chunking things together!
(Very) similar code: Potential to chunk the wrong things together!
Duplicated code ~ Chunking gone wrong
Principle/Pattern: Reduce argument clutter with an options object
Kara Woo
── Failure (test-my_fun.R:4:3): my_fun() works ─────────────────────────────────
`one_thing` (`actual`) not equal to `another_thing` (`expected`).
`actual`: FALSE
`expected`: TRUE
[ FAIL 1 | WARN 0 | SKIP 0 | PASS 3 ]
Test complete
"my_fun() works"
looks likelibrary(dplyr)
dat <- data.frame(a = 1:3, b = c("a", "b", "c"))
skip_if_not_installed("a_package")
test_that("my_fun() does this", {
expect_equal(my_fun(dat), ...)
})
dat2 <- data.frame(x = 1:5, y = 6:10)
skip_on_os("windows")
test_that("my_fun_2() does that", {
dat2 <- mutate(dat2, z = x + y)
expect_equal(my_fun_2(dat, dat2), ...)
})
Example adapted from Hadley Wickham’s Package Development Masterclass at posit::conf(2023)
library(dplyr)
skip_if_not_installed("a_package")
test_that("my_fun() does this", {
dat <- data.frame(a = 1:3, b = c("a", "b", "c"))
expect_equal(my_fun(dat), ...)
})
skip_on_os("windows")
test_that("my_fun_2() does that", {
dat <- data.frame(a = 1:3, b = c("a", "b", "c"))
dat2 <- data.frame(x = 1:5, y = 6:10)
dat2 <- mutate(dat2, z = x + y)
expect_equal(my_fun_2(dat, dat2), ...)
})
skip_if_not_installed("a_package")
test_that("my_fun() does this", {
dat <- data.frame(a = 1:3, b = c("a", "b", "c"))
expect_equal(my_fun(dat), ...)
})
skip_on_os("windows")
test_that("my_fun_2() does that", {
dat <- data.frame(a = 1:3, b = c("a", "b", "c"))
dat2 <- data.frame(x = 1:5, y = 6:10)
dat2 <- dplyr::mutate(dat2, z = x + y)
expect_equal(my_fun_2(dat, dat2), ...)
})
test_that("my_fun() does this", {
skip_if_not_installed("a_package")
dat <- data.frame(a = 1:3, b = c("a", "b", "c"))
expect_equal(my_fun(dat), ...)
})
test_that("my_fun_2() does that", {
skip_if_not_installed("a_package")
skip_on_os("windows")
dat <- data.frame(a = 1:3, b = c("a", "b", "c"))
dat2 <- data.frame(x = 1:5, y = 6:10)
dat2 <- dplyr::mutate(dat2, z = x + y)
expect_equal(my_fun_2(dat, dat2), ...)
})
Photo by Ken Suarez on Unsplash
The village: Maëlle, Tracy, Emil, Max, Simon, tidy team, Mine, Julie