<img src="cover.png" style="width:50%;display: block;margin-left: auto;margin-right: auto;opacity:0.1;"></img> <div style="position: absolute;top: 40%;left: 50%;transform: translate(-50%, -50%);"> <h1 style="margin-bottom:0;font-size:110px !important;"><a class="title" href="http://www.burns-stat.com/pages/Tutor/R_inferno.pdf">The R Inferno<a></h1> <div style="text-align:center;">Content bySlides by Maya Gans</div> </div> --- <div style="position: absolute;top: 40%;left: 50%;transform: translate(-50%, -50%);text-align:center;"> After a journey, we arrived at an archway. Inscribed on it: “Through me the way into the suffering city, through me the way among the lost.” Through the archway we went. </div> <div style="position: absolute;top: 60%;left: 68%;transform: translate(-50%, -50%);text-align:right;color:#F00000;"> -Dante Alighieri </div> --- .container[ .slide-one[ ] .right-side[ # Floating Point Trap ```r .1 == .3 / 3 ``` ``` ## [1] FALSE ``` ] ] --- .container[ .slide-two[ ] <div style="font-size:12px !important;"> .right-side[ <h1 style="margin-bottom:0;">Growing Objects</h1> ```r grow <- function(n) { vec <- numeric(0) for(i in 1:n) vec <- c(vec, i) return(vec) } subscript <- function(n) { vec <- numeric(n) for(i in 1:n) vec[i] <- i return(vec) } colon_op <- function(n) { vec <- 1:n return(vec) } n <- 100 test(grow(n), subscript(n), colon_op(n)) ``` <table class="table" style="margin-left: auto; margin-right: auto;"> <thead> <tr> <th style="text-align:left;"> expression </th> <th style="text-align:right;"> median </th> <th style="text-align:right;"> relative_time </th> </tr> </thead> <tbody> <tr> <td style="text-align:left;"> colon_op(n) </td> <td style="text-align:right;"> 523ns </td> <td style="text-align:right;"> 1.0 </td> </tr> <tr> <td style="text-align:left;"> subscript(n) </td> <td style="text-align:right;"> 9.42µs </td> <td style="text-align:right;"> 18.0 </td> </tr> <tr> <td style="text-align:left;"> grow(n) </td> <td style="text-align:right;"> 53.57µs </td> <td style="text-align:right;"> 102.4 </td> </tr> </tbody> </table> </div> ] ] --- .container[ .slide-three[ ] <div style="font-size:12px !important;"> .right-side[ # Failing to Vectorize ```r non_vectorized <- function(x) { lsum <- 0 for(i in 1:length(x)) { lsum <- lsum + log(x[i]) } return(lsum) } vectorized <- function(x) { lsum <- sum(log(x)) return(lsum) } x <- 1:10000 test(non_vectorized(x), vectorized(x)) ``` <table class="table" style="margin-left: auto; margin-right: auto;"> <thead> <tr> <th style="text-align:left;"> expression </th> <th style="text-align:right;"> median </th> <th style="text-align:right;"> relative_time </th> </tr> </thead> <tbody> <tr> <td style="text-align:left;"> vectorized(x) </td> <td style="text-align:right;"> 153µs </td> <td style="text-align:right;"> 1.0 </td> </tr> <tr> <td style="text-align:left;"> non_vectorized(x) </td> <td style="text-align:right;"> 658µs </td> <td style="text-align:right;"> 4.3 </td> </tr> </tbody> </table> </div> ] ] --- .container[ .slide-four[ ] <div style="font-size:3px !important;"> .right-side[ # Over Vectorizing ```r > apply function (X, MARGIN, FUN, ...) { FUN <- match.fun(FUN) dl <- length(dim(X)) if (!dl) stop("dim(X) must have a positive length") if (is.object(X)) X <- if (dl == 2L) as.matrix(X) else as.array(X) d <- dim(X) dn <- dimnames(X) ds <- seq_len(dl) if (is.character(MARGIN)) { if (is.null(dnn <- names(dn))) stop("'X' must have named dimnames") MARGIN <- match(MARGIN, dnn) if (anyNA(MARGIN)) stop("not all elements of 'MARGIN' are names of dimensions") } d.call <- d[-MARGIN] d.ans <- d[MARGIN] if (anyNA(d.call) || anyNA(d.ans)) stop("'MARGIN' does not match dim(X)") s.call <- ds[-MARGIN] s.ans <- ds[MARGIN] dn.call <- dn[-MARGIN] dn.ans <- dn[MARGIN] d2 <- prod(d.ans) if (d2 == 0L) { newX <- array(vector(typeof(X), 1L), dim = c(prod(d.call), 1L)) ans <- forceAndCall(1, FUN, if (length(d.call) < 2L) newX[, 1] else array(newX[, 1L], d.call, dn.call), ...) return(if (is.null(ans)) ans else if (length(d.ans) < 2L) ans[1L][-1L] else array(ans, d.ans, dn.ans)) } newX <- aperm(X, c(s.call, s.ans)) dim(newX) <- c(prod(d.call), d2) ans <- vector("list", d2) if (length(d.call) < 2L) { if (length(dn.call)) dimnames(newX) <- c(dn.call, list(NULL)) * for (i in 1L:d2) { tmp <- forceAndCall(1, FUN, newX[, i], ...) if (!is.null(tmp)) ans[[i]] <- tmp } } * else for (i in 1L:d2) { tmp <- forceAndCall(1, FUN, array(newX[, i], d.call, dn.call), ...) if (!is.null(tmp)) ans[[i]] <- tmp } ans.list <- is.recursive(ans[[1L]]) l.ans <- length(ans[[1L]]) ans.names <- names(ans[[1L]]) if (!ans.list) ans.list <- any(lengths(ans) != l.ans) if (!ans.list && length(ans.names)) { all.same <- vapply(ans, function(x) identical(names(x), ans.names), NA) if (!all(all.same)) ans.names <- NULL } len.a <- if (ans.list) d2 else length(ans <- unlist(ans, recursive = FALSE)) if (length(MARGIN) == 1L && len.a == d2) { names(ans) <- if (length(dn.ans[[1L]])) dn.ans[[1L]] ans } else if (len.a == d2) array(ans, d.ans, dn.ans) else if (len.a && len.a%%d2 == 0L) { if (is.null(dn.ans)) dn.ans <- vector(mode = "list", length(d.ans)) dn1 <- list(ans.names) if (length(dn.call) && !is.null(n1 <- names(dn <- dn.call[1])) && nzchar(n1) && length(ans.names) == length(dn[[1]])) names(dn1) <- n1 dn.ans <- c(dn1, dn.ans) array(ans, c(len.a%/%d2, d.ans), if (!is.null(names(dn.ans)) || !all(vapply(dn.ans, is.null, NA))) dn.ans) } else ans } <bytecode: 0x7f963cc1f208> <environment: namespace:base> ``` ] </div> .loop[ The `apply` function has a `for` loop in its definition. The `lapply` function buries the loop, but execution times tend to be roughly equal to an explicit `for` loop. ] ] --- .container[ .slide-five[ ] .right-side[ # Not Writing Functions * Abstraction * Simplicity * Consistency ] ] --- .container[ .slide-six[ ] .right-side[ # Doing Global Assignments If you think you need `<<-`, think again. If on reflection you still think you need `<<-`, think again. ] ] --- .container[ .slide-seven[ ] .right-side[ ## Tripping on Object Orientation * S3 * S4 * R6 * Namespaces ] ] --- .container[ .slide-eight[ ] .right-side[ .small-list[ # Believing it Does as Intended * debugging with `browser` * `?Syntax` to check operator precedence * `x == NA` * testing `NULL` * `== != %in%` * logical > integer > numeric > complex > character * excluding named lists and missing values * `1 + NULL == numeric(0)` * `sum(numeric(0)) == 0` * partial matching * `print` gives you indexes, `cat` just contents * Quotes, backslashes and backquotes * Subscripting strips attiributes * Name masking * `sort.list` is not for lists! * `source` vs `attach` vs `load` ] ] ] --- .container[ .slide-nine[ ] .right-side[ # Unhelpfully Seeking Help * Read the documentation * Update Package * Select the best listserv * Use a descriptive subject line * Clearly state question * Write a reproducible example * W A I T ] ] <style> .remark-slide-content { padding: 0; } .container { width: 100%; height: 100%; } .slide-one { background-image: url("circle1.jpg"); height: 100%; width: 40%; background-position: center; background-size: cover; float: left; } .slide-two { background-image: url("circle2.jpg"); height: 100%; width: 40%; background-position: center; background-size: cover; float: left; } .slide-three { background-image: url("circle3.jpg"); height: 100%; width: 40%; background-position: center; background-size: cover; float: left; } .slide-four { background-image: url("circle4.jpg"); height: 100%; width: 40%; background-position: center; background-size: cover; float: left; } .slide-five { background-image: url("circle5.jpg"); height: 100%; width: 40%; background-position: center; background-size: cover; float: left; } .slide-six { background-image: url("circle6.jpg"); height: 100%; width: 40%; background-position: center; background-size: cover; float: left; } .slide-seven { background-image: url("circle7.jpg"); height: 100%; width: 40%; background-position: center; background-size: cover; float: left; } .slide-eight { background-image: url("circle8.jpg"); height: 100%; width: 40%; background-position: center; background-size: cover; float: left; } .slide-nine { background-image: url("circle9.jpg"); height: 100%; width: 40%; background-position: center; background-size: cover; float: left; } .right-side { width: 60%; float: right; text-indent: 10%; list-style-position: inside; } .title { text-decoration:none; color:#F00000; text-align: center; padding-bottom: 0; } a.title:hover { color: black; } .hljs-github .hljs { color: #F00000; } .hljs-github .hljs-number, .hljs-github .hljs-literal, .hljs-github .hljs-variable, .hljs-github .hljs-template-variable, .hljs-github .hljs-tag .hljs-attr { color: black; } .loop { position: absolute; top: 57%; left: 80%; text-align: center; font-size: 30px; transform: translate(-50%, -50%); } li { line-height: 2em; } .small-list { font-size: 15px !important; } h1#believing-it-does-as-intended { margin-bottom: 0; } table.table { width: 60%; } th { background-color: white; color: #F00000; } td { background-color: white; } </style>