1 Learning Objectives

  • Identify R’s core data types: numeric (double), integer, character, logical, and complex
  • Use class(), typeof(), and str() to inspect objects
  • Convert between data types using coercion functions, and understand R’s automatic coercion hierarchy
  • Understand the distinct meanings of NA, NULL, NaN, and Inf, including typed NAs
  • Apply arithmetic, relational, and logical operators correctly, including %in%

2 Core Data Types in R

Type Example Description
numeric (double) 3.14, 10 Any real number, decimal or whole; R’s default for numbers
integer 5L Whole numbers only (note the L suffix)
character "hello" Text, always in quotes
logical TRUE, FALSE Boolean values
complex 2+3i Numbers with an imaginary part
num_var <- 3.14
int_var <- 5L
char_var <- "R is fun"
logical_var <- TRUE
complex_var <- 2 + 3i

class(num_var)
## [1] "numeric"
class(int_var)
## [1] "integer"
class(char_var)
## [1] "character"
class(logical_var)
## [1] "logical"
class(complex_var)
## [1] "complex"

2.1 class() vs typeof() vs str()

class() tells you the object’s high-level type; typeof() tells you its underlying storage mode; str() gives a compact, human-readable structural summary — arguably the single most useful inspection function in all of R.

typeof(num_var)   # "double" - R's default numeric storage
## [1] "double"
typeof(int_var)    # "integer"
## [1] "integer"
str(num_var)
##  num 3.14
str(list(a = 1, b = "text", c = TRUE))
## List of 3
##  $ a: num 1
##  $ b: chr "text"
##  $ c: logi TRUE

2.2 Numeric Precision: Why 0.1 + 0.2 != 0.3

Like almost all programming languages, R stores decimal numbers using floating-point representation, which cannot represent every decimal value exactly.

0.1 + 0.2 == 0.3          # surprisingly FALSE!
## [1] FALSE
0.1 + 0.2                  # looks like 0.3, but carries tiny rounding error
## [1] 0.3
isTRUE(all.equal(0.1 + 0.2, 0.3))  # the CORRECT way to compare decimals for "equality"
## [1] TRUE

Best Practice: Never use == to compare two decimal numbers directly. Use all.equal() (wrapped in isTRUE()) or round both sides to a sensible number of digits first.

3 Character Strings in Depth

greeting <- "Hello, World!"
apostrophe_example <- 'It\'s a sunny day'     # escaping a single quote inside single quotes
newline_example <- "Line one\nLine two"        # \n = newline
tab_example <- "Name:\tAmina"                   # \t = tab

cat(newline_example, "\n")
## Line one
## Line two
cat(tab_example, "\n")
## Name:    Amina
Escape Sequence Meaning
\n New line
\t Tab
\" Literal double quote inside a double-quoted string
\\ A single literal backslash

4 Special Values

Value Meaning
NA Missing / Not Available
NULL Empty / absence of a value (zero length)
NaN “Not a Number” — e.g. result of 0/0
Inf / -Inf Positive/negative infinity — e.g. 1/0
missing_value <- NA
empty_value <- NULL
not_a_number <- 0/0
infinite_value <- 1/0

is.na(missing_value)
## [1] TRUE
is.null(empty_value)
## [1] TRUE
is.nan(not_a_number)
## [1] TRUE
is.infinite(infinite_value)
## [1] TRUE

4.1 Typed NAs (Often Overlooked)

NA actually comes in several type-specific flavors under the hood. This rarely matters for everyday use, but explains some confusing edge cases you may encounter later:

typeof(NA)              # logical, by default
## [1] "logical"
typeof(NA_integer_)
## [1] "integer"
typeof(NA_character_)
## [1] "character"
typeof(NA_real_)
## [1] "double"

4.2 NA vs. NULL — A Critical Distinction

length(NA)     # 1 - NA IS a value, it just represents "unknown"
## [1] 1
length(NULL)   # 0 - NULL represents complete absence, nothing is there at all
## [1] 0
x <- c(1, 2, NA, 4)   # a vector CAN contain NA
length(x)               # 4 - NA still counts as an element
## [1] 4
# c(1, 2, NULL, 4) would simply drop the NULL - try it and see!
c(1, 2, NULL, 4)
## [1] 1 2 4

Common mistake: Beginners often confuse NA (a real but unknown/missing value that occupies a “slot”) with NULL (the complete absence of a value, zero length, and effectively invisible when combined into a vector).

5 Type Coercion (Converting Between Types)

R can convert values from one type to another using as.*() functions.

as.character(123)      # numeric -> character
## [1] "123"
as.numeric("45.6")     # character -> numeric
## [1] 45.6
as.integer(7.9)         # numeric -> integer (truncates toward zero, does NOT round!)
## [1] 7
as.integer(-7.9)        # truncates toward zero -> -7, not -8
## [1] -7
as.logical("TRUE")     # character -> logical
## [1] TRUE
as.numeric(TRUE)       # logical -> numeric (TRUE becomes 1)
## [1] 1
as.numeric(FALSE)      # FALSE becomes 0
## [1] 0

Coercion that fails gracefully (produces NA with a warning):

as.numeric("hello")   # Cannot convert text to a number -> NA, with a warning
## Warning: NAs introduced by coercion
## [1] NA

5.1 R’s Automatic Coercion Hierarchy

When you combine different types together (e.g., in a vector, covered fully in Lesson 3), R automatically coerces everything to the “widest” compatible type, following this order:

logical -> integer -> numeric -> character

c(1, TRUE)                    # logical coerced up to numeric -> 1, 1
## [1] 1 1
c(1, TRUE, "hello")          # everything coerced up to character
## [1] "1"     "TRUE"  "hello"
class(c(1, TRUE, "hello"))
## [1] "character"
c(1L, 2.5)                    # integer coerced up to numeric (double)
## [1] 1.0 2.5
class(c(1L, 2.5))
## [1] "numeric"

6 Operators in R

6.1 Arithmetic Operators

7 + 3   # Addition
## [1] 10
7 - 3   # Subtraction
## [1] 4
7 * 3   # Multiplication
## [1] 21
7 / 3   # Division
## [1] 2.333333
7 %% 3  # Modulus (remainder)
## [1] 1
7 %/% 3 # Integer division
## [1] 2
7 ^ 3   # Exponentiation
## [1] 343

6.2 Relational (Comparison) Operators

These always return a logical (TRUE/FALSE).

5 > 3    # Greater than
## [1] TRUE
5 < 3    # Less than
## [1] FALSE
5 >= 5   # Greater than or equal to
## [1] TRUE
5 == 5   # Equal to (note: DOUBLE equals sign!)
## [1] TRUE
5 != 3   # Not equal to
## [1] TRUE

Common mistake: Using a single = to compare values instead of ==. A single = is for assignment; using it inside an if() condition, for example, will usually throw a syntax error rather than silently doing the wrong thing — but it’s still a frequent source of confusion for beginners coming from other contexts.

6.3 Logical Operators

TRUE & FALSE    # AND (element-wise, vectorized)
## [1] FALSE
TRUE | FALSE    # OR (element-wise, vectorized)
## [1] TRUE
!TRUE           # NOT
## [1] FALSE
TRUE && FALSE   # AND (evaluates only the FIRST element - used in if-statements)
## [1] FALSE
TRUE || FALSE   # OR (evaluates only the FIRST element - used in if-statements)
## [1] TRUE

Rule of thumb: Use &/| when working with whole vectors (e.g., filtering a data frame). Use &&/|| only when comparing two single values, typically inside an if() condition — this will matter a great deal starting in Lesson 7.

6.4 The %in% Operator (Membership Testing)

5 %in% c(1, 3, 5, 7)                 # is 5 present in this vector?
## [1] TRUE
"CS" %in% c("Business", "Medicine")  # is "CS" present in this vector?
## [1] FALSE
c(2, 5, 9) %in% c(1, 2, 3, 4, 5)     # works element-by-element too
## [1]  TRUE  TRUE FALSE

7 Worked Example

Scenario: A university records a student’s exam score and pass threshold.

exam_score <- 76
pass_threshold <- 50

passed <- exam_score >= pass_threshold
cat("Score:", exam_score, "\n")
## Score: 76
cat("Did the student pass?", passed, "\n")
## Did the student pass? TRUE
cat("Score as a percentage of 100:", exam_score / 100 * 100, "%\n")
## Score as a percentage of 100: 76 %
# A slightly larger, realistic example
student_majors <- c("CS", "Business", "Medicine")
applicant_major <- "Law"
cat("Is", applicant_major, "an offered major?", applicant_major %in% student_majors, "\n")
## Is Law an offered major? FALSE

8 Mini-Project: A “Type-Safe” Registration Form Checker

Simulate a very small piece of a university registration system:

  1. Create objects for a fictional applicant: full_name (character), age (numeric), has_transcript (logical), gpa (numeric).
  2. Write a check using is.na() that would flag if gpa were missing.
  3. Deliberately store the applicant’s age as the character "twenty-one", then use as.numeric() on it and observe what happens. Explain the result in a comment.
  4. Use %in% to check whether the applicant’s intended major is in an approved list of 4 majors you define.
  5. Combine at least 3 conditions with & (e.g., age above 18 AND has a transcript AND GPA above 2.0) into one final eligible logical object.
  6. Print a final summary sentence using cat() that states whether the applicant is eligible.

9 Practice Exercises

  1. Create three objects: your age (numeric), your name (character), and is_student (logical, TRUE).
  2. Check the class() and typeof() of each object you just created.
  3. Compute 17 %% 5 and 17 %/% 5. Explain in a comment what each result means.
  4. Convert the character "3.5" into a numeric value and add 10 to it.
  5. What happens when you run as.numeric("R2D2")? Why?
  6. Create two logical values and combine them with &, |, and !. Predict the output before running the code.
  7. Use is.na() to check whether NA == NA returns what you expect. What did you discover?
  8. Create a vector-like combination c(10, "20", TRUE) and check its class(). Explain the coercion that happened.
  9. Test whether 0.1 + 0.2 == 0.3 returns TRUE or FALSE, and explain why using what you learned about floating-point numbers.
  10. Use %in% to check whether the number 7 is present in the vector c(2, 4, 6, 8, 10).
  11. Create a vector containing NA and a vector containing NULL in the same position; compare their length().
  12. Write one line of code using && and explain, in a comment, why & would also have worked (or not) in that specific case.
  13. Create NA_character_ and NA_integer_ directly, and confirm their typeof() differs.
  14. Deliberately trigger a coercion warning (e.g. as.numeric("abc")) and explain, in your own words, what the warning message means.

10 Quiz: Lesson 2

Q1. Which function reveals an object’s fundamental storage type in R?

  1. class()
  2. typeof()
  3. str()
  4. format()

Q2. What is the result of as.integer(7.9)?

  1. 8
  2. 7
  3. 7.9
  4. An error

Q3. What does NA represent in R?

  1. A syntax error
  2. An empty, zero-length object
  3. A missing or unavailable value
  4. Infinity

Q4. Which operator checks whether two values are equal?

  1. =
  2. ==
  3. !=
  4. <-

Q5. When R combines c(1, TRUE, "hello") into one vector, what type does the result become?

  1. numeric
  2. logical
  3. character
  4. integer

Q6. Why does 0.1 + 0.2 == 0.3 return FALSE in R?

  1. R cannot add decimals
  2. Floating-point numbers cannot represent every decimal value exactly, causing tiny rounding differences
  3. It is a bug that will be fixed in a future R version
  4. 0.3 is not a valid number in R

Q7. What is the key difference between NA and NULL?

  1. They are identical
  2. NA represents a missing value and has a length of 1; NULL represents total absence and has a length of 0
  3. NULL can only be used with numbers
  4. NA can only be used with text

Q8. What does 5 %in% c(1, 3, 5, 7) return?

  1. The position of 5 in the vector
  2. TRUE
  3. 5
  4. An error
Click to reveal Answer Key Q1: b | Q2: b | Q3: c | Q4: b | Q5: c | Q6: b | Q7: b | Q8: b

11 Summary

  • R’s core data types are numeric (double), integer, character, logical, and complex; class(), typeof(), and str() all help inspect them from different angles.
  • Floating-point numbers cannot always be compared exactly with ==; use all.equal() for decimal comparisons.
  • NA, NULL, NaN, and Inf are special values with distinct meanings; NA has typed variants (NA_integer_, NA_character_, etc.).
  • Coercion (as.*() functions) converts between types; automatic coercion follows logical → integer → numeric → character.
  • Arithmetic, relational, and logical operators behave predictably but have some easy-to-miss pitfalls (= vs ==, & vs &&); %in% tests membership.