I need your help!

If you find any typos, errors, or places where the text may be improved, please let me know. The all-time ways to provide feedback are by GitHub or hypothes.is annotations.

Opening an issue or submitting a pull request on GitHub

Hypothesis Adding an annotation using hypothes.is. To add an annotation, select some text and and so click the on the popular-up carte. To see the annotations of others, click the in the upper correct-hand corner of the page.

Iteration

Introduction

The microbenchmark package is used for timing code.

The map() function appears in both the purrr and maps packages. See the "Prerequisites" section of the Introduction. If you lot see errors similar the following, y'all are using the wrong map() function.

                > map(c(TRUE, Fake, TRUE), ~ !.) Error: $ operator is invalid for atomic vectors > map(-2:two, rnorm, north = 5) Error in map(-2:two, rnorm, n = 5) :  argument three matches multiple formal arguments              

You can bank check the packet in which a role is defined using the environs() function:

The result should include namespace:purrr if map() is coming from the purrr package. To explicitly reference the package to get a role from, apply the colon operator ::. For example,

For loops

Exercise 21.2.1

Write for-loops to:

  1. Compute the hateful of every column in mtcars.
  2. Make up one's mind the type of each column in nycflights13::flights.
  3. Compute the number of unique values in each column of iris.
  4. Generate 10 random normals for each of \(\mu\) = -10, 0, 10, and 100.

The answers for each part are beneath.

  1. To compute the mean of every column in mtcars.

  2. Determine the blazon of each column in nycflights13::flights.

    I used a list, not a character vector, since the class of an object can have multiple values. For example, the class of the time_hour column is POSIXct, POSIXt.

  3. To compute the number of unique values in each column of the iris dataset.

  4. To generate 10 random normals for each of \(\mu\) = -10, 0, 10, and 100.

    Even so, we don't need a for loop for this since rnorm() recycle the hateful argument.

                                                      matrix(rnorm(due north                          *                                                                              length(mu),                          mean =                          mu),                          ncol =                          n)                          #>        [,1]   [,2]  [,iii]     [,4]   [,five]   [,half dozen]    [,7]   [,8]    [,9] [,10]                          #> [1,] -ix.930  -ix.56 -nine.88 -ten.2061 -12.27 -8.926 -eleven.178  -9.51  -eight.663 -ix.39                          #> [2,] -0.639   2.76 -1.91   0.0192   2.68 -0.665  -0.976  -1.70   0.237 -0.11                          #> [3,]  9.950  10.05 10.86  10.0296   ix.64 11.114  11.065   8.53  eleven.318 x.17                          #> [four,] 99.749 100.58 99.76 100.5498 100.21 99.754 100.132 100.28 100.524 99.91                                              

Exercise 21.2.two

Eliminate the for loop in each of the following examples past taking advantage of an existing function that works with vectors:

Since str_c() already works with vectors, use str_c() with the collapse argument to return a unmarried string.

For this I'm going to rename the variable sd to something different because sd is the name of the function we want to use.

We could simply employ the sd role.

Or if there was a need to utilise the equation (e.g. for pedagogical reasons), and then the functions mean() and sum() already work with vectors:

The code higher up is calculating a cumulative sum. Utilise the function cumsum()

Exercise 21.2.three

Combine your function writing and for loop skills:

  1. Write a for loop that prints() the lyrics to the children's song "Alice the camel".

  2. Convert the nursery rhyme "ten in the bed" to a part. Generalize it to whatsoever number of people in any sleeping structure.

  3. Convert the vocal "99 bottles of beer on the wall" to a function. Generalize to any number of any vessel containing any liquid on surface.

The answers to each part follow.

  1. The lyrics for Alice the Camel are:

    Alice the camel has five humps.
    Alice the camel has five humps.
    Alice the camel has v humps.
    Then go, Alice, go.

    This poetry is repeated, each time with one fewer hump, until there are no humps. The last poetry, with no humps, is:

    Alice the camel has no humps.
    Alice the camel has no humps.
    Alice the camel has no humps.
    At present Alice is a horse.

    Nosotros'll iterate from five to no humps, and print out a dissimilar last line if there are no humps.

                            humps <-                                                    c("five",                          "four",                          "three",                          "two",                          "one",                          "no")                          for                          (i                          in                          humps) {                           cat(str_c("Alice the camel has ",                          rep(i,                          3),                          " humps.",                           collapse =                          "                          \north                          "                            ),                          "                          \due north                          ")                           if                          (i                          ==                                                      "no") {                           true cat("Now Alice is a horse.                          \northward                          ")   }                          else                          {                           cat("And then become, Alice, get.                          \n                          ")   }                           cat("                          \n                          ") }                          #> Alice the camel has v humps.                          #> Alice the camel has five humps.                          #> Alice the camel has 5 humps.                                                    #> So go, Alice, go.                          #>                                                    #> Alice the camel has four humps.                          #> Alice the camel has iv humps.                          #> Alice the camel has four humps.                                                    #> So go, Alice, become.                          #>                                                    #> Alice the camel has three humps.                          #> Alice the camel has three humps.                          #> Alice the camel has three humps.                                                    #> Then go, Alice, go.                          #>                                                    #> Alice the camel has two humps.                          #> Alice the camel has ii humps.                          #> Alice the camel has two humps.                                                    #> Then get, Alice, become.                          #>                                                    #> Alice the camel has i humps.                          #> Alice the camel has one humps.                          #> Alice the camel has i humps.                                                    #> So go, Alice, go.                          #>                                                    #> Alice the camel has no humps.                          #> Alice the camel has no humps.                          #> Alice the camel has no humps.                                                    #> Now Alice is a horse.                                              
  2. The lyrics for X in the Bed are:

    Here we go!
    There were ten in the bed
    and the picayune one said,
    "Roll over, roll over."
    So they all rolled over and i barbarous out.

    This poetry is repeated, each fourth dimension with one fewer in the bed, until there is ane left. That last verse is:

    1! There was one in the bed
    and the little one said,
    "I'm lonely…"

                            numbers <-                                                    c(                           "x",                          "nine",                          "8",                          "seven",                          "six",                          "five",                           "four",                          "iii",                          "two",                          "one"                          )                          for                          (i                          in                          numbers) {                           cat(str_c("There were ", i,                          " in the bed                          \north                          "))                           cat("and the little 1 said                          \northward                          ")                           if                          (i                          ==                                                      "one") {                           cat("I'm lonely...")   }                          else                          {                           true cat("Coil over, roll over                          \n                          ")                           cat("So they all rolled over and ane savage out.                          \n                          ")   }                           cat("                          \n                          ") }                          #> There were ten in the bed                          #> and the little ane said                          #> Gyre over, roll over                          #> Then they all rolled over and one fell out.                          #>                                                    #> There were ix in the bed                          #> and the little 1 said                          #> Roll over, scroll over                          #> So they all rolled over and ane fell out.                          #>                                                    #> There were eight in the bed                          #> and the little one said                          #> Roll over, roll over                          #> So they all rolled over and one fell out.                          #>                                                    #> There were seven in the bed                          #> and the little i said                          #> Curlicue over, roll over                          #> So they all rolled over and one fell out.                          #>                                                    #> There were six in the bed                          #> and the little ane said                          #> Roll over, gyre over                          #> So they all rolled over and one vicious out.                          #>                                                    #> There were five in the bed                          #> and the little 1 said                          #> Roll over, whorl over                          #> So they all rolled over and one fell out.                          #>                                                    #> In that location were four in the bed                          #> and the little ane said                          #> Roll over, gyre over                          #> And so they all rolled over and one savage out.                          #>                                                    #> There were three in the bed                          #> and the petty i said                          #> Roll over, gyre over                          #> So they all rolled over and one barbarous out.                          #>                                                    #> At that place were two in the bed                          #> and the little 1 said                          #> Whorl over, scroll over                          #> So they all rolled over and one fell out.                          #>                                                    #> There were i in the bed                          #> and the little one said                          #> I'm lone...                                              
  3. The lyrics of Ninety-9 Bottles of Beer on the Wall are

    99 bottles of beer on the wall, 99 bottles of beer.
    Take one down, pass information technology around, 98 bottles of beer on the wall

    This verse is repeated, each time with one few canteen, until there are no more bottles of beer. The last poetry is

    No more bottles of beer on the wall, no more bottles of beer.
    We've taken them down and passed them around; now nosotros're drunk and passed out!

    For the bottles of beer, I define a helper function to correctly print the number of bottles.

                            bottles <-                                                    function(due north) {                           if                          (north                          >                                                                              i) {                           str_c(due north,                          " bottles")   }                          else                          if                          (n                          ==                                                                              1) {                           "1 bottle"                            }                          else                          {                           "no more bottles"                            } }  beer_bottles <-                                                    role(total_bottles) {                           # print each lyric                                                    for                          (current_bottles                          in                          seq(total_bottles,                          0)) {                           # commencement line                                                    cat(str_to_sentence(str_c(bottles(current_bottles),                          " of beer on the wall, ",                          bottles(current_bottles),                          " of beer.                          \n                          ")))                              # second line                                                    if                          (current_bottles                          >                                                                              0) {                           cat(str_c(                           "Have 1 down and laissez passer it around, ",                          bottles(current_bottles                          -                                                                              1),                           " of beer on the wall.                          \n                          "                                ))               }                          else                          {                           cat(str_c("Go to the store and buy some more than, ",                          bottles(total_bottles),                          " of beer on the wall.                          \n                          "))                }                           cat("                          \north                          ")   } }                          beer_bottles(iii)                          #> 3 Bottles of beer on the wall, 3 bottles of beer.                          #> Take 1 downwardly and pass information technology effectually, 2 bottles of beer on the wall.                          #>                                                    #> 2 Bottles of beer on the wall, two bottles of beer.                          #> Have 1 down and laissez passer it around, 1 canteen of beer on the wall.                          #>                                                    #> 1 Bottle of beer on the wall, one bottle of beer.                          #> Take one down and pass it effectually, no more bottles of beer on the wall.                          #>                                                    #> No more bottles of beer on the wall, no more than bottles of beer.                          #> Go to the store and buy some more, 3 bottles of beer on the wall.                                              

Practice 21.two.iv

It's common to see for loops that don't preallocate the output and instead increase the length of a vector at each footstep:

How does this touch on functioning? Design and execute an experiment.

In order to compare these two approaches, I'll define ii functions: add_to_vector volition append to a vector, like the case in the question, and add_to_vector_2 which pre-allocates a vector.

I'll use the package microbenchmark to run these functions several times and compare the time information technology takes. The package microbenchmark contains utilities for benchmarking R expressions. In detail, the microbenchmark() role will run an R expression a number of times and time information technology.

In this example, appending to a vector takes 325 times longer than pre-allocating the vector. Yous may get dissimilar answers, only the longer the vector and the larger the objects, the more that pre-allocation volition outperform appending.

For loop variations

Exercise 21.3.ane

Imagine you have a directory full of CSV files that you want to read in. You have their paths in a vector, files <- dir("data/", pattern = "\\.csv$", full.names = True), and now desire to read each one with read_csv(). Write the for loop that will load them into a single data frame.

Since, the number of files is known, pre-allocate a list with a length equal to the number of files.

So, read each file into a data frame, and assign it to an element in that listing. The result is a list of information frames.

Finally, utilise use bind_rows() to combine the list of data frames into a single data frame.

Alternatively, I could take pre-allocated a list with the names of the files.

Exercise 21.iii.2

What happens if you use for (nm in names(x)) and x has no names? What if only some of the elements are named? What if the names are not unique?

Let'south try it out and see what happens. When in that location are no names for the vector, information technology does non run the code in the loop. In other words, it runs zero iterations of the loop.

Notation that the length of Cipher is zero:

If there just some names, and so we get an error for trying to access an element without a proper noun.

Finally, if the vector contains duplicate names, then x[[nm]] returns the showtime element with that name.

Exercise 21.3.3

Write a function that prints the hateful of each numeric column in a information frame, along with its name. For instance, show_mean(iris) would print:

Extra claiming: what part did I use to make sure that the numbers lined up nicely, even though the variable names had unlike lengths?

At that place may be other functions to do this, but I'll use str_pad(), and str_length() to ensure that the space given to the variable names is the same. I messed around with the options to format() until I got two digits.

Exercise 21.3.4

What does this code do? How does it work?

This code mutates the disp and am columns:

  • disp is multiplied by 0.0163871
  • am is replaced by a factor variable.

The code works by looping over a named list of functions. Information technology calls the named part in the list on the column of mtcars with the same proper name, and replaces the values of that column.

This is a function.

This applies the function to the cavalcade of mtcars with the same name

For loops vs. functionals

Do 21.4.ane

Read the documentation for apply(). In the 2nd case, what two for-loops does it generalize.

For an object with ii-dimensions, such as a matrix or information frame, use() replaces looping over the rows or columns of a matrix or data-frame. The apply() function is used like apply(X, MARGIN, FUN, ...), where X is a matrix or array, FUN is a part to employ, and ... are additional arguments passed to FUN.

When MARGIN = 1, then the function is applied to each row. For example, the following instance calculates the row ways of a matrix.

That is equivalent to this for-loop.

When MARGIN = ii, apply() is equivalent to a for-loop looping over columns.

Exercise 21.4.two

Adapt col_summary() and then that it only applies to numeric columns. You lot might want to start with an is_numeric() function that returns a logical vector that has a Truthful corresponding to each numeric column.

The original col_summary() function is

The adapted version adds actress logic to just apply the office to numeric columns.

Allow's examination that col_summary2() works by creating a small data frame with some numeric and non-numeric columns.

                    df <-                                            tibble(                       X1 =                      c(i,                      2,                      3),                       X2 =                      c("A",                      "B",                      "C"),                       X3 =                      c(0,                      -1,                      five),                       X4 =                      c(TRUE,                      FALSE,                      TRUE) )                      col_summary2(df, mean)                      #>   X1   X3                                            #> 2.00 i.33                                      

As expected, it only calculates the mean of the numeric columns, X1 and X3. Permit's test that information technology works with another function.

The map functions

Exercise 21.five.1

Write lawmaking that uses one of the map functions to:

  1. Compute the mean of every column in mtcars.
  2. Decide the type of each column in nycflights13::flights.
  3. Compute the number of unique values in each column of iris.
  4. Generate 10 random normals for each of \(\mu = -10\), \(0\), \(10\), and \(100\).
  1. To calculate the hateful of every cavalcade in mtcars, utilize the role mean() to each column, and use map_dbl, since the results are numeric.

  2. To calculate the type of every cavalcade in nycflights13::flights utilize the function typeof(), discussed in the department on Vector basics, and use map_chr(), since the results are grapheme.

  3. The function n_distinct() calculates the number of unique values in a vector.

    The map_int() office is used since length() returns an integer. Notwithstanding, the map_dbl() function will too piece of work.

    An alternative to the n_distinct() function is the expression, length(unique(...)). The n_distinct() function is more curtailed and faster, simply length(unique(...)) provides an example of using anonymous functions with map functions. An anonymous part tin be written using the standard R syntax for a function:

    Additionally, map functions accept 1-sided formulas every bit a more concise culling to specify an anonymous role:

    In this case, the anonymous function accepts one argument, which is referenced past .10 in the expression length(unique(.x)).

  4. To generate ten random normals for each of \(\mu = -10\), \(0\), \(x\), and \(100\): The result is a listing of numeric vectors.

    Since a single call of rnorm() returns a numeric vector with a length greater than one we cannot apply map_dbl, which requires the function to render a numeric vector that is but length one (see Exercise 21.5.4). The map functions pass any additional arguments to the office existence called.

Exercise 21.5.ii

How tin you lot create a single vector that for each cavalcade in a data frame indicates whether or not it's a factor?

The function is.factor() indicates whether a vector is a cistron.

Checking all columns in a data frame is a job for a map_*() function. Since the result of is.cistron() is logical, nosotros will use map_lgl() to apply is.factor() to the columns of the data frame.

Do 21.5.3

What happens when you utilize the map functions on vectors that aren't lists? What does map(1:5, runif) do? Why?

Map functions work with any vectors, non only lists. Equally with lists, the map functions will use the function to each element of the vector. In the following examples, the inputs to map() are diminutive vectors (logical, character, integer, double).

                                          map(c(TRUE,                      FALSE,                      TRUE),                      ~                                                                  !.)                      #> [[1]]                      #> [one] Simulated                      #>                                            #> [[two]]                      #> [ane] TRUE                      #>                                            #> [[3]]                      #> [1] Simulated                      map(c("Hello",                      "World"), str_to_upper)                      #> [[1]]                      #> [1] "Hello"                      #>                                            #> [[2]]                      #> [1] "World"                      map(1                      :                      v,                      ~                                                                  rnorm(.))                      #> [[one]]                      #> [ane] 1.42                      #>                                            #> [[ii]]                      #> [1] -0.384 -0.174                      #>                                            #> [[3]]                      #> [1] -0.222 -1.010  0.481                      #>                                            #> [[4]]                      #> [1]  one.604 -1.515 -one.416  0.877                      #>                                            #> [[5]]                      #> [1]  0.624  two.112 -0.356 -1.064  i.077                      map(c(-                      0.5,                      0,                      1),                      ~                                                                  rnorm(1,                      hateful =                      .))                      #> [[ane]]                      #> [ane] 0.682                      #>                                            #> [[two]]                      #> [1] 0.198                      #>                                            #> [[3]]                      #> [1] 0.6                                      

It is important to exist aware that while the input of map() can be whatever vector, the output is always a list.

This expression is equivalent to running the following.

The map() function loops through the numbers 1 to 5. For each value, it calls the runif() with that number every bit the start argument, which is the number of sample to draw. The upshot is a length five list with numeric vectors of sizes i through five, each with random samples from a compatible distribution. Notation that although input to map() was an integer vector, the render value was a list.

Exercise 21.5.4

What does map(-2:2, rnorm, n = five) do? Why?

What does map_dbl(-2:two, rnorm, n = 5) practice? Why?

Consider the first expression.

This expression takes samples of size five from five normal distributions, with means of (-2, -1, 0, i, and two), merely the aforementioned standard difference (i). It returns a list with each chemical element a numeric vectors of length 5.

However, if instead, we apply map_dbl(), the expression raises an fault.

This is because the map_dbl() function requires the part information technology applies to each element to return a numeric vector of length one. If the office returns either a non-numeric vector or a numeric vector with a length greater than one, map_dbl() will raise an error. The reason for this strictness is that map_dbl() guarantees that information technology will render a numeric vector of the aforementioned length equally its input vector.

This concept applies to the other map_*() functions. The function map_chr() requires that the function always render a grapheme vector of length one; map_int() requires that the function always render an integer vector of length one; map_lgl() requires that the function ever return an logical vector of length one. Apply the map() function if the function will render values of varying types or lengths.

To return a numeric vector, use flatten_dbl() to coerce the listing returned by map() to a numeric vector.

Do 21.5.5

Rewrite map(x, function(df) lm(mpg ~ wt, data = df)) to eliminate the anonymous role.

This code in this question does not run, so I will use the following lawmaking.

We can eliminate the use of an anonymous role using the ~ shortcut.

Though not the intent of this question, the other style to eliminate anonymous function is to create a named one.

Dealing with failure

Mapping over multiple arguments

Walk

Other patterns of for loops

Exercise 21.ix.1

Implement your own version of every() using a for loop. Compare it with purrr::every(). What does purrr's version practice that your version doesn't?

The part purrr::every() does fancy things with the predicate function argument .p, like taking a logical vector instead of a function, or being able to test part of a cord if the elements of .x are lists.

Practise 21.9.two

Create an enhanced col_summary() that applies a summary function to every numeric column in a data frame.

I will use map to apply the function to all the columns, and keep to but select numeric columns.

Exercise 21.9.3

A possible base R equivalent of col_summary() is:

But it has a number of bugs as illustrated with the post-obit inputs:

What causes these bugs?

The cause of these bugs is the beliefs of sapply(). The sapply() function does not guarantee the type of vector it returns, and volition returns dissimilar types of vectors depending on its inputs. If no columns are selected, instead of returning an empty numeric vector, information technology returns an empty list. This causes an error since nosotros can't use a list with [.

The sapply() function tries to exist helpful by simplifying the results, just this beliefs tin can be counterproductive. It is okay to use the sapply() part interactively, only avert programming with it.