Lesson 7 of 17 · Week 03 · Tue Oct 13

Cleaning Data in Practice: Subsetting

Questions

Objectives

Why subsetting is cleaning

Most data cleaning is subsetting: finding the rows that are wrong, pulling out the columns you need, replacing bad values with NA. R has three subsetting operators, [, [[ and $, and six ways to say which elements you want. Once you know them, cleaning becomes a sequence of small, checkable steps.

Adapted from Software Carpentry's “Subsetting Data” episode (CC BY 4.0, The Carpentries). Text, examples and exercises have been rewritten for this course.

Vectors: six ways to pick elements

w <- c(a = 5.1, b = 7.3, c = 2.2, d = 9.8, e = 6.4)
w[2]                 # by position
w[c(1, 3)]           # several positions
w[2:4]               # a range
w[-1]                # everything EXCEPT position 1
w[-(1:2)]            # everything except 1 and 2  (note the parentheses!)
w["d"]               # by name
w[c("a", "e")]
w[w > 6]             # by a logical condition
w[w > 3 & w < 7]     # & = and, | = or, ! = not

Watch out: w[-1:2] means w[c(-1, 0, 1, 2)], which is an error (you can't mix negative and positive indices). w[10] returns NA rather than an error, so an index that's out of range can go unnoticed. which(w > 6) gives the positions where a condition is TRUE.

Matching several values: %in%

To keep elements equal to any of several values, don't use == with a vector: it compares element by element, recycling the shorter vector, and silently gives wrong answers. Use %in%:

andes <- c("Bolivia", "Chile", "Peru")
gm$country == andes            # WRONG: recycles, compares row 1 to Bolivia, row 2 to Chile...
gm$country == "Bolivia" | gm$country == "Chile" | gm$country == "Peru"   # works, but clunky
gm$country %in% andes          # right: TRUE wherever the country is any of the three

Matrices and lists

m <- matrix(1:12, nrow = 3)
m[2, 3]              # row 2, column 3
m[2, ]               # all of row 2
m[, c(1, 4)]         # columns 1 and 4
m[5]                 # a matrix is a vector: the 5th element, counting down columns

kit <- list(label = "field kit", counts = c(4, 8, 15), site = "A")
kit[2]               # a LIST containing counts (the train car)
kit[[2]]             # the counts vector itself (what's in the car)
kit$counts[3]        # 15

Model output is a list too. Look at its pieces with names() (or str()) and pull one out with $: e.g. fit <- lm(lifeExp ~ gdpPercap, data = gm), then names(fit).

Data frames

gm[gm$year == 2007, ]                        # rows by condition, all columns
gm[gm$lifeExp > 75, c("country", "year")]    # rows AND columns
gm[c(1:4, 9:12), ]                           # rows by position
gm[, -(1:2)]                                 # drop columns 1 and 2
gm[gm$year %in% c(1967, 2007), ]

With one index and no comma, gm[1:3] picks columns 1–3 (a data frame is a list of columns), and gm[1:20] is an error because gm has only 6 columns. With a comma, gm[1:3, ] picks rows. The comma matters.

Cleaning in practice

A typical cleaning pass, one small step at a time. Check each step before moving on:

survey$age[survey$age == -99] <- NA             # a code for "missing" becomes a real NA
survey$site <- tolower(trimws(survey$site))     # " North" and "north " become "north"
table(survey$site, useNA = "ifany")             # every spelling, and how many NAs
survey[survey$age < 0 | survey$age > 110, ]     # rows that can't be right
sum(is.na(survey$age))                          # how many are missing now?
survey <- survey[!duplicated(survey), ]         # drop exact duplicate rows

Never overwrite the raw file. Your script reads the raw data from data/ and writes the clean version to results/ (Week 06), so every cleaning decision is written down and can be re-run.

Exercises

Each exercise gives you up to 10 checked attempts. Use Run as many times as you like to experiment first, then click Check answer when you're ready. Your exercises use numbers/subsets unique to you, so answers won't match a classmate's exactly.

Exercise 1 — Two roads to the same place

0 / 10 attempts used

Click Run to see a named vector w and a target, a piece of w. Produce target in two different ways: way1 using positions (positive or negative) and way2 using names. Don't use target itself. Afterwards, compare strategies with a neighbour.

Pasting isn't accepted here — please type your answer so it reflects your own work.

      

Exercise 2 — Between two bounds

0 / 10 attempts used

Click Run to see w, lo and hi. Store in between the values of w that are strictly greater than lo and strictly less than hi, in one subsetting command.

Pasting isn't accepted here — please type your answer so it reflects your own work.

      

Exercise 3 — Pick a region with %in%

0 / 10 attempts used

Click Run to see a region and the vector region of its countries (not all of them are in gm). Create:

  • in_region: a logical vector, one value per row of gm, TRUE where the country is in region;
  • region_rows: the rows of gm for those countries.

Also try the wrong way, gm$country == region, and read the warning.

Pasting isn't accepted here — please type your answer so it reflects your own work.

      

Exercise 4 — Which command extracts these?

0 / 10 attempts used

Click Run to see a matrix m, two target values, and four candidate commands. Store the letter of the command that extracts exactly those two values in choice (e.g. "A"). Predict first, then check by running the candidates.

Pasting isn't accepted here — please type your answer so it reflects your own work.

      

Exercise 5 — Dig into a list

0 / 10 attempts used

kit is a list (click Run to see it). Using list and vector subsetting, store in val the element of kit's counts item at position k.

Pasting isn't accepted here — please type your answer so it reflects your own work.

      

Exercise 6 — Pull a number out of a model

0 / 10 attempts used

A linear model fit has been fitted to part of gm (the part differs per student). A fitted model is a list. Use names(fit) to find the piece that holds the residual degrees of freedom, and store its value in rdf.

Pasting isn't accepted here — please type your answer so it reflects your own work.

      

Exercise 7 — Fix five broken subsets

0 / 10 attempts used

Each line below has a common subsetting mistake. Fix them so that:

  1. q1: rows from 1987;
  2. q2: all columns except 1 through 3;
  3. q3: rows where life expectancy is over 78;
  4. q4: the first row, columns 3 and 4 (year and lifeExp);
  5. q5: rows from 1967 and 2002.
Pasting isn't accepted here — please type your answer so it reflects your own work.

      

Exercise 8 — Rows or columns?

0 / 10 attempts used

First run gm[1:10] and read the error. Why does it fail, when gm[1:10, ] works? Then click Run to see your two row ranges and create gm_small, a data frame with only those rows of gm (all columns), in one step.

Pasting isn't accepted here — please type your answer so it reflects your own work.

      

Key Points

Proof of completion

Once you've attempted every exercise above, download a PDF summary of your work on this lesson — your name, the exercises you passed, attempt counts, timing, and your final code for each.