In the previous lab, we introduced logical values as one of the basic types of data in R. Logical values can take one of two values: TRUE or FALSE. We can create logical values by asking R whether some condition is true.
For example, the comparison operators <,
>, <=, >=,
==, and != can be used to compare values:
5 > 3
## [1] TRUE
5 == 3
## [1] FALSE
5 != 3
## [1] TRUE
Each of these expressions asks R a question and returns
either TRUE or FALSE. The ==
operator asks whether two values are equal, while != asks
whether they are not equal. Notice that == is different
from the assignment operators <- and = that
we used in the previous lab.
Logical conditions become especially useful when applied to vectors.
Because many operations in R are vectorized,
comparing a vector to a value performs the comparison separately for
every element of the vector:
x = c(2, 5, 8, 11, 14)
x > 7
## [1] FALSE FALSE TRUE TRUE TRUE
The result is another vector containing TRUE or
FALSE for each element of x. This is called a
logical vector. Each value tells us whether the corresponding element of
x satisfies the condition x > 7.
The remainder is to be worked through by you.
A few logical operators you should know of are:
Operator | Description
== | equal to
!= | not equal to
> | greater than
>= | greater than or equal to
< | less than
<= | less than or equal to
&| and
| | or
! | negation (“not”)
Question 1: Using the vector scores
below complete the following parts:
scores = c(4, 7, 9, 3, 7, 10, 6)
A. Write a logical condition that determines which values are
greater than 6. How many values in the scores vector
satisfy the condition? (count the TRUE’s)
B. Write a logical condition that determines which values are less than or equal to 7.
C. Write a logical condition that determines which values are exactly equal to 7.
D. Write a logical condition that determines which values are not equal to 7.
E. Pick one of B-D. In plain text, briefly describe what each
TRUE and FALSE in that resulting vector
represents.
On its own, a vector of TRUE’s and FALSE’s
is not always the most helpful format for identifying the actual values
that meet a condition. These next examples will continue to use the
scores vector.
scores = c(4, 7, 9, 3, 7, 10, 6)
# Returns a logical vector using the condition "> 6"
scores > 6
## [1] FALSE TRUE TRUE FALSE TRUE TRUE FALSE
The which() function can be used to identify the indices
(positions) where a logical condition evaluates to TRUE:
which(scores > 6)
## [1] 2 3 5 6
These indices can then be used to subset the original vector. Recall
from the previous lab that square brackets [ and
] can be used to access elements of a vector by their
position:
# Store the indices where scores > 6
keep_idx = which(scores > 6)
# Use those indices to extract the corresponding values
scores[keep_idx]
## [1] 7 9 7 10
We can also combine these steps into a single line without having to save the index to our Environment.
scores[which(scores > 6)]
## [1] 7 9 7 10
Notice the difference between these three operations by pasting them
into your R console.
scores > 6 # Which positions satisfy the condition? TRUE/FALSE
which(scores > 6) # What are the indices of those positions?
scores[which(scores > 6)] # What values are stored at those positions?
Question 2: Using the scores vector,
complete the following:
A. Use which() to find the indices of all values
that are less than or equal to 7.
B. Use these indices to extract the corresponding values from scores.
C. In a single line of code, use which() and
indexing to return all values of scores that are exactly equal to 7. How
many are there?
Often we want to access all data that meet certain criteria, and so these logical condition operations can be used on data sets. For example, we may want to analyze all countries (using data from the previous lab) with a life expectancy above 80.
# load in data
HappyPlanet = read.csv("https://remiller1450.github.io/data/HappyPlanet.csv")
## This returns a logical vector using the condition "> 80"
HappyPlanet$LifeExpectancy > 80
## [1] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
## [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
## [49] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE TRUE FALSE
## [61] FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE FALSE FALSE FALSE FALSE
## [73] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [85] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [97] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [109] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
## [121] FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [133] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
For each country (row) in our data set, this vector tells us whether or not it meets this condition (Life expectancy greater than 80).
On its own, the previous list of TRUE’s and
FALSE’s for the countries is not in a super helpful format
for identifying the actual countries.
Just as with the scores vector, we can use
which() to find the indices in the HappyPlanet
data where this condition is TRUE. In a data frame, these
indices correspond to rows.
## This returns the positions where the condition evaluated to TRUE
which(HappyPlanet$LifeExpectancy > 80)
## [1] 6 25 47 57 59 65 66 68 119 122 123
Looking better! This result could then be used as an index to
subset HappyPlanet (keep only the rows/countries
of data meeting this condition):
## sub-setting via indices
keep_idx <- which(HappyPlanet$LifeExpectancy > 80)
my_subset <- HappyPlanet[keep_idx, ]
Again, the approach shown above is a bit cumbersome. As a more
convenient alternative, we can use the subset() function
alongside a logical expression, which will give us the desired rows
without having to do the indexing:
## Example #1 (try this in your R console)
Ex1 <- subset(HappyPlanet, LifeExpectancy > 80)
In example #1, the data frame Ex1 will contain the
subset of countries with life expectancy above 80. Notice how the
subset() function knows that LifeExpectancy is
a component of HappyPlanet.
## Example #2 (try this in your R console)
Ex2 <- subset(HappyPlanet, LifeExpectancy <= 70 & Happiness > 6)
In example #2, the & operator is used to create a
data frame, Ex2, containing all countries with a life
expectancy of 70 or below and a happiness score above 6. A
country must meet both conditions to be included.
## Example #3 (try this in your R console)
Ex3 <- subset(HappyPlanet, LifeExpectancy <= 70 | Happiness > 6)
In example #3, the | operator is used to create a data
frame of all countries with a life expectancy of 70 or below,
or a happiness score above 6. Notice the different dimensions
of Ex2 and Ex3:
dim(Ex2) # 9 rows, 11 columns
## [1] 9 11
dim(Ex3) # 118 rows, 11 columns
## [1] 118 11
Question 3:
A. Create a data frame named Q3A that contains all
countries with a population over 100 million. Write code that shows me
how many rows are in this data frame.
B. Create a data frame named Q3B that contains all
countries with a happiness score of 6 or lower. How many rows are in
this data frame?
C. Using the data frame Q3A that you created in part
A, create a new data frame named Q3C that contains only
countries with a happiness score of 6 or lower. How many countries are
in this data frame?
D. Create a data frame named Q3D by applying both
conditions from parts A and B at the same time in a single
subset() function. Confirm that Q3C and
Q3D contain the same number of countries. In part C you
applied the conditions one at a time; here you are applying both
conditions at once.
E. Give me a vector of the names of the countries in data frame
Q3D.
There are situations where logical vectors can also be
useful without subsetting our data. When logical values are used in
numerical calculations, R treats TRUE as 1 and
FALSE as 0.
For example, consider our scores vector again:
scores = c(4, 7, 9, 3, 7, 10, 6)
We can use sum() function on this logical vector (which
normally adds up all values in a vector):
scores > 6 # should be 4 TRUEs
## [1] FALSE TRUE TRUE FALSE TRUE TRUE FALSE
sum(scores > 6)
## [1] 4
Because each TRUE is treated as 1 and each
FALSE as 0, the sum gives us the number of values that
satisfy the condition.
Similarly, we can use mean() function, which gives us
the proportion of values that satisfy the condition.
mean(scores > 6)
## [1] 0.5714286
sum(scores > 6) / length(scores) # verifying average
## [1] 0.5714286
We can apply the same idea to logical conditions involving variables in a data frame:
# Number of countries with life expectancy above 80
sum(HappyPlanet$LifeExpectancy > 80)
## [1] 11
# Proportion of countries with life expectancy above 80
mean(HappyPlanet$LifeExpectancy > 80)
## [1] 0.07692308
Question 4: Use logical conditions and the
sum() or mean() functions to answer each of
the following questions. Do not create a new subset of the data.
A. How many countries have a Happiness score greater than 7?
B. What proportion of countries have a Happiness score greater than 7?
C. What proportion of countries do not belong to Region 1?
D. How many countries have both a life expectancy of at least 75 years and an HDI value more than 0.7?
E. What proportion of countries have an HDI value larger than the
mean HDI value? What does this suggest about the skew of this variable?
(Hint: make sure to use na.rm=TRUE).
Up to this point, most of our logical conditions have involved
comparing numerical values using operators such as < and
>.
For example, we’ve used conditions like the following to identify countries belonging to a particular region, which worked because Region was coded as an integer variable.
HappyPlanet$Region == 1
We can also use == to match values in categorical
(character) variables too, but we have to be careful with spelling and
case-sensitivity of the name.
which(HappyPlanet$Country == "United States of America")
## [1] 136
This works well when we are interested in a single value, but we may instead want to identify observations that match any of several possible values.
Suppose I wanted to make a subset of this data frame that contains information for a select group of countries: United States of America, India, Brazil, Japan, South Africa, and Germany. Let’s consider how we could accomplish it using the logical operators we already know.
subset(HappyPlanet,
Country == "United States of America" |
Country == "India" |
Country == "Brazil" |
Country == "Japan" |
Country == "South Africa" |
Country == "Germany")
## Country Region Happiness LifeExpectancy Footprint HLY
## 18 Brazil 1 7.6 71.7 2.4 54.3
## 49 Germany 2 7.2 79.1 4.2 56.8
## 60 India 5 5.5 63.7 0.9 35.1
## 68 Japan 6 6.8 82.3 4.9 55.6
## 118 South Africa 4 5.0 50.8 2.1 25.2
## 136 United States of America 2 7.9 77.9 9.4 61.2
## HPI HPIRank GDPperCapita HDI Population
## 18 61.01 9 8402 0.800 186.83
## 49 48.07 51 29461 0.935 82.47
## 60 53.03 35 3452 0.619 1094.58
## 68 43.25 75 31267 0.953 127.77
## 118 29.69 118 11110 0.674 46.89
## 136 30.73 114 41890 0.951 296.51
When given a list of values that we are trying to match in the data
set, we can instead use the %in% to do this with less
typing. The %in% operator checks each value to see whether it matches
any of the values in a specified vector. For readability, we can first
store the group of values we want to check in its own vector, but this
is not necessary.
six_countries = c("United States of America", "Brazil", "India", "Japan", "South Africa", "Germany")
subset(HappyPlanet, Country %in% six_countries)
# or, directly
subset(HappyPlanet, Country %in% c("United States of America", "Brazil", "India", "Japan", "South Africa", "Germany"))
Question 5
A. Using %in%, how many countries belong to Regions
2, 4, or 6?
B. Using %in% and the negation operator
!, create a logical vector identifying countries that do
not belong to Regions 2, 4, or 6. Save this logical vector as an object.
How many countries satisfy this condition?
C. Using the logical vector you created in part B, make a subset
of the HappyPlanet data containing only countries from
regions other than 2, 4, or 6.