---
title: Numbers
author: Srihith Garlapati
tutorial:
  id: numbers
output:
  learnr::tutorial:
    progressive: yes
    allow_skip: yes
runtime: shiny_prerendered
description: 'Tutorial for Chapter 13: Numbers'
---

```{r setup, include = FALSE}
library(learnr)
library(tutorial.helpers)
library(tidyverse)
library(nycflights13)

knitr::opts_chunk$set(echo = FALSE)
options(tutorial.exercise.timelimit = 600, 
        tutorial.storage = "local") 

df <- tribble(
  ~x, ~y,
  1,  3,
  5,  2,
  7, NA,
)

x <- c(1, 2, 3, 4, NA)
ranktypes <- tibble(x = x)

numbers <- tibble(id = 1:10)

times_visited <- tibble(
  time = c(0, 1, 2, 3, 5, 10, 12, 15, 17, 19, 20, 27, 28, 30)
)

repetition <- tibble(
  x = c("a", "a", "a", "b", "c", "c", "d", "e", "a", "a", "b", "b"),
  y = c(1, 2, 3, 2, 4, 1, 3, 9, 4, 8, 10, 199)
)
```

```{r copy-code-chunk, child = system.file("child_documents/copy_button.Rmd", package = "tutorial.helpers")}
```

```{r info-section, child = system.file("child_documents/info_section.Rmd", package = "tutorial.helpers")}
```

<!-- Luit: -->

<!-- Edit Knowledge drops as needed. -->
<!-- Go on to tutorials 14 and 15.    -->



## Introduction
### 

This tutorial covers [Chapter 13: Numbers](https://r4ds.hadley.nz/numbers.html) from [*R for Data Science (2e)*](https://r4ds.hadley.nz/) by Hadley Wickham, Mine Çetinkaya-Rundel, and Garrett Grolemund. We will be utilizing two core packages of Tidyverse, [**readr**](https://readr.tidyverse.org/) and [**dplyr**](https://dplyr.tidyverse.org/). Key commands of this section will include [`parse_double()`](https://readr.tidyverse.org/reference/parse_atomic.html) for parsing numbers directly from strings, [`parse_number()`](https://readr.tidyverse.org/reference/parse_number.html) for removing useless characters and parsing numbers from strings, [`count()`](https://dplyr.tidyverse.org/reference/count.html) which counts the unique values of one or more variables, [`pmin()`](https://stat.ethz.ch/R-manual/R-devel/library/base/html/Extremes.html) which take one or more vectors in and returns the minima or maxima of these vectors, 
[`round()`](https://stat.ethz.ch/R-manual/R-devel/library/base/html/Round.html) which rounds values in its first argument to the specified number of decimal places, and [`min_rank()`](https://dplyr.tidyverse.org/reference/row_number.html) which gives every tie the same value and ranks an inputted vector.

The goal of this chapter is to learn how to parse numbers from strings, utilize numeric transformations on variables within datasets, and learn new ways to numerically summarize data. 

Note that many of the exercises in this tutorial produce this warning:

````
Warning message:
Returning more (or less) than 1 row per `summarise()` group was deprecated in dplyr 1.1.0.
ℹ Please use `reframe()` instead.
ℹ When switching from `summarise()` to `reframe()`, remember that `reframe()` always returns an
  ungrouped data frame and adjust accordingly.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
````

Ignore that for now. We are fixing it.

## Making Numbers
### 

Our ultimate goal in this section is to make use of the functions `parse_double()` and `parse_number()` to get specific values of a variable from a certain dataset.

### Exercise 1

This chapter mostly uses functions from base R, which are available without loading any packages. But we still need the tidyverse because we’ll use these base R functions inside of tidyverse functions like mutate() and filter(). 

### 

Use `library()` let's load in the **tidyverse** package.

```{r making-numbers-1, exercise = TRUE}

```

```{r making-numbers-1-hint-1, eval = FALSE}
library(...)
```

```{r making-numbers-1-test, include = FALSE}
library(tidyverse)
```

### 

The [**readr**](https://readr.tidyverse.org/) package is one of the nine core packages in the *Tidyverse*. It provides two useful functions for parsing strings into numbers: `parse_double()` and `parse_number()`.

### Exercise 2

Define a vector `x` that has the following values within it `"1.2"`, `"5.6"`, `"1e3"`. These values are strings for now.

```{r making-numbers-2, exercise = TRUE}

```

```{r making-numbers-2-hint-1, eval = FALSE}
x <- c(..., ..., ...)
```

```{r making-numbers-2-test, include = FALSE}
x <- c("1.2", "5.6", "1e3")
```

### 

In most cases, you’ll get numbers already recorded in one of R’s numeric types: integer or double. In some cases, however, you’ll encounter them as strings.

### Exercise 3

When you have numbers that have been written as strings we should use `parse_double()`. So let's use use `parse_double()` on `x` we just created. 

```{r making-numbers-3, exercise = TRUE}
x <- c("1.2", "5.6", "1e3")

```

```{r making-numbers-3-hint-1, eval = FALSE}
x <- c("1.2", "5.6", "1e3")
parse_double(...)
```

```{r making-numbers-3-test, include = FALSE}
x <- c("1.2", "5.6", "1e3")
parse_double(x)
```

### 

This should return an output of: `#> [1]    1.2    5.6 1000.0`.

### Exercise 4

Use `parse_number()` when the string contains non-numeric text that you want to ignore. This is particularly useful for currency data and percentages. Let's first make a vector `y` that has the string values: `"$1,234"`, `"USD 3,513"`, `"59%"`

```{r making-numbers-4, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r making-numbers-4-hint-1, eval = FALSE}
y <- c(..., ..., ...)
```

```{r making-numbers-4-test, include = FALSE}
y <- c("$1,1234", "USD 3,513", "59%")
```

### 

As you can see, in this vector `y` there are non-numeric text that we would normally want to ignore if we were to see this in a dataset. 






### Exercise 5

Let's use `parse_number()` with `y` as the argument to ignore this non-numeric text.

```{r making-numbers-5, exercise = TRUE}
y <- c("$1,234", "USD 3,513", "59%")
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r making-numbers-5-hint-1, eval = FALSE}
y <- c("$1,234", "USD 3,513", "59%")
parse_number(y)
```

```{r making-numbers-5-test, include = FALSE}
y <- c("$1,234", "USD 3,513", "59%")
parse_number(y)
```

### 

The result you should've received is `#> [1] 1234 3513   59`

## Counts
### 

In this section, we will be reviewing how to use `count()` and other similar functions to make data analysis easier. 

### Exercise 1

Run `help(package = "dplyr")` at the Console. Find the help page for `count()`. Click on it. Copy/paste the Description below. (Don't worry about formatting.)

```{r counts-1}
question_text(NULL,
	answer(NULL, correct = TRUE),
	allow_retry = TRUE,
	try_again_button = "Edit Answer",
	incorrect = NULL,
	rows = 3)
```

```{r counts-1-test, include = FALSE}
question_text(NULL,
	answer(NULL, correct = TRUE),
	allow_retry = TRUE,
	try_again_button = "Edit Answer",
	incorrect = NULL,
	rows = 3)
```

### 

The functions `add_count()` and `add_tally()` are equivalents to `count()` and `tally()` but use `mutate()` instead of `summarise()` so that they add a new column with group-wise counts.

### Exercise 2

Now let's load in the dataset, `flights` that we will be making use of. This dataset contains information about all flights that departed from NYC (e.g. EWR, JFK and LGA) to destinations in the United States, Puerto Rico, and the American Virgin Islands) in 2013: 336,776 flights in total. To help understand what causes delays, it also includes a number of other useful datasets.

### 

Type in `flights` and hit "Run Code".

```{r counts-2, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-2-hint-1, eval = FALSE}
flights
```

```{r counts-2-test, include = FALSE}
flights
```

### Exercise 3

Let's count the number of planes of which departed from a certain destination. Pipe `flights` to the `count()` function. Within the call to `count()`, put an argument of `dest`.

```{r counts-3, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-3-hint-1, eval = FALSE}
flights |> count(dest)
```

```{r counts-3-test, include = FALSE}
flights |> count(dest)
```

### 

We usually put `count()` on a single line because it’s usually used at the console for a quick check that a calculation is working as expected.

### Exercise 4

If you want to see the most common values, use the previous pipe and add the parameter `sort = TRUE` to the function `count()`.

```{r counts-4, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-4-hint-1, eval = FALSE}
flights |> count(dest, sort = ...)
```

```{r counts-4-test, include = FALSE}
flights |> count(dest, sort = TRUE)
```

### 

And remember that if you want to see all the values, you can use `|> View()` or `|> print(n = Inf)`.

### Exercise 5

Create a new pipeline. Pipe flights to the `summarize()` function. Within the `summarize()` function, create a new variable `carriers` and set it to `n_distinct(carrier)`.

```{r counts-5, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-5-hint-1, eval = FALSE}
flights |> 
  summarize(carriers = ...) 
  
```

```{r counts-5-test, include = FALSE}
flights |> 
  summarize(carriers = n_distinct(carrier)) 
  
```

### 

The function `n_distinct()` counts the number of distinct (unique) values of one or more variables. For example, we could figure out which destinations are served by the most carriers. 

### Exercise 6

Using your previous code, within your call to `summarize()` add a the `.by` argument and set it to `dest`.

```{r counts-6, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-6-hint-1, eval = FALSE}
flights |> 
  summarize(carriers =  n_distinct(carrier), .by = ...)
```

```{r counts-6-test, include = FALSE}
flights |> 
  summarize(carriers = n_distinct(carrier), .by = dest)
```

### 

Setting `.by` to `dest` lets us group the `summarize()` function by the destination.

### Exercise 7

Using the previous code, add the `arrange()` function to your pipeline. Within the function `arrange()` add the argument of `carriers`. 

```{r counts-7, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-7-hint-1, eval = FALSE}
... |> 
  arrange(...)
```

```{r counts-7-test, include = FALSE}
flights |> 
  summarize(carriers = n_distinct(carrier), .by = dest) |> 
  arrange(carriers)
```

### 

We can see that the dataframe is only showing the destinations with the least carriers. Let's try and show the destinations with the most carriers.

### Exercise 8

Using your previous code, within your call to `arrange()`, add the argument `desc(carriers)` so that it arranges the carriers in descending order.

```{r counts-8, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-8-hint-1, eval = FALSE}
... |> 
  arrange(desc(...))
```

```{r counts-8-test, include = FALSE}
flights |> 
  summarize(carriers = n_distinct(carrier), .by = dest) |> 
  arrange(desc(carriers))
```

### 

`desc(x)` gives the largest values the smallest ranks.

### Exercise 9

With a new pipeline, pipe `flights` to the `count()` function. Within the call to `count`, add the `tailnum` argument.

```{r counts-9, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-9-hint-1, eval = FALSE}
flights |> 
  count(...)
```

```{r counts-9-test, include = FALSE}
flights |> 
  count(tailnum)
```

### 

A weighted count is a sum. For example you could “count” the number of miles each plane flew by using the `sum()` function. 

### Exercise 10

Within your call to `count()` add the `wt` argument and set it to `distance`. 

```{r counts-10, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-10-hint-1, eval = FALSE}
flights |> 
  count(tailnum, wt = ...)
```

```{r counts-10-test, include = FALSE}
flights |> 
  count(tailnum, wt = distance)
```

### 

Weighted counts are a common problem so count() has the `wt` argument that does the same thing.

### Exercise 11

Now, count the number of cancelled flights or missing values of each destination. With a new pipeline, pipe `flights` to the `summarize()` function. Within the call to `summarize()`, create a new variable called `n_cancelled` and set it equal to `sum(is.na(dep_time)`.

```{r counts-11, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-11-hint-1, eval = FALSE}
flights |> 
  summarize(n_cancelled = ...)
```

```{r counts-11-test, include = FALSE}
flights |> 
  summarize(n_cancelled = sum(is.na(dep_time)))
```

### 

You can count missing values by combining sum() and is.na(). In the flights dataset this represents flights that are cancelled.

### Exercise 12

Within your call to the `summarize()` function, add the `.by` argument and set it to `dest`.

```{r counts-12, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r counts-12-hint-1, eval = FALSE}
flights |> 
  summarize(n_cancelled = sum(is.na(dep_time)), .by = ...)
```

```{r counts-12-test, include = FALSE}
flights |> 
  summarize(n_cancelled = sum(is.na(dep_time)), .by = dest)
```

### 

The summarize() function here now counts the canceled flights in each destination.

## Numeric transformations
### 

This section will go over transforming numeric vectors with the function `mutate()` and using various mathematic methods to make new columns in a certain dataset.

### 

Operations like `flights |> mutate(air_time = air_time / 60)` have different lengths in the left and the right hand side. There are 336,776 individual numbers on the left of the `/` but only one number on the right. This would normally be a problem but R handles these mismatched lengths by *recycling*, or repeating, the short vector.

### 

By the end of this section we will be making a plot that looks like this:

```{r}
plots <- flights |> 
  mutate(hour = sched_dep_time %/% 100) |> 
  summarize(prop_cancelled = mean(is.na(dep_time)), n = n(), .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  geom_line(color = "grey50") + 
  geom_point(aes(size = n))

plots
```

### Exercise 1

Make a new vector `x` and set it to `c(1, 2, 10, 20)`.

```{r numeric-transformations-1, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-1-hint-1, eval = FALSE}
x <- c(...)
```

```{r numeric-transformations-1-test, include = FALSE}
x <- c(1, 2, 10, 20)
```

### 

### Exercise 2

Divide the vector `x` by 5.

```{r numeric-transformations-2, exercise = TRUE}
x <- c(1, 2, 10, 20)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-2-hint-1, eval = FALSE}
x <- c(1, 2, 10, 20)
x / ...
```

```{r numeric-transformations-2-test, include = FALSE}
x <- c(1, 2, 10, 20)
x / 5
```

### 

This operation is shorthand for `x / c(5, 5, 5, 5)`. Generally, you only want to recycle single numbers (i.e. vectors of length 1), but R will recycle any shorter length vector.

### Exercise 3

Multiply the vector `x` with the values `c(1, 2, 3)`

```{r numeric-transformations-3, exercise = TRUE}
x <- c(1, 2, 10, 20)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-3-hint-1, eval = FALSE}
x <- c(1, 2, 10, 20)
x * c(...)
```

```{r numeric-transformations-3-test, include = FALSE}
x <- c(1, 2, 10, 20)
x * c(1,2,3)
```

### 

It usually (but not always) gives you a warning if the longer vector isn’t a multiple of the shorter:

### Exercise 4

Create a new pipeline. Pipe `flights` to the `filter()` function. And add an argument that checks if `month` equals `c(1, 2)`.

```{r numeric-transformations-4, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-4-hint-1, eval = FALSE}
flights |> 
  filter(month == c(...))
```

```{r numeric-transformations-4-test, include = FALSE}
flights |> 
  filter(month == c(1, 2))
```

### 

The code runs without error, but it doesn’t return what you want. Because of the recycling rules it finds flights in odd numbered rows that departed in January and flights in even numbered rows that departed in February. And unfortunately there’s no warning because `flights` has an even number of rows.

### Exercise 5

Most arithmetic functions work with pairs of variables. Two closely related functions are `pmin()` and `pmax()`, which when given two or more variables will return the smallest or largest value in each row. 

### 

Let's utilize a premade dataframe `df`. Type in `df` and hit "Run Code".

```{r numeric-transformations-5, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-5-hint-1, eval = FALSE}
df
```

```{r numeric-transformations-5-test, include = FALSE}
df
```

### 

Note that these are different to the summary functions `min()` and `max()` which take multiple observations and return a single value. You can tell that you’ve used the wrong form when all the minimums and all the maximums have the same value.



### Exercise 6

Create a new pipeline. Pipe `df` to the `mutate()`. Within the `mutate()`, create a new variable called `min` and set it to `pmin(x, y)`.

```{r numeric-transformations-6, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-6-hint-1, eval = FALSE}
df |> 
  mutate(
    min = ...)
```

```{r numeric-transformations-6-test, include = FALSE}
df |> 
  mutate(
    min = pmin(x, y)
  )
```

### 

We can see that the missing values are still being accounted for in the min variable (`NA`).

### Exercise 7

Using the previous pipe, add the argument `na.rm` to the `pmin()` function call within the `mutate()` function and set it to `TRUE`. 

```{r numeric-transformations-7, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-7-hint-1, eval = FALSE}
df |> 
  mutate(
    min = pmin(x, y, na.rm = ...)
  )
```

```{r numeric-transformations-7-test, include = FALSE}
df |> 
  mutate(
    min = pmin(x, y, na.rm = TRUE)
  )
```

### 

If `na.rm` is `FALSE` an `NA` value in any of the arguments will cause a value of `NA` to be returned, otherwise `NA` values are ignored.

### Exercise 8

Using the previous pipeline. Within the call to the `mutate()` function create a new variable `max` and set it to `pmax(x, y)`.

```{r numeric-transformations-8, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-8-hint-1, eval = FALSE}
df |> 
  mutate(
    min = pmin(x, y, na.rm = TRUE), 
    max = ...)
```

```{r numeric-transformations-8-test, include = FALSE}
df |> 
  mutate(
    min = pmin(x, y, na.rm = TRUE), 
    max = pmax(x, y)
  )
```

### 

We can see that the missing values are still being accounted for in the max variable (`NA`).

### Exercise 9

Using the previous pipe, add the argument `na.rm` to the `pmax()` function call within the `mutate()` function and set it to `TRUE`. 

```{r numeric-transformations-9, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-9-hint-1, eval = FALSE}
df |> 
  mutate(
    min = pmin(x, y, na.rm = TRUE), 
    max = pmax(x, y, na.rm = ...)
  )
```

```{r numeric-transformations-9-test, include = FALSE}
df |> 
  mutate(
    min = pmin(x, y, na.rm = TRUE), 
    max = pmax(x, y, na.rm = TRUE)
  )
```

 
### 

Note that these are different to the summary functions `min()` and `max()` which take multiple observations and return a single value. You can tell that you’ve used the wrong form when all the minimums and all the maximums have the same value.

### Exercise 10

Modular arithmetic is the technical name for the type of math you did before you learned about decimal places, i.e. division that yields a whole number and a remainder. In R, `%/%` does integer division and `%%` computes the remainder.

### 

Make a new vector called `z` equal to `1:10`. 

```{r numeric-transformations-10, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-10-hint-1, eval = FALSE}
z <- c(1:10)
```

```{r numeric-transformations-10-test, include = FALSE}
z <- c(1:10)
```

### Exercise 11

Using the same vector, `z`, divide by 3 using integer division (`%/%`).

```{r numeric-transformations-11, exercise = TRUE}
z <- 1:10
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-11-hint-1, eval = FALSE}
z <- 1:10
z ... 3
```

```{r numeric-transformations-11-test, include = FALSE}
z <- 1:10
z %/% 3
```

### 

We can also find the remainder using `%%`.

### Exercise 12

Compute the remainder of the vector `z` divided by `3` by typing in `z %% 3` and hitting "Run Code". 

```{r numeric-transformations-12, exercise = TRUE}
z <- 1:10
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-12-hint-1, eval = FALSE}
z <- 1:10
z ... 3
```

```{r numeric-transformations-12-test, include = FALSE}
z <- 1:10
z %% 3
```

### Exercise 13

Create a new pipeline. Pipe `flights` with the function `mutate()`. Within the `mutate()` function, create a new variable called `hour` and set it to `sched_dep_time %/% 100`.

```{r numeric-transformations-13, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-13-hint-1, eval = FALSE}
flights |> 
  mutate(
    hour = ...)
```

```{r numeric-transformations-13-test, include = FALSE}
flights |> 
  mutate(
    hour = sched_dep_time %/% 100)
```

### 

Modular arithmetic is handy for the flights dataset, because we can use it to unpack the `sched_dep_time` variable into `hour` and `minute`.

### Exercise 14

Using the previous code, within the `mutate()` function, create a new variable called `minute` and set it to `sched_dep_time %% 100`.

```{r numeric-transformations-14, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-14-hint-1, eval = FALSE}
flights |> 
  mutate(
    hour = sched_dep_time %/% 100, 
    minute = ...)
```

```{r numeric-transformations-14-test, include = FALSE}
flights |> 
  mutate(
    hour = sched_dep_time %/% 100, 
    minute = sched_dep_time %% 100)
```

### 

You can read more about other arithmetic operators [here](https://stat.ethz.ch/R-manual/R-devel/library/base/html/Arithmetic.html).

### Exercise 15

Using your previous code, within the `mutate()` function and add an argument called `.keep` and set it to `"used"`. 

```{r numeric-transformations-15, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-15-hint-1, eval = FALSE}
flights |> 
  mutate(
    hour = sched_dep_time %/% 100, 
    minute = sched_dep_time %% 100, 
    .keep = ...
  )
```

```{r numeric-transformations-15-test, include = FALSE}
flights |> 
  mutate(
    hour = sched_dep_time %/% 100, 
    minute = sched_dep_time %% 100, 
    .keep = "used"
  )
```

### 

Modular arithmetic is handy for the flights dataset, because we can use it to unpack the `sched_dep_time` variable into `hour` and `minute` variables.

### Exercise 16

Let's use all of the stuff we've learnt to create a graph. This is what the graph is supposed to look like.

```{r include = TRUE}
plots
```

### 

Create a new pipeline. Pipe `flights` to the function `summarize()`. Within the call to `summarize()`, create a new variable called `hour` and set it to `sched_dep_time %/% 100`.

```{r numeric-transformations-16, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-16-hint-1, eval = FALSE}
flights |> 
  summarize(hour = ...)

```

```{r numeric-transformations-16-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100)
```

### 

We can combine that with a trick using `mean(is.na(x))` to see how the proportion of cancelled flights varies over the course of the day. 

### Exercise 17

Using your previous code. Within the call to `summarize()`, create a new variable called `prop_cancelled` and set it to `mean(is.na(dep_time))`.

```{r numeric-transformations-17, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-17-hint-1, eval = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100, 
            prop_cancelled = ...)
```

```{r numeric-transformations-17-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100, 
            prop_cancelled = mean(is.na(dep_time)))
```

### 

`is.na(x)` works with any type of vector and returns TRUE for missing values and FALSE for everything else, which we can use to find all the rows with a missing `dep_time`.

### Exercise 18

Using your previous code. Within the call to `summarize()`, create another variable called `n` and set it to `n()`.

```{r numeric-transformations-18, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-18-hint-1, eval = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100, 
            prop_cancelled = mean(is.na(dep_time)), 
            n = ...)
```

```{r numeric-transformations-18-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100, 
            prop_cancelled = mean(is.na(dep_time)), 
            n = n())
```

### 

`n()` is a special summary function that doesn’t take any arguments and instead accesses information about the “current” group. This means that it only works inside dplyr verb.

### Exercise 19

Using your previous code. Within the call to `summarize()`, add an argument called `.by` and set it to hour, so that we can group by hour. 

```{r numeric-transformations-19, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-19-hint-1, eval = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100, 
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = ...)
```

```{r numeric-transformations-19-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour)
```

### 

### Exercise 20

Using your previous code. Add the function `filter()` to the pipeline. Within the call to `filter()` create a new argument that checks if the variable `hour` is `> 1`. 

```{r numeric-transformations-20, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-20-hint-1, eval = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(...)
```

```{r numeric-transformations-20-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1)
```

### Exercise 21

Using your previous code. Add the function `ggplot()` to the pipeline. Within the function `ggplot()` and map `x` to `hour` and `y` to `prop_cancelled` using `aes()`. 

```{r numeric-transformations-21, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-21-hint-1, eval = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = ..., y = ...))
```

```{r numeric-transformations-21-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled))
```

### 

### Exercise 22

Using your previous code. Add the function `geom_line()` to the pipeline.

```{r numeric-transformations-22, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-22-hint-1, eval = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  ...
```

```{r numeric-transformations-22-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  geom_line()
```

### Exercise 23

Using your previous code, within the call to `geom_line()` make a new argument called `color` and set it to `"grey50"`.

```{r numeric-transformations-23, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-23-hint-1, eval = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  geom_line(color = ...)
```

```{r numeric-transformations-23-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  geom_line(color = "grey50")
```

### Exercise 24

Using your previous code, add the function `geom_point()` to the pipeline.

```{r numeric-transformations-24, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-24-hint-1, eval = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  geom_line(color = "grey50") + 
  ...
```

```{r numeric-transformations-24-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  geom_line(color = "grey50") + 
  geom_point()
```

### Exercise 25

Copy your previous code. Within the function `geom_point()` map `size` to `n`. 

```{r numeric-transformations-25, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-25-hint-1, eval = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  geom_line(color = "grey50") + 
  geom_point(aes(size = ...))
```

```{r numeric-transformations-25-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  geom_line(color = "grey50") + 
  geom_point(aes(size = n))
```

### Exercise 26

Using your previous code, add a title, subtitle, and axis titles using the `labs()` function. 

```{r numeric-transformations-26, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-transformations-26-hint-1, eval = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  geom_line(color = "grey50") + 
  geom_point(aes(size = n)) + 
  labs(title = ...,
       subtitle = ..., 
       x = ...,
       y = ...)
```

```{r numeric-transformations-26-test, include = FALSE}
flights |> 
  summarize(hour = sched_dep_time %/% 100,
            prop_cancelled = mean(is.na(dep_time)), 
            n = n(), 
            .by = hour) |> 
  filter(hour > 1) |> 
  ggplot(aes(x = hour, y = prop_cancelled)) +
  geom_line(color = "grey50") + 
  geom_point(aes(size = n)) + 
  labs(title = "",
       subtitle = "", 
       x = "Hour",
       y = "Probability cancelled")
```

### 

What we have here is a line plot with scheduled departure hour on the x-axis, and proportion of cancelled flights on the y-axis. Cancellations seem to accumulate over the course of the day until 8pm, very late flights are much less likely to be cancelled.

Reminder: Your plot should look somewhat like this.

```{r include = TRUE}
plots
```

## Other Number Functions
### 

This section will allow you to practice with many other number functions with many different uses.

### Exercise 1

Run `?log` in the Console and copy paste the Description below. (Don't worry about formatting)

```{r other-number-functions-1}
question_text(NULL,
	answer(NULL, correct = TRUE),
	allow_retry = TRUE,
	try_again_button = "Edit Answer",
	incorrect = NULL,
	rows = 6)
```

### 

Logarithms are an incredibly useful transformation for dealing with data that ranges across multiple orders of magnitude and converting exponential growth to linear growth.

### Exercise 2

Use the function `round()` with the argument `123.456` and hit "Run Code".

```{r other-number-functions-2, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-2-hint-1, eval = FALSE}
round(...)
```

```{r other-number-functions-2-test, include = FALSE}
round(123.456)
```

### 

The function round(x) rounds a number to the nearest integer. You can control the precision of the rounding with the second argument, digits. `round(x, digits)` rounds to the nearest 10^-digits so `digits = 2` will round to the nearest 0.01.

### Exercise 3

Copy your previous code. Add the `digits` argument, with a value of `2`, to the `round()` function.

```{r other-number-functions-3, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-3-hint-1, eval = FALSE}
round(123.456, digits = ...)
```

```{r other-number-functions-3-test, include = FALSE}
round(123.456, digits = 2)
```

### 

You can control the precision of the rounding with the second argument, digits. round(x, digits) rounds to the nearest 10^-n so digits = 2 will round to the nearest 0.01.

### Exercise 4

Copy your previous code and change the `digits` argument to `-2`. 

```{r other-number-functions-4, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-4-hint-1, eval = FALSE}
round(123.456 , digits = ...)
```

```{r other-number-functions-4-test, include = FALSE}
round(123.456, digits = -2)
```

### 

The *-2* argument in the `round()` function will round the number `123.456` to the nearest hundred which would be 100 in this case.

### Exercise 5

Type in `round(c(1.5, 2.5))` and hit "Run Code".

```{r other-number-functions-5, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-5-hint-1, eval = FALSE}
round(...)
```

```{r other-number-functions-5-test, include = FALSE}
round(c(1.5, 2.5))
```

### 

round() uses what’s known as “round half to even” or Banker’s rounding: if a number is half way between two integers, it will be rounded to the even integer. This is a good strategy because it keeps the rounding unbiased: half of all 0.5s are rounded up, and half are rounded down.

### Exercise 6

The functions `floor()` and `ceiling()` are paired with `round()`. Use the function `floor()` with the numerical argument of `123.456`.

```{r other-number-functions-6, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-6-hint-1, eval = FALSE}
floor(...)
```

```{r other-number-functions-6-test, include = FALSE}
floor(123.456)
```

### 

The function `floor()` will always round down to the nearest integer.

### Exercise 7

Type in ceiling() with the numerical argument of `123.456`. 
```{r other-number-functions-7, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-7-hint-1, eval = FALSE}
ceiling(...)
```

```{r other-number-functions-7-test, include = FALSE}
ceiling(123.456)
```

### 

The function `ceiling()` always round up to the nearest integer.

### Exercise 8

Make a new variable `x` and set it to the number 123.456.
```{r other-number-functions-8, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-8-hint-1, eval = FALSE}
x <- ...
```

```{r other-number-functions-8-test, include = FALSE}
x <- 123.456
```

### 

The `floor()` and `ceiling()` functions don’t have a digits argument, so you can instead scale down, round, and then scale back up

### Exercise 9

Let's round down to the nearest two digits. Type in `floor(x / 0.01) * 0.01` and hit "Run Code".

```{r other-number-functions-9, exercise = TRUE}
x <- 123.456
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-9-hint-1, eval = FALSE}
x <- 123.456
floor(...) * ...
```

```{r other-number-functions-9-test, include = FALSE}
x <- 123.456
floor(x / 0.01) * 0.01
```

### Exercise 10

Let's round up to the nearest two digits. Type in `ceiling(x / 0.01) * 0.01` and hit "Run Code". 
```{r other-number-functions-10, exercise = TRUE}
x <- 123.456
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-10-hint-1, eval = FALSE}
x <- 123.456
ceiling(...) * ...
```

```{r other-number-functions-10-test, include = FALSE}
x <- 123.456
ceiling(x / 0.01) * 0.01
```

### 

You can use the same technique if you want to round() to a multiple of some other number

### Exercise 11

Let's round to the nearest multiple of 4. Type in `round(x / 4) * 4` and hit "Run Code".
```{r other-number-functions-11, exercise = TRUE}
x <- 123.456
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-11-hint-1, eval = FALSE}
x <- 123.456
round(...) * ...
```

```{r other-number-functions-11-test, include = FALSE}
x <- 123.456
round(x/4) * 4
```

### Exercise 12

This time let's round to the nearest 0.25. Type in `round(x / 0.25) * 0.25` and hit "Run Code".
```{r other-number-functions-12, exercise = TRUE}
x <- 123.456
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-12-hint-1, eval = FALSE}
x <- 123.456
round(...) * ...
```

```{r other-number-functions-12-test, include = FALSE}
x <- 123.456
round(x / 0.25) * 0.25
```

### Exercise 13

Make a new vector `x` and set it to equal `c(1, 2, 5, 10, 15, 20)`. 
```{r other-number-functions-13, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-13-hint-1, eval = FALSE}
x <- c(...)
```

```{r other-number-functions-13-test, include = FALSE}
x <- c(1,2,5,10,15,20)
```

### 

Use [`cut()`](https://stat.ethz.ch/R-manual/R-devel/library/base/html/cut.html) to break up (aka bin) a numeric vector into discrete buckets.

### Exercise 14

Use `cut()` on the numeric vector `x` and add in the `breaks` argument and set `breaks` to `c(0, 5, 10, 15, 20)`
```{r other-number-functions-14, exercise = TRUE}
x <- c(1, 2, 5, 10, 15, 20)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-14-hint-1, eval = FALSE}
x <- c(1, 2, 5, 10, 15, 20)
cut(x, breaks = ...)
```

```{r other-number-functions-14-test, include = FALSE}
x <- c(1, 2, 5, 10, 15, 20)
cut(x, breaks = c(0,5,10,15,20))
```

### Exercise 15

The breaks don't need to be evenly spaced. Copy the previous code and change the `breaks` argument and set it to `c(0, 5, 10, 100)`.
```{r other-number-functions-15, exercise = TRUE}
x <- c(1, 2, 5, 10, 15, 20)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-15-hint-1, eval = FALSE}
x <- c(1, 2, 5, 10, 15, 20)
cut(x, breaks = ...)
```

```{r other-number-functions-15-test, include = FALSE}
x <- c(1, 2, 5, 10, 15, 20)
cut(x, breaks = c(0,5,10,100))
```

### 

You can optionally supply your own `labels`. Note that there should be one less `labels` than `breaks`. 

### Exercise 16

Copy your previous code and add a new argument called `labels` and set it to `c("sm", "md", "lg")`.
```{r other-number-functions-16, exercise = TRUE}
x <- c(1, 2, 5, 10, 15, 20)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-16-hint-1, eval = FALSE}
x <- c(1, 2, 5, 10, 15, 20)
cut(x, 
    breaks = ...,
    labels = c(...))
```

```{r other-number-functions-16-test, include = FALSE}
x <- c(1, 2, 5, 10, 15, 20)
cut(x, breaks = c(0,5,10,100), labels = c("sm", "md", "lg"))
```

### 

Any values outside of the range of the breaks will become `NA`. See the documentation for other useful arguments like `right` and `include.lowest`, which control if the intervals are [a, b) or (a, b] and if the lowest interval should be [a, b] by running `?cut()` in the Console.

### Exercise 17

Make a new vector `x` and set it to `1:10`.

```{r other-number-functions-17, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-17-hint-1, eval = FALSE}
x <- ...
```

```{r other-number-functions-17-test, include = FALSE}
x <- 1:10
```

### 

The function `cumsum()` will cumulate the sum using all the previous integers within the vector

### Exercise 18

Use the function `cumsum()` on `x`.
```{r other-number-functions-18, exercise = TRUE}
x <- 1:10
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r other-number-functions-18-hint-1, eval = FALSE}
x <- 1:10
cumsum(...)
```

```{r other-number-functions-18-test, include = FALSE}
x <- 1:10
cumsum(x)
```

### 

Base R provides cumsum(), cumprod(), cummin(), cummax() for running, or cumulative, sums, products, mins and maxes. dplyr provides cummean() for cumulative means. Cumulative sums tend to come up the most in practice. 

## General transformations
### 

This section will utilize the package **dplyr** and allow you to make use of transformations on an actual dataframe.

### Exercise 1

Make a new vector named `x` that has the values of `c(1, 2, 3, 4, NA)`.

```{r general-transformations-1, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-1-hint-1, eval = FALSE}
x <- ...
```

```{r general-transformations-1-test, include = FALSE}
x <- c(1,2,3,4,NA)
```

### 

dplyr provides a number of ranking functions inspired by SQL, but you should always start with dplyr::min_rank(). It uses the typical method for dealing with ties, e.g., 1st, 2nd, 2nd, 4th

### Exercise 2

Use the function `min_rank()` taking `x` as the argument.

```{r general-transformations-2, exercise = TRUE}
x <- c(1, 2, 3, 4, NA)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-2-hint-1, eval = FALSE}
x <- c(1, 2, 3, 4, NA)
min_rank(x)
```

```{r general-transformations-2-test, include = FALSE}
x <- c(1, 2, 3, 4, NA)
min_rank(x)
```

### 

Note that the smallest values get the lowest ranks; use desc(x) to give the largest values the smallest ranks

### Exercise 3

Now, use the function `min_rank()` taking `desc(x)` as its argument.

```{r general-transformations-3, exercise = TRUE}
x <- c(1, 2, 3, 4, NA)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-3-hint-1, eval = FALSE}
x <- c(1, 2, 3, 4, NA)
min_rank(...)
```

```{r general-transformations-3-test, include = FALSE}
x <- c(1, 2, 3, 4, NA)
min_rank(desc(x))
```

### 

If min_rank() doesn’t do what you need, look at the variants `dplyr::row_number()`, `dplyr::dense_rank()`, `dplyr::percent_rank()`, and `dplyr::cume_dist()`. See the documentation for details.

### Exercise 4

Type in `ranktypes` and hit "Run Code".

```{r general-transformations-4, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-4-hint-1, eval = FALSE}
ranktypes
```

```{r general-transformations-4-test, include = FALSE}
ranktypes
```

### 

Right now, `ranktypes` is just a tibble of the vector `x`.

### Exercise 5

Pipe `ranktypes` to the `mutate()` function. Within the call to `mutate()`, create a variable `row_number` which equals `row_number(x)`.

```{r general-transformations-5, exercise = TRUE}
x <- c(1, 2, 3, 4, NA)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-5-hint-1, eval = FALSE}
x <- c(1, 2, 3, 4, NA)
ranktypes |> 
  mutate(row_number = ...)
```

```{r general-transformations-5-test, include = FALSE}
x <- c(1, 2, 3, 4, NA)
ranktypes |> 
  mutate(row_number = row_number(x))
```

### Exercise 6

Using the same pipe as above, create another variable, within the call to `mutate()`, called `dense_rank` equal to `dense_rank(x)`.

```{r general-transformations-6, exercise = TRUE}
x <- c(1, 2, 3, 4, NA)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-6-hint-1, eval = FALSE}
x <- c(1, 2, 3, 4, NA)
ranktypes |> 
  mutate(row_number = row_number(x), 
         dense_rank = ...)
```

```{r general-transformations-6-test, include = FALSE}
x <- c(1, 2, 3, 4, NA)
ranktypes |> 
  mutate(row_number = row_number(x),
         dense_rank = dense_rank(x))
```

### Exercise 7

Using the same pipe as above, create another variable, within the call to `mutate()`, called `percent_rank` equal to `percent_rank(x)`.

```{r general-transformations-7, exercise = TRUE}
x <- c(1, 2, 3, 4, NA)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-7-hint-1, eval = FALSE}
x <- c(1, 2, 3, 4, NA)
ranktypes |> 
  mutate(row_number = row_number(x),
         dense_rank = dense_rank(x),
         percent_rank = ...)
```

```{r general-transformations-7-test, include = FALSE}
x <- c(1, 2, 3, 4, NA)
ranktypes |> 
  mutate(row_number = row_number(x),
         dense_rank = dense_rank(x),
         percent_rank = percent_rank(x))
```

### Exercise 8

Using the same pipe as above, create another variable, within the call to `mutate()`, called `cume_dist` equal to `cume_dist(x)`.

```{r general-transformations-8, exercise = TRUE}
x <- c(1, 2, 3, 4, NA)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-8-hint-1, eval = FALSE}
x <- c(1, 2, 3, 4, NA)
ranktypes |> 
  mutate(row_number = row_number(x),
         dense_rank = dense_rank(x),
         percent_rank = percent_rank(x),
         cume_dist = ...)
```

```{r general-transformations-8-test, include = FALSE}
x <- c(1, 2, 3, 4, NA)
ranktypes |> 
  mutate(row_number = row_number(x),
         dense_rank = dense_rank(x),
         percent_rank = percent_rank(x),
         cume_dist = cume_dist(x))
```

### 

You can achieve many of the same results by picking the appropriate ties.method argument to base R’s rank(); you’ll probably also want to set na.last = "keep" to keep NAs as NA.

### Exercise 9

Type in `numbers` and hit "Run Code". 

```{r general-transformations-9, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-9-hint-1, eval = FALSE}
numbers
```

```{r general-transformations-9-test, include = FALSE}
numbers
```

### 

The `numbers` tibble just has one variable, `id`,  with values from 1 through 10.

### Exercise 10

Pipe `numbers` to the `mutate()` function. Within the call to `mutate()`, create a variable `row0` which equals `row_number()` minus `1`.

```{r general-transformations-10, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-10-hint-1, eval = FALSE}
numbers |> 
  mutate(
    row0 = ... - ...
  )
```

```{r general-transformations-10-test, include = FALSE}
numbers |> 
  mutate(
    row0 = row_number() - 1)
```

### 

row_number() can also be used without any arguments when inside a dplyr verb. In this case, it’ll give the number of the “current” row.

### Exercise 11

Using the same pipe as above, create another variable, within the call to `mutate()`, called `three_groups` equal to `row0 %% 3`.

```{r general-transformations-11, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-11-hint-1, eval = FALSE}
numbers |> 
  mutate(
    row0 = row_number() - 0, 
    three_groups = ... %% ...
  )
```

```{r general-transformations-11-test, include = FALSE}
numbers |> 
  mutate(
    row0 = row_number() - 0,
    three_groups = row0 %% 3)
```

### 

When combined with %% or %/%, `row_number()`` can be a useful tool for dividing data into similarly sized groups

### Exercise 12

Using the same pipe as above, create another variable, within the call to `mutate()`, called `three_in_each_group` set to `row0 %/% 3`.

```{r general-transformations-12, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-12-hint-1, eval = FALSE}
numbers |> 
  mutate(
    row0 = row_number() - 0,
    three_groups = row0 %% 3,
    three_in_each_group = ... %/% ...
  )
```

```{r general-transformations-12-test, include = FALSE}
numbers |> 
  mutate(
    row0 = row_number() - 0,
    three_groups = row0 %% 3,
    three_in_each_group = row0 %/% 3)
```

### Exercise 13

Create a new vector `x` and set it to `c(2, 5, 11, 11, 19, 35)`

```{r general-transformations-13, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-13-hint-1, eval = FALSE}
x <- ...
```

```{r general-transformations-13-test, include = FALSE}
x <- c(2, 5, 11, 11, 19, 35)
```

### 

dplyr::lead() and dplyr::lag() allow you to refer the values just before or just after the “current” value. They return a vector of the same length as the input, padded with NAs at the start or end

### Exercise 14

Use the function `lag()` taking in `x` as its argument.

```{r general-transformations-14, exercise = TRUE}
x <- c(2, 5, 11, 11, 19, 35)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-14-hint-1, eval = FALSE}
x <- c(2, 5, 11, 11, 19, 35)
lag(...)
```

```{r general-transformations-14-test, include = FALSE}
x <- c(2, 5, 11, 11, 19, 35)
lag(x)
```

### Exercise 15

Use the function `lead()` taking in `x` as its argument. 

```{r general-transformations-15, exercise = TRUE}
x <- c(2, 5, 11, 11, 19, 35)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-15-hint-1, eval = FALSE}
x <- c(2, 5, 11, 11, 19, 35)
lead(...)
```

```{r general-transformations-15-test, include = FALSE}
x <- c(2, 5, 11, 11, 19, 35)
lead(x)
```

### Exercise 16

Subtract `lag(x)` from  `x`.

```{r general-transformations-16, exercise = TRUE}
x <- c(2, 5, 11, 11, 19, 35)
```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-16-hint-1, eval = FALSE}
x <- c(2, 5, 11, 11, 19, 35)
x - ...
```

```{r general-transformations-16-test, include = FALSE}
x <- c(2, 5, 11, 11, 19, 35)
x-lag(x)
```

### 

<!-- This is sloppy. We are using two concepts at the same time. First, is the specific calculation using the `x` vector. The second, in the knowledge drop, is the generic calculation which works for any vector `x`. Students find this juxtaposition confusing! -->

`x - lag(x)` will give you the difference between the current and previous value for all the elements of the vector `x`.

### Exercise 17

Type in `x == lag(x)` and hit "Run Code".

```{r general-transformations-17, exercise = TRUE}
x <- c(2, 5, 11, 11, 19, 35)
```

```{r general-transformations-17-hint-1, eval = FALSE}
x <- c(2, 5, 11, 11, 19, 35)
x == ...
```

```{r general-transformations-17-test, include = FALSE}
x <- c(2, 5, 11, 11, 19, 35)
x==lag(x)
```

### 

`x == lag(x)` tells you when the current value changes. You can lead or lag by more than one position by using the second argument, `n`.

### Exercise 18

When you’re looking at website data, it’s common to want to break up events into sessions, where you begin a new session after a gap of more than `x` minutes since the last activity. For example, the `times_visited` dataset has the times when someone visited a website.

### 

Type in `times_visited` and hit "Run Code".

```{r general-transformations-18, exercise = TRUE}

```

```{r general-transformations-18-hint-1, eval = FALSE}
times_visited
```

```{r general-transformations-18-test, include = FALSE}
times_visited
```

### 

Sometimes you want to start a new group every time some event occurs. We've computed the time between each event, and figured out that there's a gap that's big enough to qualify.

### Exercise 19

Pipe `times_visited` to the `mutate()` function. Within the call to `mutate()`, create a variable `diff` which is set to `time` minus `lag(time, default = first(time))`.

```{r general-transformations-19, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-19-hint-1, eval = FALSE}
times_visited |> 
  mutate(diff = ...)
```

```{r general-transformations-19-test, include = FALSE}
times_visited |> 
  mutate(diff = lag(time, default = first(time)))
```

### Exercise 20

Using the same pipe as above, create another variable, within the call to `mutate()`, called `has_gap` set to `diff >= 5`.

```{r general-transformations-20, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-20-hint-1, eval = FALSE}
times_visited |> 
  mutate(
    diff = diff = lag(time, default = first(time)), 
    has_gap = ...)
```

```{r general-transformations-20-test, include = FALSE}
times_visited |> 
  mutate(diff = lag(time, default = first(time)),
         has_gap = diff >=5)
```

### 

But how do we go from that logical vector to something that we can `group_by()`? `cumsum()` comes to the rescue here.

### Exercise 21

Using the same pipe as above, create another variable, within the call to `mutate()`, called `group` set to `cumsum(has_gap)`. 

```{r general-transformations-21, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-21-hint-1, eval = FALSE}
times_visited |> 
  mutate(
    diff = diff = lag(time, default = first(time)), 
    has_gap = diff >=5,
    group = ...)
```

```{r general-transformations-21-test, include = FALSE}
times_visited |> 
  mutate(diff = lag(time, default = first(time)),
         has_gap = diff >=5,
         group = cumsum(has_gap))
```

### 

When `has_gap` is `TRUE`, `cumsum()` will increment `group` by `1`. 

### Exercise 22

Imagine you have a dataframe with a bunch of repeated values. Type in `repetition` and hit "Run Code".

```{r general-transformations-22, exercise = TRUE}

```

```{r general-transformations-22-hint-1, eval = FALSE}
repetition
```

### Exercise 23

Create a new pipeline and pipe `repetition` to the `summarize()` function. Within the call to `summarize()`, create a variable `id` and set it to `consecutive_id(x)`.

```{r general-transformations-23, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-23-hint-1, eval = FALSE}
repetition |> 
  summarize(id = ...)
```

```{r general-transformations-23-test, include = FALSE}
repetition |> 
  summarize(id = consecutive_id(x))
```

### 

Another approach for creating grouping variables is `consecutive_id()`, which starts a new group every time one of its arguments changes.

### Exercise 24

Using your previous code, within your call to the `summarize()` function, add the parameters `x` and `y` so that we could include these columns in our plot.

```{r general-transformations-24, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-24-hint-1, eval = FALSE}
repetition |> 
  summarize(id = consecutive_id(x), x, ...)
```

```{r general-transformations-24-test, include = FALSE}
repetition |> 
  summarize(id = consecutive_id(x), x, y)
```

### Exercise 25

Using the same pipe as above, add the function `slice_head()` to the pipeline. Within this function add the argument `n` and set it to `1`.

```{r general-transformations-25, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-25-hint-1, eval = FALSE}
repetition |> 
  summarize(id = consecutive_id(x), x, y)
  slice_head(n = ...)
```

```{r general-transformations-25-test, include = FALSE}
repetition |> 
  summarize(id = consecutive_id(x), x, y) |>
  slice_head(n=1)
```

### 

This keeps the first row from each repeated `x`.

### Exercise 26

Using your previous code, within the `slice_head()` function, add the `by` argument and set it to `id`.

```{r general-transformations-26, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r general-transformations-26-hint-1, eval = FALSE}
repetition |> 
  summarize(id = consecutive_id(x), x, y) |>
  slice_head(n=1, by = .__C__.name)
```

```{r general-transformations-26-test, include = FALSE}
repetition |> 
  summarize(id = consecutive_id(x), x, y) |>
  slice_head(n=1, by = id)
```

## Numeric summaries
### 

This section will introduce more useful summary functions that will help to summarize your data much better.

### Exercise 1

Type in `flights` and hit "Run Code".

```{r numeric-summaries-1, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-1-hint-1, eval = FALSE}
flights
```

```{r numeric-summaries-1-test, include = FALSE}
flights
```

### Exercise 2

Create a new pipeline. Pipe `flights` to the `summarize()` function. Within the `summarize()` function, create a new variable `mean` and set it to `mean(dep_delay, na.rm = TRUE)`.

```{r numeric-summaries-2, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-2-hint-1, eval = FALSE}
flights |> 
  summarize(
    mean = ...
  )
```

```{r numeric-summaries-2-test, include = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE)
  )
```

### 

An alternative to `mean()` to use the `median()`, which finds a value that lies in the “middle” of the vector, Depending on the shape of the distribution of the variable you’re interested in, mean or median might be a better measure of center. 

### Exercise 3

Using the same pipe as above, create another variable, within the call to `summarize()`, called `median` and set it to `median(dep_delay, na.rm = TRUE)`. 

```{r numeric-summaries-3, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-3-hint-1, eval = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = ...
  )
```

```{r numeric-summaries-3-test, include = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE)
  )
```

### Exercise 4

Using the same pipe as above, create another variable, within the call to `summarize()`, called `n` and set it to `n()`. 

```{r numeric-summaries-4, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-4-hint-1, eval = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = ...
  )
```

```{r numeric-summaries-4-test, include = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n()
  )
```

### Exercise 5

Using your previous code, within your call to the `summarize()` function add an argument called `.by` and set it to `c(year, month, day)`. 

```{r numeric-summaries-5, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-5-hint-1, eval = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(...)
  )
```

```{r numeric-summaries-5-test, include = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  )
```

### Exercise 6

Using the same pipe as above, add the `ggplot()` function to the pipeline. Within the `ggplot()` function, map `x` to `mean` and `y` to `median`.

```{r numeric-summaries-6, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-6-hint-1, eval = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = ..., y = ...))
```

```{r numeric-summaries-6-test, include = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = mean, y = median))
```

### Exercise 7

Using the same pipe as above, add the `geom_abline()` function to the pipeline. Within the `geom_abline()` function, add the argument `slope` and set it to `1`
.
```{r numeric-summaries-7, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-7-hint-1, eval = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = mean, y = median)) + 
  geom_abline(slope = ...)
```

```{r numeric-summaries-7-test, include = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = mean, y = median)) + 
  geom_abline(slope = 1)
```

### Exercise 8

Using the same pipe as above, within the `geom_abline()` function, add another argument called `intercept` and set it to `0`.

```{r numeric-summaries-8, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-8-hint-1, eval = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = mean, y = median)) + 
  geom_abline(slope = 1, intercept = ...)
```

```{r numeric-summaries-8-test, include = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = mean, y = median)) + 
  geom_abline(slope = 1, intercept = 0)
```

### Exercise 9

Using the same pipe as above, within the `geom_abline()` function, add another argument called `color` and set it to `"white"`.

```{r numeric-summaries-9, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-9-hint-1, eval = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = mean, y = median)) + 
  geom_abline(slope = 1, intercept = 0, color = ...)
```

```{r numeric-summaries-9-test, include = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = mean, y = median)) + 
  geom_abline(slope = 1, intercept = 0, color = "white")
```

### Exercise 10

Using the same pipe as above, within the `geom_abline()` function, add another argument called `linewidth` and set it to `2`.

```{r numeric-summaries-10, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-10-hint-1, eval = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = mean, y = median)) + 
  geom_abline(slope = 1, intercept = 0, color = "white", linewidth = ...)
```

```{r numeric-summaries-10-test, include = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = mean, y = median)) + 
  geom_abline(slope = 1, intercept = 0, color = "white", 
              linewidth = 2)
```

### 

Depending on the shape of the distribution of the variable you’re interested in, mean or median might be a better measure of center. For example, for symmetric distributions we generally report the mean while for skewed distributions we usually report the median.

### Exercise 11

Using the same pipe as above, add the `geom_point()` function to the pipeline.

```{r numeric-summaries-11, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-11-hint-1, eval = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)
  ) |> 
  ggplot(aes(x = mean, y = median)) + 
  geom_abline(slope = 1, intercept = 0, color = "white", linewidth = 2) + 
  ...
```

```{r numeric-summaries-11-test, include = FALSE}
flights |> 
  summarize(
    mean = mean(dep_delay, na.rm = TRUE),
    median = median(dep_delay, na.rm = TRUE),
    n = n(),
    .by = c(year, month, day)) |> 
  ggplot(aes(x = mean, y = median)) +
  geom_abline(slope = 1, intercept = 0, color = "white", linewidth = 2) +
  geom_point()
```

### 

This plot compares the mean vs. the median departure delay (in minutes) for each destination. The median delay is always smaller than the mean delay because flights sometimes leave multiple hours late, but never leave multiple hours early. It is a scatterplot showing the differences of summarizing daily depature delay with median instead of mean.

### Exercise 12

Create a new pipeline. Pipe `flights` to the `summarize()` function. Within the `summarize()` function, create a new variable `max` and set it to `max(dep_delay, na.rm = TRUE)`.
```{r numeric-summaries-12, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-12-hint-1, eval = FALSE}
flights |> 
  summarize(
    max = ...
  )
```

```{r numeric-summaries-12-test, include = FALSE}
flights |> 
  summarize(
    max = max(dep_delay, na.rm = TRUE)
  )
```

### 

min() and max() will give you the largest and smallest values.

### Exercise 13

Using the same pipe as above, create another variable, within the call to `summarize()`, called `q95` and set it to `quantile(dep_delay, 0.95, na.rm = TRUE)`. 
```{r numeric-summaries-13, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-13-hint-1, eval = FALSE}
flights |> 
  summarize(
    max = max(dep_delay, na.rm = TRUE), 
    q95 = ...
  )
```

```{r numeric-summaries-13-test, include = FALSE}
flights |> 
  summarize(
    max = max(dep_delay, na.rm = TRUE),
    q95 = quantile(dep_delay, 0.95, na.rm = TRUE)
  )
```

### 

Another powerful tool is quantile() which is a generalization of the median: quantile(x, 0.25) will find the value of x that is greater than 25% of the values, quantile(x, 0.5) is equivalent to the median, and quantile(x, 0.95) will find the value that’s greater than 95% of the values.

### Exercise 14

Using your previous code, within your call to the `summarize()` function, add the `.by` argument and set it to `c(year, month, day)`. 

```{r numeric-summaries-14, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-14-hint-1, eval = FALSE}
flights |> 
  summarize(
    max = max(dep_delay, na.rm = TRUE),
    q95 = quantile(dep_delay, 0.95, na.rm = TRUE),
    .by = ...
  )
```

```{r numeric-summaries-14-test, include = FALSE}
flights |> 
  summarize(
    max = max(dep_delay, na.rm = TRUE),
    q95 = quantile(dep_delay, 0.95, na.rm = TRUE),
    .by = c(year, month, day)
  )
```

### 

For the flights data, we are looking at the 95% quantile of delays rather than the maximum, because it will ignore the 5% of most delayed flights which can be quite extreme.

### Exercise 15

Create a new pipeline. Pipe `flights` to the `summarize()` function. Within the `summarize()` function, create a new variable `distance_sd` and set it to `IQR(distance)`.

```{r numeric-summaries-15, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-15-hint-1, eval = FALSE}
flights |> 
  summarize(
    distance_sd = ...
  )
```

```{r numeric-summaries-15-test, include = FALSE}
flights |> 
  summarize(
    distance_sd = IQR(distance)
  )
```

### 

Two commonly used summaries are the standard deviation, sd(x), and the inter-quartile range, `IQR()`. `IQR()` gives us the range that contains the middle 50% of the data and is calculated by subtracting `quantile(x, 0.75) - quantile(x, 0.25)`.

### Exercise 16

Using the same pipe as above, create another variable, within the call to `summarize()`, called `n` and set it to `n()`. 

```{r numeric-summaries-16, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-16-hint-1, eval = FALSE}
flights |> 
  summarize(
    distance_sd = IQR(distance),
    n = n()
  )
```

```{r numeric-summaries-16-test, include = FALSE}
flights |> 
  summarize(
    distance_sd = IQR(distance),
    n=n()
  )
```

### Exercise 17

Using your previous code, within your call to `summarize()` add the `.by` argument and set it to `c(origin, dest)`.

```{r numeric-summaries-17, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-17-hint-1, eval = FALSE}
flights |> 
  summarize(
    distance_sd = IQR(distance),
    n=n(),
    .by = ...
  )
```

```{r numeric-summaries-17-test, include = FALSE}
flights |> 
  summarize(
    distance_sd = IQR(distance),
    n=n(),
    .by = c(origin, dest)
  )
```

### Exercise 18

Using the same pipe as above, add the `filter()` function to the pipeline. Within the `filter()` function add the argument `distance_sd > 0`.

```{r numeric-summaries-18, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-18-hint-1, eval = FALSE}
flights |> 
  summarize(
    distance_sd = IQR(distance),
    n=n(),
    .by = c(origin, dest)) |> 
  filter(...)
```

```{r numeric-summaries-18-test, include = FALSE}
flights |> 
  summarize(
    distance_sd = IQR(distance),
    n=n(),
    .by = c(origin, dest)) |>
  filter(distance_sd > 0)
```

### 

We can use this to reveal a small oddity in the flights data. You might expect the spread of the distance between origin and destination to be zero, since airports are always in the same place. But the code above reveals a data oddity for airport EGE (Eagle County Regional Airport).

### Exercise 19

Describe the oddity.

```{r numeric-summaries-19}
question_text(NULL,
	message = "The distance between any two airports should be constant, obviously. Airports don't move! For some reason, the are distances between EGE, on one hand, and JFK/EWR on the other hand, are not always the same. That seems odd!",
	answer(NULL, correct = TRUE),
	allow_retry = FALSE,
	incorrect = NULL,
	rows = 6)
```

### Exercise 20

Create a new pipeline. Pipe `flights` to the `filter()` function. Within the call to `filter()`, have the argument be `dep_delay < 120`, filtering out any departure delay that is greater than 2 hours. 

```{r numeric-summaries-20, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-20-hint-1, eval = FALSE}
flights |> 
  filter(...)
```

```{r numeric-summaries-20-test, include = FALSE}
flights |> 
  filter(dep_delay < 120)
```

### Exercise 21

Using the same pipe as above, add the `ggplot()` function to the pipeline. Within the `ggplot()` function map `x` to `dep_delay` and `group` to `interaction(day, month)`
```{r numeric-summaries-21, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-21-hint-1, eval = FALSE}
flights |> 
  filter(dep_delay < 120) |> 
  ggplot(aes(x = ..., group = ...))
```

```{r numeric-summaries-21-hint-2, eval = FALSE}
flights |> 
  filter(dep_delay < 120) |> 
  ggplot(aes(x = dep_delay, group = interaction(day, month)))
```

### Exercise 22

Using the same pipe as above, add the `geom_freqpoly()` function to the pipeline. Within the `geom_freqpoly()` function set the argument `binwidth` to `5` and `alpha` to `1/5`.
```{r numeric-summaries-22, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-22-hint-1, eval = FALSE}
flights |> 
  filter(dep_delay < 120) |> 
  ggplot(aes(x = dep_delay, group = interaction(day, month))) |> 
  geom_freqpoly(...)
```

```{r numeric-summaries-22-test, include = FALSE}
flights |> 
  filter(dep_delay < 120) |>
  ggplot(aes(x=dep_delay, group = interaction(day, month))) +
  geom_freqpoly(binwidth = 5, alpha = 1/5)
```

### 

In the following plot 365 frequency polygons of dep_delay, one for each day, are overlaid. The distributions seem to follow a common pattern, suggesting it’s fine to use the same summary for each day.

### Exercise 23

Create a new pipeline. Pipe `flights` to the `summarize()` function. Within the `summarize()` function, create a new variable `first_dep` and set it to `first(dep_time, na_rm = TRUE)`.

```{r numeric-summaries-23, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-23-hint-1, eval = FALSE}
flights |> 
  summarize(
    first_dep = ...
  )
```

```{r numeric-summaries-23-test, include = FALSE}
flights |> 
  summarize(
    first_dep = first(dep_time, na_rm = TRUE))
```

### Exercise 24

Using the same pipe as above, create another variable, within the call to `summarize()`, called `fifth_dep` and set it to `nth(dep_time, 5, na_rm = TRUE)`. 

```{r numeric-summaries-24, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-24-hint-1, eval = FALSE}
flights |> 
  summarize(
    first_dep = first(dep_time, na_rm = TRUE),
    fifth_dep = ...
  )
```

```{r numeric-summaries-24-test, include = FALSE}

flights |> 
  summarize(
    first_dep = first(dep_time, na_rm = TRUE),
    fifth_dep = nth(dep_time, 5, na_rm = TRUE))
```

### Exercise 25

Using the same pipe as above, create another variable, within the call to `summarize()`, called `last_dep` and set it to `last(dep_time, na_rm = TRUE)`. 

```{r numeric-summaries-25, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-25-hint-1, eval = FALSE}
flights |> 
  summarize(
    first_dep = first(dep_time, na_rm = TRUE),
    fifth_dep = nth(dep_time, 5, na_rm = TRUE),
    last_dep = ...
  )
```

```{r numeric-summaries-25-test, include = FALSE}

flights |> 
  summarize(
    first_dep = first(dep_time, na_rm = TRUE),
    fifth_dep = nth(dep_time, 5, na_rm = TRUE),
    last_dep = last(dep_time, na_rm = TRUE))
```

### Exercise 26

Using your previous code, within your call to `summarize()` add the `.by` argument and set it to `c(year, month, day)`.

```{r numeric-summaries-26, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-26-hint-1, eval = FALSE}
flights |> 
  summarize(
    first_dep = first(dep_time, na_rm = TRUE),
    fifth_dep = nth(dep_time, 5, na_rm = TRUE),
    last_dep = last(dep_time, na_rm = TRUE),
    .by = ...
  )
```

```{r numeric-summaries-26-test, include = FALSE}
flights |> 
  summarize(
    first_dep = first(dep_time, na_rm = TRUE),
    fifth_dep = nth(dep_time, 5, na_rm = TRUE),
    last_dep = last(dep_time, na_rm = TRUE),
    .by = c(year, month, day))
```

### 

The functions `first(x)`, `last(x)`, and `nth(x, n)` extract a value at a specific position.

### Exercise 27

Create a new pipeline and pipe `flights` to the `mutate()` function to the pipeline. Within the `mutate()` function, create a new variable `y` and set it to `min_rank(sched_dep_time)`.

```{r numeric-summaries-27, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-27-hint-1, eval = FALSE}
flights |> 
  mutate(y = min_rank(...)) 
```

```{r numeric-summaries-27-test, include = FALSE}
flights |> 
  mutate(y = min_rank(sched_dep_time))
```

### Exercise 28

Using your previous code, within your call to the `mutate()` function, add another argument called `.by` and set it to `c(year, month, day)`. 

```{r numeric-summaries-28, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-28-hint-1, eval = FALSE}
flights |> 
  mutate(y = min_rank(sched_dep_time), .by = ...) 
```

```{r numeric-summaries-28-test, include = FALSE}
flights |> 
  mutate(y = min_rank(sched_dep_time),
         .by = c(year, month, day))
```

### 

As the names suggest, the summary functions are typically paired with `summarize()`. However, because of the recycling rules, they can also be usefully paired with `mutate()`

### Exercise 29

Using the same pipe as above, add the `filter()` function to the pipeline. Within the `filter()` function, add the argument `y %in% c(1, max(y))`.

```{r numeric-summaries-29, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r numeric-summaries-29-hint-1, eval = FALSE}
flights |> 
  mutate(y = min_rank(sched_dep_time), .by = c(year, month, day)) |> 
  filter(...)
```

```{r numeric-summaries-29-test, include = FALSE}

flights |> 
  mutate(y=min_rank(sched_dep_time),
         .by = c(year, month, day)) |>
  filter(y %in% c(1, max(y)))
```

### 

Extracting values at positions is complementary to filtering on ranks. Filtering gives you all variables, with each observation in a separate row.

## Summary
### 

This tutorial covered [Chapter 13: Numbers](https://r4ds.hadley.nz/numbers.html) from [*R for Data Science (2e)*](https://r4ds.hadley.nz/) by Hadley Wickham, Mine Çetinkaya-Rundel, and Garrett Grolemund. We have utilized two core packages from *Tidyverse*: [**readr**](https://readr.tidyverse.org/) and [**dplyr**](https://dplyr.tidyverse.org/). Key commands that you learnt include [`parse_double()`](https://readr.tidyverse.org/reference/parse_atomic.html) parsed numbers directly from strings, [`parse_number()`](https://readr.tidyverse.org/reference/parse_number.html) removed useless characters and parsing numbers from strings, [`count()`](https://dplyr.tidyverse.org/reference/count.html) which counted the unique values of one or more variables, [`pmin()`](https://stat.ethz.ch/R-manual/R-devel/library/base/html/Extremes.html) which take one or more vectors in and returns the minima or maxima of these vectors, 
[`round()`](https://stat.ethz.ch/R-manual/R-devel/library/base/html/Round.html) which rounds values in its first argument to the specified number of decimal places, and [`min_rank()`](https://dplyr.tidyverse.org/reference/row_number.html) which gives every tie the same value and ranks an inputted vector.



```{r download-answers, child = system.file("child_documents/download_answers.Rmd", package = "tutorial.helpers")}
```
