Skip to content

R - Data Frames

A data frame is R’s primary structure for storing tabular data. Think of it as a spreadsheet or a database table, where columns represent variables and rows represent observations. Columns can contain different data types (e.g., numbers, text, dates).

The modern R ecosystem, particularly the tidyverse, has introduced the tibble, a next-generation data frame. Tibbles are more user-friendly: they print better, avoid changing variable names, and never convert strings to factors by default. For all new data analysis projects, using tibbles is highly recommended.

We’ll use the tibble() function from the tibble package (part of the tidyverse).

# First, ensure the tidyverse is installed and loaded
# install.packages("tidyverse")
library(tibble)
# Create a tibble
emp_data <- tibble(
emp_id = 1:5,
emp_name = c("Rick", "Dan", "Michelle", "Ryan", "Gary"),
salary = c(623.3, 515.2, 611.0, 729.0, 843.25),
start_date = as.Date(c("2012-01-01", "2013-09-23", "2014-11-15", "2014-05-11", "2015-03-27"))
)
# Print the tibble. Notice the clean output!
print(emp_data)

Executing this code produces a neatly formatted output:

# A tibble: 5 × 4
emp_id emp_name salary start_date
<int> <chr> <dbl> <date>
1 1 Rick 623. 2012-01-01
2 2 Dan 515. 2013-09-23
3 3 Michelle 611 2014-11-15
4 4 Ryan 729 2014-05-11
5 5 Gary 843. 2015-03-27

The str() (structure) and summary() functions from base R still work perfectly. However, the tidyverse provides glimpse(), which offers a compact and highly readable summary.

library(dplyr) # glimpse is in the dplyr package
# Get the structure using glimpse()
glimpse(emp_data)

The output of glimpse() is designed for easy reading:

Rows: 5
Columns: 4
$ emp_id <int> 1, 2, 3, 4, 5
$ emp_name <chr> "Rick", "Dan", "Michelle", "Ryan", "Gary"
$ salary <dbl> 623.30, 515.20, 611.00, 729.00, 843.25
$ start_date <date> 2012-01-01, 2013-09-23, 2014-11-15, 2014-05-11, 2015-03-27

The dplyr package provides a powerful and intuitive set of verbs for data manipulation. We often chain these verbs together using the pipe operator |> (or %>%). The pipe takes the result of the expression on its left and passes it as the first argument to the function on its right.

# Load dplyr
library(dplyr)
# Select specific columns
name_and_salary <- emp_data |>
select(emp_name, salary)
print(name_and_salary)
# Find employees with a salary over 700
high_earners <- emp_data |>
filter(salary > 700)
print(high_earners)
# Add a new department column and a calculated bonus column
emp_data_expanded <- emp_data |>
mutate(
dept = c("IT", "Operations", "IT", "HR", "Finance"),
bonus = salary * 0.10
)
print(emp_data_expanded)
# Create a new tibble with new employee data
new_hires <- tibble(
emp_id = 6:7,
emp_name = c("Rasmi", "Pranab"),
salary = c(578.0, 722.5),
start_date = as.Date(c("2023-05-21", "2023-07-30")),
dept = c("IT", "Operations")
)
# Combine the original and new data
# bind_rows is safer than rbind()
all_employees <- bind_rows(emp_data_expanded, new_hires)
print(all_employees)

The true power of dplyr comes from chaining operations to perform a complex query in a readable way. Let’s find the names of employees in the IT department who started after 2013.

it_employees_post_2013 <- all_employees |>
filter(dept == "IT", start_date > as.Date("2013-12-31")) |>
select(emp_name, salary)
print(it_employees_post_2013)

This readable chain of commands produces the final, filtered result:

# A tibble: 2 × 2
emp_name salary
<chr> <dbl>
1 Michelle 611
2 Rasmi 578

This is just the beginning! The next steps in mastering dplyr involve learning to group and summarize your data:

  • group_by(): Groups the data by one or more variables, so that subsequent operations are performed on each group separately.
  • summarise(): Collapses each group into a single row, calculating summary statistics like mean, sum, or count.
  • arrange(): Sorts the rows of your data by one or more columns.