Data Science R Basics
Data Science R Basics
The textbook for the Data Science course series is freely available online.
Learning Objectives
• Learn to read, extract, and create datasets in R
• Learn to perform a variety of operations on datasets using R
• Learn to write your own functions/sub-routines in R
Course Overview
You will get started with R, learn about its functions and data types.
You will learn to operate on vectors and advanced functions such as sorting.
You will learn to use general programming features like ‘if-else’, and ‘for loop’ commands, and write your
own functions to perform various operations on datasets.
Section 1 Overview
Section 1 introduces you to R Basics, Functions and Datatypes.
In Section 1, you will learn to:
1
Motivation
Here is a link to the textbook section on the motivation for this course.
Getting started
• R was developed by statisticians and data analysts as an interactive environment for data analysis.
• Some of the advantages of R are that (1) it is free and open source, (2) it has the capability to save
scripts, (3) there are numerous resources for learning, and (4) it is easy for developers to share software
implementation.
• Expressions are evaluated in the R console when you type the expression into the console and hit
Return.
• A great advantage of R over point and click analysis software is that you can save your work as scripts.
• “Base R” is what you get after you first install R. Additional components are available via packages.
Installing R
To install R to work on your own computer, you can download it freely from the Comprehensive R Archive
Network (CRAN). Note that CRAN makes several versions of R available: versions for multiple operating
systems and releases older than the current one. You want to read the CRAN instructions to assure you
download the correct version. If you need further help, you read the walkthrough in this Chapter of the
textbook.
Installing RStudio
RStudio is an integrated development environment (IDE). We highly recommend installing and using RStudio
to edit and test your code. You can install RStudio through the RStudio website. Their cheatsheet is a great
resource. You must install R before installing RStudio.
Textbook Link
2
R Basics - Objects
## [1] 0.618034
## [1] -1.618034
R Basics - Functions
• In general, to evaluate a function we need to use parentheses. If we type a function without parenthesis,
R shows us the code for the function. Most functions also require an argument, that is, something to
be written inside the parenthesis.
• To access help files, we may use the help function help(“function name”), or write the question mark
followed by the function name.
• The help file shows you the arguments the function is expecting, some of which are required and some
are optional. If an argument is optional, a default value is assigned with the equal sign. The args()
function also shows the arguments a function needs.
• To specify arguments, we use the equals sign. If no argument name is used, R assumes you’re entering
arguments in the order shown in the help file.
• Creating and saving a script makes code much easier to execute.
• To make your code more readable, use intuitive variable names and include comments (using the “#”
symbol) to remind yourself why you wrote a particular line of code.
3
Assessment - R Basics
1. What is the sum of the first n positive integers? We can use the formula 𝑛(𝑛 + 1)/2 to quickly compute
this quantity.
# Here is how you compute the sum for the first 20 integers
20*(20+1)/2
## [1] 210
# However, we can define a variable to use the formula for other values of n
n <- 20
n*(n+1)/2
## [1] 210
n <- 25
n*(n+1)/2
## [1] 325
# Below, write code to calculate the sum of the first 100 integers
n<-100
n*(n+1)/2
## [1] 5050
2. What is the sum of the first 1000 positive integers? We can use the formula 𝑛(𝑛 + 1)/2 to quickly
compute this quantity.
# Below, write code to calculate the sum of the first 1000 integers
n<-1000
n*(n+1)/2
## [1] 500500
n <- 1000
x <- seq(1, n)
sum(x)
## [1] 500500
Based on the result, what do you think the functions seq and sum do?
4
□ D. sum always returns the same number.
4. In math and programming we say we evaluate a function when we replace arguments with specific
values. So if we type log2(16) we evaluate the log2 function to get the log base 2 of 16 which is 4.
In R it is often useful to evaluate a function inside another function. For example, sqrt(log2(16)) will
calculate the log to the base 2 of 16 and then compute the square root of that value. So the first evaluation
gives a 4 and this gets evaluated by sqrt to give the final answer of 2.
## [1] 4
## [1] 2
# Compute log to the base 10 (log10) of the sqrt of 100. Do not use variables.
log10(sqrt(100))
## [1] 1
5. Which of the following will always return the numeric value stored in x? You can try out examples
and use the help system in the R console.
□ A. log(10^x)
□ B. log10(x^10)
⊠ C. log(exp(x))
□ D. exp(log(x, base = 2))
Data Types
You can find the section of the textbook on data types here.
Key Points
Code
5
# loading the the murders dataset
data(murders)
## [1] "[Link]"
## [1] 51
6
# vectors can be of class numeric and character
class(pop)
## [1] "numeric"
class(murders$state)
## [1] "character"
## [1] FALSE
class(z)
## [1] "logical"
## [1] "factor"
1. We’re going to be using the following dataset for this module. Run this code in the console.
library(dslabs)
data(murders)
Next, use the function str to examine the structure of the murders object. We can see that this object is a
data frame with 51 rows and five columns.
str(murders)
7
Which of the following best describes the variables represented in this data frame?
□ A. The 51 states.
□ B. The murder rates for all 50 states and DC.
⊠ C. The state name, the abbreviation of the state name, the state’s region, and the state’s population
and total number of murders for 2010.
□ D. str shows no relevant information.
2. In the previous question, we saw the different variables that are a part of this dataset from the output
of the str() function. The function names() is specifically designed to extract the column names from
a data frame.
3. In this module we have learned that every variable has a class. For example, the class can be a
character, numeric or logical. The function class() can be used to determine the class of an object.
Here we are going to determine the class of one of the variables in the murders data frame. To extract
variables from a data frame we use $, referred to as the accessor.
# To access the population variable from the murders dataset use this code:
p <- murders$population
## [1] "numeric"
## [1] "character"
4. An important lesson you should learn early on is that there are multiple ways to do things in R. For
example, to generate the first five integers we note that 1:5 and seq(1,5) return the same result.
There are also multiple ways to access variables in a data frame. For example we can use the square brackets
[[ instead of the accessor $.
If you instead try to access a column with just one bracket,
8
murders["population"]
R returns a subset of the original data frame containing just this column. This new object will be of class
[Link] rather than a vector. To access the column itself you need to use either the $ accessor or the
double square brackets [[.
Parentheses, in contrast, are mainly used alongside functions to indicate what argument the function should
be doing something to. For example, when we did class(p) in the last question, we wanted the function
class to do something related to the argument p.
This is an example of how R can be a bit idiosyncratic sometimes. It is very common to find it confusing at
first.
## [1] TRUE
## [1] TRUE
5. Using the str() command, we saw that the region column stores a factor. You can corroborate this
by using the class command on the region column.
## [1] "factor"
## [1] 4
6. The function table takes a vector as input and returns the frequency of each unique element in the
vector.
9
# Here is an example of what the table function does
x <- c("a", "a", "b", "b", "b", "c")
table(x)
## x
## a b c
## 2 3 1
# Write one line of code to show the number of states per region
table(murders$region)
##
## Northeast South North Central West
## 9 17 12 13
Section 1 Assessment
1. To find the solutions to an equation of the format 𝑎𝑥2 + 𝑏𝑥 + 𝑐, use the quadratic equation: 𝑥 =
−𝑏±√(𝑏2 −4𝑎𝑐)
2𝑎 .
What are the two solutions to 2𝑥2 − 𝑥 − 4 = 0? Use the quadratic equation. (Report the greater of the two
solutions first, using 3 significant digits for both solutions)
options(digits = 3)
a <- 2
b <- -1
c <- -4
(-b+sqrt(b^2-4*a*c))/(2*a)
## [1] 1.69
(-b-sqrt(b^2-4*a*c))/(2*a)
## [1] -1.19
2. Use R to compute log base 4 of 1024. You can use the help function to learn how to use arguments to
change the base of the log function.
log(1024, base = 4)
## [1] 5
data(movielens)
str(movielens)
10
## '[Link]': 100004 obs. of 7 variables:
## $ movieId : int 31 1029 1061 1129 1172 1263 1287 1293 1339 1343 ...
## $ title : chr "Dangerous Minds" "Dumbo" "Sleepers" "Escape from New York" ...
## $ year : int 1995 1941 1996 1981 1989 1978 1959 1982 1992 1991 ...
## $ genres : Factor w/ 901 levels "(no genres listed)",..: 762 510 899 120 762 836 81 762 844 899 ..
## $ userId : int 1 1 1 1 1 1 1 1 1 1 ...
## $ rating : num 2.5 3 3 2 4 2 2 2 3.5 2 ...
## $ timestamp: int 1260759144 1260759179 1260759182 1260759185 1260759205 1260759151 1260759187 12607
4. We already know we can use the levels() function to determine the levels of a factor. A different
function, nlevels(), may be used to determine the number of levels of a factor.
Use this function to determine how many levels are in the factor genres in the movielens data frame.
nlevels(movielens$genres)
## [1] 901
Section 2 Overview
In Section 2.1, you will:
11
In Section 2.2, you will:
Vectors
• The function c(), which stands for concatenate, is useful for creating vectors.
• Another useful function for creating vectors is the seq() function, which generates sequences.
• Subsetting lets us access specific parts of a vector by using square brackets to access elements of a
vector.
Code
# We may create vectors of class numeric or character with the concatenate function
codes <- c(380, 124, 818)
country <- c("italy", "canada", "egypt")
# We can also name the elements of a numeric vector using the names() function
codes <- c(380, 124, 818)
country <- c("italy","canada","egypt")
names(codes) <- country
# Using square brackets is useful for subsetting to access specific elements of a vector
codes[2]
## canada
## 124
codes[c(1,3)]
## italy egypt
## 380 818
12
codes[1:2]
## italy canada
## 380 124
# If the entries of a vector are named, they may be accessed by referring to their name
codes["canada"]
## canada
## 124
codes[c("egypt","italy")]
## egypt italy
## 818 380
• In general, coercion is an attempt by R to be flexible with data types by guessing what was meant
when an entry does not match the expected. For example, when defining x as
Assessment - Vectors
1. A vector is a series of values, all of the same type. They are the most basic data type in R and can
hold numeric data, character data, or logical data. In R, you can create a vector with the concatenate
(or combine) function c()
You place the vector elements separated by a comma between the parentheses. For example a numeric vector
would look something like this:
# Create a numeric vector to store the temperatures listed in the instructions into a vector named temp
# Make sure to follow the same order in the instructions
temp <- c("Beijing"=35, "Lagos"=88, "Paris"=42, "Rio de Janeiro"=84, "San Juan"=81, "Toronto"=30)
cost
13
## [1] 50 75 90 100 150
temp
class(temp)
## [1] "numeric"
2. As in the previous question, we are going to create a vector. Only this time, we learn to create character
vectors. The main difference is that these have to be written as strings and so the names are enclosed
within double quotes.
3. We have successfully assigned the temperatures as numeric values to temp and the city names as
character values to city. But can we associate the temperature to its related city? Yes! We can do so
using a code we already know - names. We assign names to the numeric values.
14
## Beijing Lagos Paris Rio de Janeiro San Juan
## 35 88 42 84 81
## Toronto
## 30
4. If we want to display only selected values from the object, R can help us do that easily.
For example, if we want to see the cost of the last 3 items in our food list, we would type:
cost[3:5]
Note here, that we could also type cost[c(3,4,5)] and get the same result. The : operator helps us
condense the code and get consecutive values.
5. In the previous question, we accessed the temperature for consecutive cities (1st three). But what if
we want to access the temperatures for any 2 specific cities?
An example: To access the cost of pizza (1st) and pasta (5th food item) in our list, the code would be:
cost[c(1,5)]
# Access the cost of pizza and pasta from our food list
cost[c(1,5)]
## pizza pasta
## 50 150
# Define temp
temp <- c(35, 88, 42, 84, 81, 30)
city <- c("Beijing", "Lagos", "Paris", "Rio de Janeiro", "San Juan", "Toronto")
names(temp) <- city
15
6. The : operator helps us create sequences of numbers. For example, 32:99 would create a list of
numbers from 32 to 99.
Then, if we want to know the length of this sequence, all we need to do is use the length command.
## [1] 68
## [1] 62
7. We can also create different types of sequences in R. For example, in seq(7, 49, 7), the first argument
defines the start, and the second the end. The default is to go up in increments of 1, but a third
argument lets us tell it by what interval.
## [1] 7 14 21 28 35 42 49
# Create a vector containing all the positive odd numbers smaller than 100.
# The numbers should be in ascending order
seq(1,99,2)
## [1] 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49
## [26] 51 53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97 99
8. The second argument of the function seq is actually a maximum, not necessarily the end.
So if we type
seq(7, 50, 7)
seq(7, 49, 7)
This can be useful because sometimes all we want are sequential numbers that are smaller than some value.
Let’s look at an example.
16
# We can create a vector with the multiples of 7, smaller than 50 like this
seq(7, 49, 7)
## [1] 7 14 21 28 35 42 49
# But note that the second argument does not need to be the last number
# It simply determines the maximum value permitted
# so the following line of code produces the same vector as seq(7, 49, 7)
seq(7, 50, 7)
## [1] 7 14 21 28 35 42 49
# Create a sequence of numbers from 6 to 55, with 4/7 increments and determine its length
length(seq(6,55,4/7))
## [1] 86
9. The seq() function has another useful argument. The argument [Link]. This argument lets us
generate sequences that are increasing by the same amount but are of the prespecified length.
Let’s create a vector and see what is the class of the object produced.
## [1] "numeric"
10. We have discussed the numeric class. We just saw that the seq function can generate objects of this
class.
into the console and note that the class is numeric. R has another type of vector we have not described,
the integer class. You can create an integer by adding the letter L after a whole number. If you type
class(3L)
in the console, you see this is an integer and not a numeric. For most practical purposes, integers and
numerics are indistinguishable. For example 3, the integer, minus 3 the numeric is 0. To see this type this
in the console
17
3L - 3
The main difference is that integers occupy less space in the computer memory, so for big computations
using integers can have a substantial impact.
## [1] "integer"
## [1] "numeric"
## [1] "integer"
12. The concept of coercion is a very important one. Watching the video, we learned that when an entry
does not match what an R function is expecting, R tries to guess what we meant before throwing an
error. This might get confusing at times.
As we’ve discussed in earlier questions, there are numeric and character vectors. The character vectors are
placed in quotes and the numerics are not.
We can avoid issues with coercion in R by changing characters to numerics and vice-versa. This is known as
typecasting. The code, [Link](x) helps us convert character strings to numbers. There is an equivalent
function that converts its argument to a string, [Link](x).
Let’s practice doing this!
18
x
## [1] 1 3 5 NA
Sorting
The textbook for this section is available here.
Key Points
Assessment - Sorting
1. When looking at a dataset, we may want to sort the data in an order that makes more sense for
analysis. Let’s learn to do this using the murders dataset as an example
## [1] "Alabama"
## [1] 563626
2. The function order() returns the index vector needed to sort the vector. This implies that sort(x)
and x[order(x)] give the same result.
This can be useful for finding row numbers with certain properties such as “the row for the state with
the smallest population”. Remember that when we extract a variable from a data frame the order of the
resulting vector is the same as the order of the rows of the data frame. So for example, the entries of the
vector murders$state are ordered in the same way as the states if you go down the rows of murders.
19
# Access population from the dataset and store it in pop
pop <- murders$population
# Use the command order to find the vector of indexes that order pop and store in object ord
ord <- order(pop)
# Find the index number of the entry with the smallest population size
ord[1]
## [1] 51
3. We can actually perform the same operation as in the previous exercise using the function [Link].
It basically tells us which is the minimum value.
## [1] 46
## [1] 51
4. Now we know how small the smallest state is and we know which row represents it. However, which
state is it?
# Use the index you just defined to find the state with the smallest population
states[i]
## [1] "Wyoming"
20
# Store temperatures in an object
temp <- c(35, 88, 42, 84, 81, 30)
# Create a data frame my_df with the state name and its rank
my_df <- [Link](name = states, rank = ranks)
6. This exercise is somewhat more challenging. We are going to repeat the previous exercise but this time
order ‘my_df so that the states are ordered from least populous to most.
# Define a variable states to be the state names from the murders data frame
states <- murders$state
# Define a variable ind to store the indexes needed to order the population values
ind <- order(murders$population)
# Create a data frame my_df with the state name and its rank and ordered from least populous to most
my_df <- [Link](states = states[ind], ranks = ranks[ind])
7. The na_example dataset represents a series of counts. It is included in the dslabs package.
library(dslabs)
data(na_example)
str(na_example)
However, when we compute the average we obtain an NA. You can see this by typing
mean(na_example)
21
## int [1:1000] 2 1 3 2 1 3 1 4 3 2 ...
## [1] NA
# Use [Link] to create a logical index ind that tells which entries are NA
ind <- [Link](na_example)
## [1] 145
8. We previously computed the average of na_example using mean(na_example) and obtain NA. This is
because the function mean returns NA if it encounters at least one NA. A common operation is therefore
removing the entries that are NA and after that perform operations on the rest.
## [1] 1 3
## [1] NA
## [1] 2.3
Vector arithmetic
Code
22
# The name of the state with the maximum population is found by doing the following
murders$state[[Link](murders$population)]
## [1] "California"
{r, eval=FALSE, echo=TRUE temp <- c(35, 88, 42, 84, 81, 30) city <- c("Beijing", "Lagos",
"Paris", "Rio de Janeiro", "San Juan", "Toronto") city_temps <- [Link](name = city,
temperature = temp)
# Convert temperature into Celsius and overwrite the original values of 'temp' with these Celsius values
temp <- 5/9 * (temp -32)
2. We can use some of what we have learned to perform calculations that would otherwise be quite
complicated. Let’s see an example.
23
# Define an object `x` with the numbers 1 through 100
x <- seq(1,100)
## [1] 1.63
3. Compute the per 100,000 murder rate for each state and store it in the object murder_rate. Then
compute the average murder rate for the US using the function mean. What is the average?
# Store the per 100,000 murder rate for each state in murder_rate
murder_rate <- murders$total / murders$population * 100000
## [1] 2.78
Section 2 Assessment
Match the following outputs to the function which produces that output. Options include sort(x),
order(x), rank(x) and none of these.
## [1] 2 18 27 43 96
order(x)
## [1] 1 5 3 2 4
rank(x)
## [1] 1 4 3 5 2
1, 2, 3, 4, 5 none of these
1, 5, 3, 2, 4 order(x)
1, 4, 3, 5, 2 rank(x)
2, 18, 27, 43, 96 sort(x)
2. Continue working with the vector x <- c(2, 43, 27, 96, 18).
24
x <- c(2, 43, 27, 96, 18)
min(x)
## [1] 2
[Link](x)
## [1] 1
max(x)
## [1] 96
[Link](x)
## [1] 4
min(x) 2
[Link](x) 1
max(x) none of these
[Link](x) 4
3. Mandi, Amy, Nicole, and Olivia all ran different distances in different time intervals. Their distances
(in miles) and times (in minutes) are as follows:
Write a line of code to convert time to hours. Remember there are 60 minutes in an hour. Then write a line
of code to calculate the speed of each runner in miles per hour. Speed is distance divided by time.
How many hours did Olivia run?
## [1] 0.833
## [1] 4.8
25
name[[Link](speed)]
## [1] "Amy"
Section 3 Overview
Section 3 introduces to the R commands and techniques that help you wrangle, analyze, and visualize data.
In Section 3.1, you will:
Indexing
Code
26
# calculating how many states have a murder rate less than or equal to 0.71
sum(index)
## [1] 5
• The function which() gives us the entries of a logical vector that are true.
• The function match() looks for entries in a vector and returns the index needed to access them.
• We use the function %in% if we want to know whether or not each element of a first vector is in a
second vector.
Code
## [1] 1.8
# to obtain the indices and subsequent murder rates of New York, Florida, Texas, we do:
ind <- match(c("New York", "Florida", "Texas"), murders$state)
ind
## [1] 33 10 44
murder_rate[ind]
27
Assessment - Indexing
1. Here we will be using logical operators to create a logical vector. Compute the per 100,000 murder
rate for each state and store it in an object called murder_rate. Then use logical operators to create
a logical vector named low that tells us which entries of murder_rate are lower than 1.
# Store the murder rate per 100,000 for each state, in `murder_rate`
murder_rate <- murders$total / murders$population * 100000
2. The function ‘which() helps us know directly, which values are low or high, etc. Let’s use it in this
question.
# Store the murder rate per 100,000 for each state, in murder_rate
murder_rate <- murders$total/murders$population*100000
## [1] 12 13 16 20 24 30 35 38 42 45 46 51
3. Note that if we want to know which entries of a vector are lower than a particular value we can use
code like this.
The code above shows us the states with populations smaller than one million.
# Store the murder rate per 100,000 for each state, in murder_rate
murder_rate <- murders$total/murders$population*100000
4. Now we will extend the code from the previous exercises to report the states in the Northeast with a
murder rate lower than 1.
28
# Store the murder rate per 100,000 for each state, in `murder_rate`
murder_rate <- murders$total/murders$population*100000
# Create a vector ind for states in the Northeast and with murder rates lower than 1.
northeast <- murders$region == "Northeast"
ind <- low & northeast
5. In a previous exercise we computed the murder rate for each state and the average of these numbers.
How many states are below the average?
# Store the murder rate per 100,000 for each state, in murder_rate
murder_rate <- murders$total/murders$population*100000
# Compute the average murder rate using `mean` and store it in object named `avg`
avg <- mean(murder_rate)
# How many states have murder rates below avg ? Check using sum
ind <- murder_rate < avg
sum(ind)
## [1] 27
6. In this exercise we use the match function to identify the states with abbreviations AK, MI, and IA.
# Store the 3 abbreviations in a vector called `abbs` (remember that they are character vectors and need
abbs <- c("AK", "MI", "IA")
7. If rather than an index we want a logical that tells us whether or not each element of a first vector is
in a second, we can use the function %in%.
For example:
29
x <- c(2, 3, 5)
y <- c(1, 2, 3, 4)
x%in%y
Gives us two TRUE followed by a FALSE because 2 and 3 are in y but 5 is not.
# Store the 5 abbreviations in `abbs`. (remember that they are character vectors)
abbs <- c("MA", "ME", "MI", "MO", "MU")
# Use the %in% command to check if the entries of abbs are abbreviations in the the murders data frame
abbs%in%murders$abb
8. In a previous exercise we computed the index abbs%in%murders$abb. Based on that, and using the
which function and the ! operator, get the index of the entries of abbs that are not abbreviations.
# Store the 5 abbreviations in abbs. (remember that they are character vectors)
abbs <- c("MA", "ME", "MI", "MO", "MU")
# Use the `which` command and `!` operator to find out which index abbreviations are not actually part o
ind <- which(!abbs%in%murders$abb)
## [1] "MU"
• To change a data table by adding a new column, or changing an existing one, we use the mutate
function.
• To filter the data by subsetting rows, we use the function filter.
• To subset the data by selecting specific columns, we use the select function.
• We can perform a series of operations by sending the results of one function to another function using
what is called the pipe operator, %>%.
Code
30
##
## Attaching package: 'dplyr'
library(dplyr)
Key Points
Code
31
# creating a data frame with stringAsFactors = FALSE
grades <- [Link](names = c("John", "Juan", "Jean", "Yao"),
exam_1 = c(95, 80, 90, 85),
exam_2 = c(90, 85, 85, 90),
stringsAsFactors = FALSE)
This function is aware of the column names and inside the function you can call them unquoted. Like this:
Note that we can write population rather than murders$population. The function mutate knows we are
grabing columns from murders.
# Redefine murders so that it includes a column named rate with the per 100,000 murder rates
murders <- mutate(murders, rate = total / population * 100000)
2. Note that if rank(x) gives you the ranks of x from lowest to highest, rank(-x) gives you the ranks
from highest to lowest.
# Note that if you want ranks from highest to lowest you can take the negative and then compute the rank
x <- c(88, 100, 83, 92, 94)
rank(-x)
## [1] 4 1 5 3 2
# Defining rate
rate <- murders$total/ murders$population * 100000
3. With dplyr we can use select to show only certain columns. For example with this code we would
only show the states and population sizes:
# Use select to only show state names and abbreviations from murders
select(murders, state, abb)
## state abb
## 1 Alabama AL
## 2 Alaska AK
## 3 Arizona AZ
## 4 Arkansas AR
32
## 5 California CA
## 6 Colorado CO
## 7 Connecticut CT
## 8 Delaware DE
## 9 District of Columbia DC
## 10 Florida FL
## 11 Georgia GA
## 12 Hawaii HI
## 13 Idaho ID
## 14 Illinois IL
## 15 Indiana IN
## 16 Iowa IA
## 17 Kansas KS
## 18 Kentucky KY
## 19 Louisiana LA
## 20 Maine ME
## 21 Maryland MD
## 22 Massachusetts MA
## 23 Michigan MI
## 24 Minnesota MN
## 25 Mississippi MS
## 26 Missouri MO
## 27 Montana MT
## 28 Nebraska NE
## 29 Nevada NV
## 30 New Hampshire NH
## 31 New Jersey NJ
## 32 New Mexico NM
## 33 New York NY
## 34 North Carolina NC
## 35 North Dakota ND
## 36 Ohio OH
## 37 Oklahoma OK
## 38 Oregon OR
## 39 Pennsylvania PA
## 40 Rhode Island RI
## 41 South Carolina SC
## 42 South Dakota SD
## 43 Tennessee TN
## 44 Texas TX
## 45 Utah UT
## 46 Vermont VT
## 47 Virginia VA
## 48 Washington WA
## 49 West Virginia WV
## 50 Wisconsin WI
## 51 Wyoming WY
4. The dplyr function filter is used to choose specific rows of the data frame to keep. Unlike select
which is for columns, filter is for rows.
For example you can show just the New York row like this:
33
filter(murders, state == "New York")
# Filter to show the top 5 states with the highest murder rates
filter(murders, rank <= 5)
## [1] 34
For example you can see the data from New York and Texas like this:
# Create a new data frame called murders_nw with only the states from the northeast and the west
murders_nw <- filter(murders, region %in% c("Northeast", "West"))
## [1] 22
7. Suppose you want to live in the Northeast or West and want the murder rate to be less than 1.
We want to see the data for the states satisfying these options. Note that you can use logical operators with
filter:
34
filter(murders, population < 5000000 & region == “Northeast”)
# Use select to show only the state name, the murder rate and the rank
select(my_states, state, rate, rank)
8. The pipe %>% can be used to perform operations sequentially without having to define intermediate
objects.
library(dplyr)
murders <- mutate(murders, rate = total / population * 100000, rank = (-rate))
# Created a table
my_states <- filter(murders, region %in% c(“Northeast”, “West”) & rate < 1)
# Used select to show only the state name, the murder rate and the rank
select(my_states, state, rate, rank)
The pipe %>% permits us to perform both operation sequentially and without having to define an intermediate
variable my_states
For example we could have mutated and selected in the same line like this:
mutate(murders, rate = total / population * 100000, rank = (-rate)) %>% select(state, rate, rank)
Note that select no longer has a data frame as the first argument. The first argument is assumed to be the
result of the operation conducted right before the %>%
# show the result and only include the state, rate, and rank columns, all in one line
filter(murders, region %in% c("Northeast", "West") & rate < 1) %>% select(state, rate, rank)
35
## state rate rank
## 1 Hawaii 0.515 49
## 2 Idaho 0.766 46
## 3 Maine 0.828 44
## 4 New Hampshire 0.380 50
## 5 Oregon 0.940 42
## 6 Utah 0.796 45
## 7 Vermont 0.320 51
## 8 Wyoming 0.887 43
# Create new data frame called my_states (with specifications in the instructions)
my_states <- murders %>% mutate(rate = total / population * 100000, rank = rank(-rate)) %>% filter(regi
Basic Plots
Code
36
1000
600
y
200
0
0 10 20 30
x
# a histogram of murder rates
hist(rate)
Histogram of rate
20
15
Frequency
10
5
0
0 5 10 15
rate
# boxplots of murder rates by region
boxplot(rate~region, data = murders)
37
15
10
rate
5
0
region
1. We made a plot of total murders versus population and noted a strong relationship: not surprisingly,
states with larger populations had more murders.
You can run the code in the console to get the plot.
library(dslabs)
data(murders)
plot(population_in_millions, total_gun_murders)
Note that many states have populations below 5 million and are bunched up in the plot. We may gain
further insights from making this plot in the log scale.
plot(population_in_millions, total_gun_murders)
38
1000
total_gun_murders
600
200
0
0 10 20 30
population_in_millions
# Transform population using the log10 transformation and save to object log10_population
log10_population <- log10(murders$population)
# Transform total gun murders using log10 transformation and save to object log10_total_gun_murders
log10_total_gun_murders <- log10(total_gun_murders)
# Create a scatterplot with the log scale transformed population and murders
plot(log10_population, log10_total_gun_murders)
3.0
log10_total_gun_murders
2.5
2.0
1.5
1.0
0.5
log10_population
39
# Store the population in millions and save to population_in_millions
population_in_millions <- murders$population/10^6
Histogram of population_in_millions
30
25
20
Frequency
15
10
5
0
0 10 20 30 40
population_in_millions
3. Now we are going to make boxplots. Boxplots are useful when we want a summary of several variables
or several strata of the same variables. Making too many histograms can become too cumbersome.
40
3e+07
population
2e+07
1e+07
0e+00
region
Section 3 Assessment
data(heights)
options(digits = 3) # report 3 significant digits for all answers
1. First, determine the average height in this dataset. Then create a logical vector ind with the indices
for those individuals who are above average height.
## [1] 532
2. How many individuals in the dataset are above average height and are female?
## [1] 31
3. If you use mean on a logical (TRUE/FALSE) vector, it returns the proportion of observations that are
TRUE.
41
mean(heights$sex == "Female")
## [1] 0.227
4. This question takes you through three steps to determine the sex of the individual with the minimum
height.
min(heights$height)
## [1] 50
Use the match() function to determine the index of the individual with the minimum height.
match(50,heights$height)
## [1] 1032
Subset the sex column of the dataset by the index above to determine the individual’s sex. Male
heights$sex[1032]
## [1] Male
## Levels: Female Male
5. This question takes you through three steps to determine how many of the integer height values between
the minimum and maximum heights are not actual heights of individuals in the heights dataset.
max(heights$height)
## [1] 82.7
Which integer values are between the maximum and minimum heights? For example, if the minimum height
is 10.2 and the maximum height is 20.8, your answer should be x <- 11:20 to capture the integers in between
those values. (If either the maximum or minimum height are integers, include those values too.)
Write code to create a vector x that includes the integers between the minimum and maximum heights.
x <- 50:82
## [1] 3
6. Using the heights dataset, create a new column of heights in centimeters named ht_cm. Recall that
1 inch = 2.54 centimeters. Save the resulting dataset as heights2.
42
heights2 <- mutate(heights, ht_cm = height*2.54)
heights2$ht_cm[18]
## [1] 163
mean(heights2$ht_cm)
## [1] 174
Create a data frame females by filtering the heights2 data to contain only female individuals.
How many females are in the heights2 dataset?
## [1] 238
mean(females$ht_cm)
## [1] 165
8. The olive dataset in dslabs contains composition in percentage of eight fatty acids found in the lipid
fraction of 572 Italian olive oils:
data(olive)
head(olive)
Plot the percent palmitic acid versus palmitoleic acid in a scatterplot. What relationship do you see?
43
plot(olive$palmitic, olive$palmitoleic)
2.5
olive$palmitoleic
2.0
1.5
1.0
0.5
6 8 10 12 14 16
olive$palmitic
9. Create a histogram of the percentage of eicosenoic acid in olive. Which of the following is true?
hist(olive$eicosenoic)
44
Histogram of olive$eicosenoic
250
200
Frequency
150
100
50
0
olive$eicosenoic
10. Make a boxplot of palmitic acid percentage in olive with separate distributions for each region.
45
16
14
palmitic
12
10
8
6
region
Which region has the highest median palmitic acid percentage? Southern Italy
Which region has the most variable palmitic acid percentage? Southern Italy
Section 4 Overview
Section 4 introduces you to general programming features like ‘if-else’, and ‘for loop’ commands so that you
can write your own functions to perform various operations on datasets.
In Section 4.1, you will:
46
Basic Conditionals
• The most common conditional expression in programming is an if-else statement, which has the form
“if [condition], perform [expression], else perform [alternative expression]”.
• The ifelse() function works similarly to an if-else statement, but it is particularly useful since it works
on vectors by examining each element of the vector and returning a corresponding answer accordingly.
• The any() function takes a vector of logicals and returns true if any of the entries are true.
• The all() function takes a vector of logicals and returns true if all of the entries are true.
Code
# an example that tells us which states, if any, have a murder rate less than 0.5
library(dslabs)
data(murders)
murder_rate <- murders$total / murders$population*100000
ind <- [Link](murder_rate)
if(murder_rate[ind] < 0.5){
print(murders$state[ind])
} else{
print("No state has murder rate that low")
}
## [1] "Vermont"
## [1] NA
47
# the ifelse() function is particularly useful on vectors
a <- c(0,1,2,-4,5)
result <- ifelse(a > 0, 1/a, NA)
## [1] 0
## [1] TRUE
all(z)
## [1] FALSE
Functions
• The R function, called function() tells R you are about to define a new function.
• Functions are objects, so must be assigned a variable name with the arrow operator.
• The general way to define functions is: (1) decide the function name, which will be an object, (2) type
function() with your function’s arguments in parentheses, (3) write all the operations inside brackets.
• Variables defined inside a function are not saved in the workspace.
Code
# we see that the above function and the pre-built R mean() function are identical
x <- 1:100
identical(mean(x), avg(x))
## [1] TRUE
48
# variables inside a function are not defined in the workspace
s <- 3
avg(1:10)
## [1] 5.5
## [1] 3
For Loops
The textbook for this section is available here.
Key points
• For-loops perform the same task over and over while changing the variable. They let us define the
range that our variable takes, and then changes the value with each loop and evaluates the expression
every time inside the loop.
• The general form of a for-loop is: “For i in [some range], do operations”. This i changes across the
range of values and the operations assume i is a value you’re interested in computing on.
• At the end of the loop, the value of i is the last value of the range.
Code
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
49
# a for-loop for our summation
m <- 25
s_n <- vector(length = m) # create an empty vector
for(n in 1:m){
s_n[n] <- compute_s_n(n)
}
## s_n formula
## 1 1 1
## 2 3 3
## 3 6 6
## 4 10 10
## 5 15 15
## 6 21 21
50 100
0
5 10 15 20 25
50
x <- c(1,2,-3,4)
if(all(x>0)){
print("All Postives")
} else{
print("Not all positives")
}
□ A. All Positives
⊠ B. Not All Positives
□ C. N/A
□ D. None of the above
2. Which of the following expressions is always FALSE when at least one entry of a logical vector x is
TRUE?
□ A. all(x)
□ B. any(x)
□ C. any(!x)
⊠ D. all(!x)
3. The function nchar tells you how many characters long a character vector is.
For example:
The function ifelse is useful because you convert a vector of logicals into something else. For example,
some datasets use the number -999 to denote NA. A bad practice! You can convert the -999 in a vector to
NA using the following ifelse call:
# Assign the state abbreviation when the state name is longer than 8 characters
char_len <- nchar(murders$state)
new_names <- ifelse(char_len > 8, murders$abb, murders$state)
4. You will encounter situations in which the function you need does not already exist. R permits you to
write your own.
Let’s practice one such situation, in which you first need to define the function to be used. The functions
you define can have multiple arguments as well as default values.
To define functions we use function. For example the following function adds 1 to the number it receives
as an argument:
51
{r, eval=FALSE, echo=TRUE my_func <- function(x){ y <- x + 1 y }
The last value in the function, in this case that stored in y, gets returned.
If you run the code above R does not show anything. This means you defined the function. You can test it
out like this:
my_func(5)
## [1] 12502500
5. We will make another function for this exercise. We will define a function altman_plot that takes two
arguments x and y and plots the difference y-x in the y-axis against the sum x+y in the x-axis.
You can define functions with as many variables as you want. For example, here we need at least two, x and
y. The following function plots log transformed values:
# Create `altman_plot`
altman_plot <- function(x, y) {
plot(x+y, y-x)
}
6. Lexical scoping is a convention used by many languages that determine when an object is available by
its name.
When you run the code below you will see which x is available at different points in the code.
x <- 8
my_func <- function(y){
x <- 9
print(x)
y + x
}
my_func(x)
print(x)
Note that when we define x as 9, this is inside the function, but it is 8 after you run the function. The x
changed inside the function but not outside.
52
# Run this code
x <- 3
my_func <- function(y){
x <- 5
y+5
}
## [1] 5
print(x)
## [1] 3
7. In the next exercise we are going to write a for-loop. In that for-loop we are going to call a function.
We define that function here.
## [1] 5050
# Write a function compute_s_n with argument n that for any given n computes the sum of 1 + 2^2 + ...+ n
compute_s_n <- function(n){
x <- 1:n
sum(x^2)
}
## [1] 385
8. Now we are going to compute the sum of the squares for several values of n. We will use a for-loop for
this.
53
results <- vector("numeric", 10)
n <- 10
for(i in 1:n){
x <- 1:i
results[i] <- sum(x)
}
Note that we start with a call to vector which constructs an empty vector that we will fill while the loop
runs.
54
5000
3000
s_n
1000
0
5 10 15 20 25
10. Now let’s actually check if we get the exact same answer.
## [1] TRUE
Section 4 Assessment
library(dslabs)
data(heights)
Write an ifelse statement that returns 1 if the sex is Female and 2 if the sex is Male.
55
What is the sum of the resulting vector?
## [1] 1862
2. Write an ifelse statement that takes the height column and returns the height if it is greater than
72 inches and returns 0 otherwise.
## [1] 9.65
3. Write a function inches_to_ft that takes a number of inches x and returns the number of feet. One
foot equals 12 inches.
What is inches_to_ft(144)?
## [1] 12
How many individuals in the heights dataset have a height less than 5 feet?
sum(inches_to_ft(heights$height) < 5)
## [1] 20
5. Given an integer x, the factorial of x is called x! and is the product of all integers up to and including
x. The factorial() function computes factorials in R. For example, factorial(4) returns 4! =
4 × 3 × 2 × 1 = 24.
Complete the code below to generate a vector of length m where the first entry is 1!, the second entry is 2!,
and so on up to m!.
56
# define a vector of length m
m <- 10
f_n <- vector(length = m)
# inspect f_n
f_n
□ A. function(n)
□ B. if(n < m)
⊠ C. for(n in 1:m)
□ D. function(m,n)
□ E. if(m < n)
□ F. for(m in 1:n)
57
Ranking in R can be computed using the rank function, which by default ranks in ascending order (lowest to highest). To rank values from highest to lowest, you take the negative of the values you wish to rank before applying the rank function, as demonstrated with the calculation rank(-rate) to assign ranks to murder rates from highest to lowest .
Lexical scoping in R determines the availability and binding of variables based on the location at which they are defined, rather than where they are called. Within a function, variables defined are local to that function and do not alter variables of the same name in the global environment. This is illustrated by the fact that defining x inside a function does not affect x outside the function, ensuring encapsulated and conflict-free operations .
The ifelse() function enhances data cleaning efforts by replacing specified erroneous values with desirable alternatives. For example, it can substitute a placeholder value like -999 used to denote missing data with NA, ensuring cleaner and more accurate datasets. This function processes each element of a vector, applying conditional logic to ensure data integrity .
The pipe (%>%) operator in dplyr is significant because it allows for sequential operations without creating intermediate data objects. This operator enhances code readability and efficiency by passing the result of one operation directly as the input to the next function in the sequence, eliminating the need for temporary variables. For example, it can be used to perform mutations and selections in a single, streamlined expression .
A custom function in R can be defined using the function() keyword, encapsulating operations within curly braces, and specifying necessary arguments. For instance, a function named sum_n is defined to compute the sum of integers from 1 to n: sum_n <- function(n) { x <- 1:n sum(x) }, allowing for flexible computation of series sums with different n values .
In R, loop constructs facilitate the computation of mathematical series by iterating over a defined range of values. A for-loop can be employed to systematically compute sums or products for a sequence of numbers. For example, the summation of squares can be computed by iterating each value, calculating the square, and accumulating the results in a vector, thereby systematically handling series calculations .
Histograms and boxplots are pivotal in data visualization for summarizing distribution characteristics succinctly. Histograms provide a graphical representation of data distribution over continuous intervals, allowing insights into data shape, spread, and central tendency. Boxplots, on the other hand, offer a compact summary useful for comparing distributions across different groups, highlighting medians, quartiles, and outliers. These plots collectively yield deeper insights into dataset properties and variability .
The mutate function in R is used to add new variables or transform existing variables in a data frame. For instance, in the context given, it was used to compute a new column 'rate' representing the murder rate per 100,000 people, by dividing 'total' murders by 'population' and multiplying by 100,000. Additionally, a 'rank' column is created by ranking the 'rate' in descending order using rank(-rate).
The filter function in R is highly effective for selecting specific data subsets based on logical conditions. It operates row-wise to retain only those entries that meet the specified criteria, like retaining rows where 'rank' is less than or equal to 5 to filter out the top 5 states with the highest murder rates. This function enhances analysis precision by allowing focus on relevant data subsets .
Logical vectors can be used in R functions to create conditional outputs by employing functions like ifelse(), which evaluates conditions across each element of a vector. For example, using ifelse(a > 0, 1/a, NA), where a is a vector, returns 1/a for positive values of a and NA otherwise. This allows for efficient element-wise application of conditional logic .