Loops in R

Last updated on

October 25, 2024

Abstract

Looping is the repeated evaluation of a statement or block of statements.

What is a loop (in R)?

Looping is the repeated evaluation of a statement or block of statements. Base R provides functions for explicit (i.e., for, while, repeat) and implicit looping (e.g., apply, sapply, lapply,…). There are also other packages (e.g., parallel, purrr, furrr) that offer more advanced or parallelized looping capabilities, providing more efficient and convenient ways to iterate over data, particularly for complex workflows or large datasets.

lapply

We begin by using the lapply function that applies a function over a list or vector. The function needs 2 arguments as inputs:

  • X: a vector (atomic or list)
  • FUN: the function to be applied to each element of X

lapply returns a list of the same length as the input X (see ?lapply).

Two (nearly equivalent) examples with lapply:

printList1 <- lapply(X = 1:3,
                     FUN = print) 
[1] 1
[1] 2
[1] 3
printList2 <-  lapply(X = 1:3,
1                      FUN = function(x) {
2                        ret <- x^2 |>
                                print()
                        return(ret) 
                       }
                    ) 
1
Defines an so-called anonymous function that takes an argument x.
2
This approach offers further customization of operations such as calculating the square of x (for more see Functions in R).
[1] 1
[1] 4
[1] 9
Use your own function with apply family.

If the function() becomes more complex, it might be better to define it separately (and save in your src folder, see the section on Structure and Create the Project Folder(s)).

Example: Calculating many scale scores

In this example, we calculate many scale scores by using a named list and the sapply function. sapply is a user-friendly version and wrapper of lapply by default returning a vector, matrix or, if simplify = "array", an array […].

Code
calcScaleScore <- function( data,
                            items,
                            score = "sum" ) {

  validItems <- items %in% colnames(data)
  
  missingItems <- items[!validItems]
  
  if (length(missingItems) > 0) {

      stop("The following item(s) is/are not in the dataset: ",
           paste(missingItems, collapse = ", "))

    }

  if (score == "sum") {

    ret <- rowSums(data[,items])

  } else if ( score == "mean" ) {

    ret <- rowMeans(data[,items])

  } else {

    stop("score argument must be either 'sum' or 'mean'")

  }

  return(ret)

}
  • A simulated data set (wideLSdat) that can be found in the Example Data section.
  • a named list (wideLSVar).
Code
wideLSVar <- list("Y1" = paste0("Y", 1:3, 1),
                  "Y2" = paste0("Y", 1:3, 2),
                  "Y3" = paste0("Y", 1:3, 3))
wideLSVar
$Y1
[1] "Y11" "Y21" "Y31"

$Y2
[1] "Y12" "Y22" "Y32"

$Y3
[1] "Y13" "Y23" "Y33"
wideLSdat[,names(wideLSVar)] <- sapply(
                X = wideLSVar,
                FUN = function(x) {
                                        
                    ret <- calcScaleScore(data = wideLSdat,
                                          items = x)
                                        
                    return(ret)
                },simplify = F)

head(wideLSdat[,names(wideLSVar)])
         Y1         Y2         Y3
1 -2.680416  2.2974542  3.8943707
2  2.944144  3.4948176  3.1198335
3 -1.403956  1.5638600  2.1539978
4 -2.010152 -2.9952976 -3.7823288
5 -4.117428 -3.3980176  0.6669694
6 -2.701068  0.9692462  3.7553657

When not to loop?

Answer: If vectorization is possible…

Example: Create a large vector (10 million random values) and multiply it by 2.

set.seed(999)
x <- rnorm(1e7)  

Loop approach

system.time({

  y <- numeric(length(x))

  for (i in 1:length(x)) {
    y[i] <- x[i] * 2
  }

})
   user  system elapsed 
   0.72    0.02    0.74 

Vectorizized approach

system.time({

  y <- x * 2

})
   user  system elapsed 
   0.00    0.01    0.01 

But, How to know that vectorization is possible?

→ Especially, in coding situations where you need to perform the same operation on each element1 (e.g., vector, matrix):

  • Arithmetic operations (e.g, +, -, …)
  • Logical comparisons (e.g., ==, >, …)
  • Mathematical functions (e.g., sqrt(), log(), …)

Create Sections in Quarto Programmatically

In this section, we will generate headings by iterating through a named list. The elements of the list will be hyperlinks to Wikipedia, but they can also be other types of content, such as tables or figures.

Named list

someInfo <- list(
    "Apples" = "<https://en.wikipedia.org/wiki/Apple>",
    "Bananas" = "<https://en.wikipedia.org/wiki/Banana>",
    "Oranges" = "<https://en.wikipedia.org/wiki/Orange_(fruit)>"
    )

for loop

To generate the sections (recall, headings are created by using #, ##, etc.) for all elements, we use a for loop. All content is wrapped in the cat() function that concatenates and prints the input to plain text. To tell Quarto to treat the content as raw markdown, we set the output option to asis. For a more detailed description of the code, see the code-annotations below the code.

Be generous with "\n" as it creates a new line.

Code
```{r}
#| label: create-section
#| code-fold: show
1#| output: asis
#| code-annotations: select
2for (i in seq_along(someInfo) ) {
    
    if (i == 1) {
        cat("\n### Programmatically generated headings\n")
        }
    
3    cat("\n#### ", names(someInfo)[i], "\n")
    
    cat("In this section, we provide some information about",
        paste0(tolower(names(someInfo)[i]), ".\n"))
    
4    cat("\n::: {.column-margin}\n", "Link to wikipedia: ",
        someInfo[[i]],
        "\n:::\n")
    
5    cat("\n{{< lipsum 1 >}}\n")
}
```
1
Set output chunk option to asis.
2
Open for loop, which iterates through the someInfo list.
3
State #### before the names(someInfo) object within the cat() function to create sections.
4
(Optional): Positioning content in the margin of the document: Use a div with the .column-margin class.
5
(Optional): Adding placeholder text: {{< lipsum >}}

Programmatically generated headings

Apples

In this section, we provide some information about apples.

Etiam congue quam eget velit convallis, eu sagittis orci vestibulum. Vestibulum at massa turpis. Curabitur ornare ex sed purus vulputate, vitae porta augue rhoncus. Phasellus auctor suscipit purus, vel ultricies nunc. Nunc eleifend nulla ac purus volutpat, id fringilla felis aliquet. Duis vitae porttitor nibh, in rhoncus risus. Vestibulum a est vitae est tristique vehicula. Proin mollis justo id est tempus hendrerit. Praesent suscipit placerat congue. Aliquam eu elit gravida, consequat augue non, ultricies sapien. Nunc ultricies viverra ante, sit amet vehicula ante volutpat id. Etiam tempus purus vitae tellus mollis viverra. Donec at ornare mauris. Aliquam sodales hendrerit ornare. Suspendisse accumsan lacinia sapien, sit amet imperdiet dui molestie ut.

Bananas

In this section, we provide some information about bananas.

Etiam non efficitur urna, quis elementum nisi. Mauris posuere a augue vel gravida. Praesent luctus erat et ex iaculis interdum. Nulla vestibulum quam ac nunc consequat vulputate. Nullam iaculis lobortis sem sit amet fringilla. Aliquam semper, metus ut blandit semper, nulla velit fermentum sapien, fermentum ultrices dolor sapien sed leo. Vestibulum molestie faucibus magna, at feugiat nulla ullamcorper a. Aliquam erat volutpat. Praesent scelerisque magna a justo maximus, sit amet suscipit mauris tempor. Nulla nec dolor eget ipsum pellentesque lobortis a in ipsum. Morbi turpis turpis, fringilla a eleifend maximus, viverra nec neque. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.

Oranges

In this section, we provide some information about oranges.

Praesent ornare dolor turpis, sed tincidunt nisl pretium eget. Curabitur sed iaculis ex, vitae tristique sapien. Quisque nec ex dolor. Quisque ut nisl a libero egestas molestie. Nulla vel porta nulla. Phasellus id pretium arcu. Etiam sed mi pellentesque nibh scelerisque elementum sed at urna. Ut congue molestie nibh, sit amet pretium ligula consectetur eu. Integer consectetur augue justo, at placerat erat posuere at. Ut elementum urna lectus, vitae bibendum neque pulvinar quis. Suspendisse vulputate cursus eros id maximus. Duis pulvinar facilisis massa, et condimentum est viverra congue. Curabitur ornare convallis nisl. Morbi dictum scelerisque turpis quis pellentesque. Etiam lectus risus, luctus lobortis risus ut, rutrum vulputate justo. Nulla facilisi.

Footnotes

  1. so-called element-wise operations↩︎