Course contents

Part IChapter 3R foundations and data

Your First R Instructions

On this page
  1. 1. Calculations
  2. 2. Variables: Give a result a name
  3. 3. Text, numbers, and TRUE/FALSE
  4. 4. Integer, double, and the special values
  5. 5. Functions: Give R a job
  6. 6. Math functions
  7. 7. What is an object? typeof(), class(), and attributes
  8. 8. Comparisons and logical values
  9. 9. Comments and reading errors
  10. Try it
  11. Checkpoint

1. Calculations

Arithmetic operators work like a calculator: + adds, - subtracts, * multiplies, / divides, and ^ raises to a power. Parentheses control what happens first.

2 + 3

Output

[1] 5
10 / 2

Output

[1] 5
(4 + 6) * 2

Output

[1] 20
(4 + 6) / 2

Output

[1] 5

Predict the result of each line before running it.

Two more operators are useful with whole numbers. %% gives the remainder after division, and %/% gives the whole-number part of a division. They help with questions such as “is this number even?” or “how many full weeks are in 100 days?”

2 ^ 3        # 2 to the power 3

Output

[1] 8
17 %% 5      # remainder of 17 divided by 5

Output

[1] 2
17 %/% 5     # how many whole 5s fit in 17

Output

[1] 3
100 %/% 7    # full weeks in 100 days

Output

[1] 14
100 %% 7     # days left over

Output

[1] 2

17 %% 5 prints 2 because 17 is three 5s (15) plus 2, and 17 %/% 5 prints the 3.

2. Variables: Give a result a name

R’s assignment symbol <- means “store the value on the right under the name on the left.” It does not mean “the two things are equal forever.”

age <- 54
age                          # Read the stored value

Output

[1] 54
age_next_year <- age + 1
age_next_year

Output

[1] 55
age <- 55                    # Replaces the previous age value
Three rows of name-and-value boxes: age holds 54; age_next_year is computed from age and holds 55; then age <- 55 makes age point at 55, replacing the old 54, while age_next_year keeps 55.
Three rows of name-and-value boxes: age holds 54; age_next_year is computed from age and holds 55; then age <- 55 makes age point at 55, replacing the old 54, while age_next_year keeps 55.

Running age <- 55 makes the name age point at 55 and drops 54, while age_next_year keeps the 55 it was given.

R also accepts a single = for assignment, so this works too:

age = 54
age

Output

[1] 54

It prints 54, the same as with <-. This course uses <- for assignment because it is the common R style and our convention, not because = is wrong. It also lets = keep one job, naming arguments inside a function (as in digits = 2 below), while == compares.

Names are case-sensitive: Age and age are different objects. Prefer descriptive lowercase names with underscores (for example, systolic_bp). Avoid spaces in object names.

3. Text, numbers, and TRUE/FALSE

R handles several fundamental data types:

  • numeric: 2, 2.5, or -10.
  • character: text enclosed by quotation marks, such as "North".
  • logical: TRUE or FALSE, without quotation marks.
  • missing (NA): an unknown value; not the same as zero.
weight_kg <- 70.5
clinic <- "Mobile"
is_adult <- TRUE
missing_bmi <- NA

class(weight_kg)

Output

[1] "numeric"
class(clinic)

Output

[1] "character"
class(is_adult)

Output

[1] "logical"
is.na(missing_bmi)

Output

[1] TRUE

The three class() lines print "numeric", "character" and "logical", and is.na(missing_bmi) prints TRUE because that value is unknown. Note that "Mobile" needs quotes but TRUE does not.

A numeric-looking identifier such as "00123" should often remain text. Identifiers are labels, not measurements to average.

4. Integer, double, and the special values

Numbers come in two types. A double is a number that may have decimals; plain numbers you type, such as 5, are doubles. An integer is a whole number, and you make one by adding L, as in 5L. typeof() shows the stored type. For everyday work you rarely need to care, but counts such as length() are integers, and you will see “integer” and “double” in tables and error messages.

typeof(5)

Output

[1] "double"
typeof(5L)

Output

[1] "integer"
typeof(2.5)

Output

[1] "double"
class(5L)

Output

[1] "integer"

R also has special values that are not ordinary numbers:

  • Inf and -Inf: infinity, from dividing a nonzero number by zero.
  • NaN: “not a number”, from an undefined calculation such as 0 / 0.
  • NULL: nothing at all, an empty object. It is not the same as NA, which is a missing value in a place where a value belongs.
1 / 0

Output

[1] Inf
-1 / 0

Output

[1] -Inf
0 / 0

Output

[1] NaN
is.nan(0 / 0)

Output

[1] TRUE
length(NULL)

Output

[1] 0
length(NA)

Output

[1] 1

1 / 0 prints Inf, 0 / 0 prints NaN, and length(NULL) is 0 while length(NA) is 1, because NA still occupies one slot. If a calculation on clinic data returns Inf or NaN, look for a zero in the denominator, such as a height of 0.

5. Functions: Give R a job

A function is a named operation followed by parentheses. Values inside the parentheses are called arguments. Some arguments have names.

round(3.14159, digits = 2)

Output

[1] 3.14
sqrt(81)

Output

[1] 9
sum(2, 5, 8)

Output

[1] 15

round(3.14159, digits = 2) prints 3.14, sqrt(81) prints 9 and sum(2, 5, 8) prints 15. In the first call, digits is the name of the second argument, and 2 is its value.

Type help("round") in your own Console to open the help page for a function. It is shown here, not run, because it opens a help page rather than printing a result.

help("round")

When you see round(3.14159, digits = 2), read it aloud as “round 3.14159 to two digits after the decimal point.”

6. Math functions

R has the usual math functions built in. Each takes a number (or a whole vector of numbers) and returns a number.

x <- 7.58
round(x, 1)      # nearest tenth

Output

[1] 7.6
ceiling(x)       # round up to a whole number

Output

[1] 8
floor(x)         # round down

Output

[1] 7
trunc(-x)        # drop the decimals, toward zero

Output

[1] -7
sqrt(49)

Output

[1] 7
exp(1)           # e to the power 1

Output

[1] 2.718282
log(100)         # natural logarithm

Output

[1] 4.60517
log10(100)       # base-10 logarithm

Output

[1] 2
log2(8)          # base-2 logarithm

Output

[1] 3
factorial(5)     # 5 * 4 * 3 * 2 * 1

Output

[1] 120
sin(0)

Output

[1] 0
cos(0)

Output

[1] 1

ceiling(7.58) is 8 and floor(7.58) is 7. trunc(-7.58) is -7 (toward zero), whereas floor(-7.58) would be -8. log(100) is about 4.6, but log10(100) is exactly 2. sin() and cos() work in radians and are rarely needed in clinic data; log() is the one you will meet most, for skewed measurements.

7. What is an object? typeof(), class(), and attributes

Everything you store in R is an object: a number, a piece of text, a vector, or later a whole table. Two questions describe an object. typeof() says how it is stored inside R, and class() says what kind of thing it is for the functions that use it. Often they agree, but not always (a factor is stored as integers).

An object can also carry extra labels called attributes, such as names. attributes() lists them and attr() reads or sets one.

pulse <- c(a = 70, b = 82)
typeof(pulse)

Output

[1] "double"
class(pulse)

Output

[1] "numeric"
attributes(pulse)

Output

$names
[1] "a" "b"
attr(pulse, "units") <- "beats/min"
attr(pulse, "units")

Output

[1] "beats/min"
attributes(pulse)

Output

$names
[1] "a" "b"

$units
[1] "beats/min"

attributes(pulse) first lists only $names ("a" and "b"); after the attr() line it lists $units too. You rarely write attributes yourself, but this is where a factor keeps its levels and a table keeps its column names.

8. Comparisons and logical values

Comparison operators produce TRUE or FALSE. Use == to compare values; do not confuse it with <- to assign. Other comparisons are >, <, >=, <=, and !=.

age <- 54
age >= 18

Output

[1] TRUE
age == 54

Output

[1] TRUE
age != 54

Output

[1] FALSE
(age > 40) & (age < 65)      # Both conditions must be true

Output

[1] TRUE
(age < 20) | (age > 50)      # Either condition may be true

Output

[1] TRUE

With age equal to 54, the five lines print TRUE, TRUE, FALSE, TRUE and TRUE. The last one is TRUE because the second condition, age > 50, holds even though age < 20 does not.

R uses & for “and” and | for “or” when comparing vectors element by element.

9. Comments and reading errors

Text after a # is for humans; R does not execute it.

# This is a useful explanation for your future self.
bmi <- 27.2
bmi * 2

Output

[1] 54.4

R skips the comment line, stores 27.2 under bmi, and prints 54.4. Typing bmii * 2 instead would give “object ‘bmii’ not found”, because that name was never created.

If R prints “object not found,” check whether you created that object, ran the earlier lines, and used the exact capitalization. If it prints “unexpected symbol,” look for missing quotation marks, commas, and parentheses.

Try it

  1. Calculate a BMI from weight 72 kg and height 1.65 m: weight divided by (height squared). Save each input in a named object.
Show solution to question 1

Answer: The BMI is about 26.4 kg/m².

Step by step

  1. Store each input under its own name.
  2. Divide the weight by the squared height.
weight_kg <- 72
height_m <- 1.65
bmi <- weight_kg / height_m^2
bmi

Output

[1] 26.44628

3. The Console prints the BMI. The ^ does the squaring first, so we divide by 2.7225 and get 26.44628. Reporting it as 26.4 is enough.

  1. Create a logical object called high_bmi that is TRUE if BMI is at least 30.
Show solution to question 2

Answer: high_bmi is FALSE, because 26.4 is below 30.

Step by step

  1. Use the bmi object from the last item and the comparison >=.
bmi <- 72 / 1.65^2
high_bmi <- bmi >= 30
high_bmi

Output

[1] FALSE

2. The comparison returns one TRUE or FALSE, which is stored under the name high_bmi. 3. It prints FALSE. A BMI of exactly 30 would give TRUE, because >= includes the boundary.

  1. Explain why patient_id <- "00123" is different from patient_id <- 123.
Show solution to question 3

Answer: "00123" is a character label that keeps its leading zeros, while 123 is a number; an ID in quotes is a label, and a number falsely implies that arithmetic on it is meaningful.

Step by step

  1. Store both versions and check their classes.
id_text <- "00123"
id_number <- 123
class(id_text)

Output

[1] "character"
class(id_number)

Output

[1] "numeric"
id_text

Output

[1] "00123"
id_number

Output

[1] 123

2. The first is "character" and the second is "numeric". The number printed has lost its two zeros. 3. An ID is a name, not a measurement. Averaging patient IDs gives nonsense, and losing a leading zero can make two IDs look the same or fail to match another file.

  1. Intentionally mistype a name, read the error, then fix it.
Show solution to question 4

Answer: A mistyped name such as bmii gives “object ‘bmii’ not found”; fixing it means using the name that was actually created, bmi.

Step by step

  1. Create the object, then use a wrong name. The chunk below catches the error so the page can show its message.
bmi <- 26.4
msg <- tryCatch(bmii * 2, error = function(e) conditionMessage(e))
msg

Output

[1] "object 'bmii' not found"

2. The message says object bmii was not found. R has no such name because we created bmi. 3. Fix the spelling.

bmi * 2

Output

[1] 52.8

4. It prints 52.8. At the Console you would see the same message in red; check spelling, capitalization and whether the earlier line was run.

  1. Find the remainder and the whole-number part of 250 divided by 7. Then check typeof(250) and typeof(250L).
Show solution to question 5

Answer: The remainder is 5 and the whole-number part is 35; 250 is a double and 250L is an integer.

Step by step

  1. Use %% for the remainder and %/% for the whole-number part.
250 %% 7

Output

[1] 5
250 %/% 7

Output

[1] 35

2. 35 sevens make 245, and 250 - 245 leaves 5. 3. Check the types.

typeof(250)

Output

[1] "double"
typeof(250L)

Output

[1] "integer"

4. A plain number is stored as a "double"; adding L makes it an "integer".

  1. Predict, then run: 0 / 0, 1 / 0, length(NULL). Say what each special value means.
Show solution to question 6

Answer: 0 / 0 is NaN (not a number), 1 / 0 is Inf (infinity), and length(NULL) is 0 because NULL is an empty object.

Step by step

  1. Predict first: zero divided by zero is undefined, a positive number divided by zero grows without limit, and NULL means nothing.
  2. Run them.
0 / 0

Output

[1] NaN
1 / 0

Output

[1] Inf
length(NULL)

Output

[1] 0

3. The output matches. In clinic data, Inf or NaN usually means a zero in the denominator. NULL differs from NA: length(NA) is 1, because NA is a missing value that still occupies a slot.

  1. For the value 3.7, calculate round(), ceiling(), floor(), trunc(), sqrt(), and log10(). Also calculate factorial(4).
Show solution to question 7

Answer: round() gives 4, ceiling() 4, floor() 3, trunc() 3, sqrt() about 1.92, log10() about 0.568, and factorial(4) is 24.

Step by step

  1. Store the value once, then apply each function.
x <- 3.7
round(x)

Output

[1] 4
ceiling(x)

Output

[1] 4
floor(x)

Output

[1] 3
trunc(x)

Output

[1] 3
sqrt(x)

Output

[1] 1.923538
log10(x)

Output

[1] 0.5682017
factorial(4)

Output

[1] 24

2. round() goes to the nearest whole number, 4, and ceiling() always goes up, also 4. floor() and trunc() both give 3 for a positive number. 3. sqrt(3.7) and log10(3.7) print the decimals shown. factorial(4) is 4 * 3 * 2 * 1 = 24.

  1. Create bp <- c(120, 135), give it an attribute called "units" with the text "mmHg", and print attributes(bp). Also say why this course writes x <- 5 rather than x = 5.
Show solution to question 8

Answer: attributes(bp) lists $units with the value "mmHg". The course writes <- so that <- always means assign, = names arguments, and == compares.

Step by step

  1. Make the vector and set the attribute with attr().
bp <- c(120, 135)
attr(bp, "units") <- "mmHg"
attributes(bp)

Output

$units
[1] "mmHg"

2. The output has one entry, $units, holding "mmHg". The values are unchanged, and the label travels with the object. 3. Both x <- 5 and x = 5 work. Keeping <- for assigning leaves = for the digits = 2 style of argument and == for comparison, so each symbol has one job.

Checkpoint

You can assign values, use built-in functions, write comments, compare values, and understand the first error messages.