Data types

wk5-d01-data-types

Dr. D

Chico State
DATA 385 - Fall 2026

September 22, 2026

Setup

🎥 Data types - code along in wk5-d01

library(tidyverse)
library(here) # new! Install it before first use. 

New Packages

here: The here package creates paths relative to the top-level directory. We will talk more about this in wk5-d03.

Why should you care about data types?

Example: Cat lovers

A survey asked respondents their name and number of cats. The instructions said to enter the number of cats as a numerical value.

cat_lovers <- read_csv(here::here("notes/data/cat-lovers.csv"))

Important

Do not add the notes part of the file path above to your code file.

cat_lovers
# A tibble: 60 × 3
   name           number_of_cats handedness
   <chr>          <chr>          <chr>     
 1 Bernice Warren 0              left      
 2 Woodrow Stone  0              left      
 3 Willie Bass    1              left      
 4 Tyrone Estrada 3              left      
 5 Alex Daniels   3              left      
 6 Jane Bates     2              left      
 7 Latoya Simpson 1              left      
 8 Darin Woods    1              left      
 9 Agnes Cobb     0              left      
10 Tabitha Grant  0              left      
# ℹ 50 more rows

Oh why won’t you work?!

cat_lovers %>%
  summarise(mean_cats = mean(number_of_cats))
# A tibble: 1 × 1
  mean_cats
      <dbl>
1        NA
?mean

Oh why won’t you still work??!

cat_lovers %>%
  summarise(mean_cats = mean(number_of_cats, na.rm = TRUE))
# A tibble: 1 × 1
  mean_cats
      <dbl>
1        NA

Take a breath and look at your data

What is the type of the number_of_cats variable?

glimpse(cat_lovers)
Rows: 60
Columns: 3
$ name           <chr> "Bernice Warren", "Woodrow Stone", "Willie Ba…
$ number_of_cats <chr> "0", "0", "1", "3", "3", "2", "1", "1", "0", …
$ handedness     <chr> "left", "left", "left", "left", "left", "left…

Let’s take another look

table(cat_lovers$number_of_cats)

                                                  0 
                                                 32 
                                                  1 
                                                 15 
1.5 - honestly I think one of my cats is half human 
                                                  1 
                                                  2 
                                                  4 
                                                  3 
                                                  6 
                                                  4 
                                                  1 
                                              three 
                                                  1 

Sometimes you might need to babysit your respondents

cat_lovers %>%
  mutate(number_of_cats = case_when(
    name == "Ginger Clark" ~ 2,
    name == "Doug Bass"    ~ 3,
    TRUE                   ~ as.numeric(number_of_cats)
    )) %>%
  summarise(mean_cats = mean(number_of_cats))
# A tibble: 1 × 1
  mean_cats
      <dbl>
1     0.833

Always respect data types

cat_lovers %>%
  mutate(
    number_of_cats = case_when(
      name == "Ginger Clark" ~ "2",
      name == "Doug Bass"    ~ "3",
      TRUE                   ~ number_of_cats
      ),
    number_of_cats = as.numeric(number_of_cats)
    ) %>%
  summarise(mean_cats = mean(number_of_cats))
# A tibble: 1 × 1
  mean_cats
      <dbl>
1     0.833

Don’t mix data types mid-stream. Recode as a character, and then convert to numeric afterward.

Now that we know what we’re doing…

Reassign the result of these steps back to cat_lovers.

cat_lovers <- cat_lovers %>%
  mutate(
    number_of_cats = case_when(
      name == "Ginger Clark" ~ "2",
      name == "Doug Bass"    ~ "3",
      TRUE                   ~ number_of_cats
      ),
    number_of_cats = as.numeric(number_of_cats)
    )

Dr. D’s note - I don’t like to overwrite my data set too often. I’m bound to make mistakes so I like to create a new variable every once in a while when it makes sense. Like moving from individual records to grouped/aggregated records.

Moral of the story

  • If your data does not behave how you expect it to, type coercion upon reading in the data might be the reason.
  • Go in and investigate your data, apply the fix, save your data, live happily ever after.

Data types

Data types in R

  • logical
  • double
  • integer
  • character
  • and some more, but we won’t be focusing on those

Logical & character

logical - boolean values TRUE and FALSE

typeof(TRUE)
[1] "logical"

character - character strings

typeof("hello")
[1] "character"

Double & integer

double - floating point numerical values (default numerical type)

typeof(1.335)
[1] "double"
typeof(7)
[1] "double"

integer - integer numerical values (indicated with an L)

typeof(7L)
[1] "integer"
typeof(1:3)
[1] "integer"

Concatenation

Vectors can be constructed using the c() function.

c(1, 2, 3)
[1] 1 2 3
c("Hello", "World!")
[1] "Hello"  "World!"
c(c("hi", "hello"), c("bye", "jello"))
[1] "hi"    "hello" "bye"   "jello"

Converting between types, with intention

x <- 1:3
x
[1] 1 2 3
typeof(x)
[1] "integer"
y <- as.character(x)
y
[1] "1" "2" "3"
typeof(y)
[1] "character"

Converting between types, with intention

x <- c(TRUE, FALSE)
x
[1]  TRUE FALSE
typeof(x)
[1] "logical"
y <- as.numeric(x)
y
[1] 1 0
typeof(y)
[1] "double"

Converting between types, without intention

R will happily convert between various types without complaint when different types of data are concatenated in a vector, and that’s not always a great thing!

c(1, "Hello")
[1] "1"     "Hello"
c(FALSE, 3L)
[1] 0 3
c(1.2, 3L)
[1] 1.2 3.0
c(2L, "two")
[1] "2"   "two"

Explicit vs. implicit coercion

  • Explicit coercion is when you call a function like as.logical(), as.numeric(), as.integer(), as.double(), or as.character()
  • Implicit coercion happens when you use a vector in a specific context that expects a certain type of vector

Pause the video?

Ignore this. We’re not doing that Hotels + Data types

Example: guessing a type

Example

Suppose we want to know the type of c(1, "a"). First, look at the type of each piece on its own, and guess based on those. Then check the combined vector directly.

typeof(1)
[1] "double"
typeof("a")
[1] "character"
typeof(c(1, "a"))
[1] "character"

Special values

Special values

  • NA: Not available
  • NaN: Not a number
  • Inf: Positive infinity
  • -Inf: Negative infinity
pi / 0
[1] Inf
0 / 0
[1] NaN
1/0 - 1/0
[1] NaN
1/0 + 1/0
[1] Inf

NAs are special snowflakes

x <- c(1, 2, 3, 4, NA)

mean(x)
[1] NA
mean(x, na.rm = TRUE)
[1] 2.5
summary(x)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.     NAs 
   1.00    1.75    2.50    2.50    3.25    4.00       1 

Note! mean() requires na.rm = TRUE to drop the NA, but summary() handles it automatically and just reports the count.

NAs are logical

R uses NA to represent missing values in its data structures.

typeof(NA)
[1] "logical"

Mental model for NAs

  • Unlike NaN, NAs are genuinely unknown values
  • But that doesn’t mean they can’t function in a logical way

Why do the following give different answers?

# TRUE or NA
TRUE | NA
[1] TRUE
# FALSE or NA
FALSE | NA
[1] NA

NAs are logical, explained

NA is unknown, so it could be TRUE or FALSE:

TRUE | NA

TRUE | TRUE  # if NA was TRUE
[1] TRUE
TRUE | FALSE # if NA was FALSE
[1] TRUE

FALSE | NA

FALSE | TRUE  # if NA was TRUE
[1] TRUE
FALSE | FALSE # if NA was FALSE
[1] FALSE
  • Doesn’t make sense for mathematical operations
  • Makes sense in the context of missing data

Acknowledgements & Credits

Note

This page adapts material from Data Science in a Box (Unit 2, Deck 10: “Data types”) by Mine Çetinkaya-Rundel, licensed under CC BY-SA 4.0. Source: tidyverse/datascience-box. Modified: converted from xaringan to Quarto revealjs.