R Data Frames: Importing, Inspecting, Subsetting, and Summarizing Data
Edited by Dev Kilaru, Senior Data Analyst. How we make our content
Introduction
A data frame is R's table: each column is a vector, columns can have different types, and every column has the same length. Nearly every analysis starts by reading a file into a data frame and learning its structure. This lesson uses base R, which works everywhere without extra packages and which you will still see in older code, before the next lesson introduces dplyr for more readable data wrangling.
Prerequisites
- The previous lesson on vectors, types, and indexing
- An RStudio Project with a data folder
Step-by-Step Guide
Step 1: Build a data frame by hand
The data.frame function combines vectors of equal length into columns. Building a small table yourself is a good way to understand the structure before you load a large file. Each column keeps its own type, so a character column of names can sit beside a numeric column of salaries. Use the dollar sign to pull out a single column as a vector, and nrow and ncol to get the dimensions.
employees <- data.frame(
name = c("Asha", "Ben", "Chen", "Dina", "Eli"),
dept = c("Eng", "Eng", "Sales", "Sales", "HR"),
salary = c(85000, 95000, 60000, 65000, 55000)
)
nrow(employees) # 5
ncol(employees) # 3
employees$salary # the salary column as a vectorStep 2: Import a CSV file
Real data usually comes from a file. The read.csv function reads a comma-separated file into a data frame, using the first line as column names. Pass a relative path inside your project so the code works on any computer. Check the result immediately, because encoding, delimiters, and stray header lines are common problems. If your file uses semicolons, as is common in some European locales, use read.csv2 or the sep argument. You can write results back out with write.csv, and row.names = FALSE keeps R from adding a useless index column.
sales <- read.csv("data/sales.csv")
write.csv(employees, "output/employees.csv", row.names = FALSE)Step 3: Inspect before you analyze
Always look at a dataset before computing anything. The head function shows the first rows, str shows each column's type and a preview of its values, and summary gives quick statistics, counting values for text and factor columns and reporting quartiles for numeric ones. A numeric column that str reports as character usually has a stray non-numeric value. Checking for missing values with colSums and is.na at this stage tells you how much cleaning the file needs.
head(employees, 3)
str(employees)
summary(employees$salary)
colSums(is.na(employees)) # missing values per columnStep 4: Select rows and columns
A data frame is indexed with two positions inside square brackets, rows first and columns second. Leaving one side empty means all of them. Logical tests on columns produce row filters, and you can combine tests with & for and, and | for or. Note the trailing comma in the filter example, which says to keep all columns. Forgetting it is a classic mistake that produces a confusing error or the wrong shape.
employees[2, ] # second row, all columns
employees[ , c("name", "salary")] # all rows, two columns
employees[employees$salary > 60000, ] # Asha, Ben, Dina
employees[employees$dept == "Eng" & employees$salary > 90000, ] # BenStep 5: Add columns and sort rows
Assigning to a new column name with the dollar sign creates the column, calculated from existing columns for every row at once. To sort, use order, which returns the row positions that would put a column in order, and use those positions to reindex the data frame. Add decreasing = TRUE for descending order. Because assignment changes the data frame in your session only, nothing happens to the original file, which keeps raw data safe.
employees$bonus <- employees$salary * 0.10
employees$senior <- employees$salary >= 80000
employees[order(employees$salary, decreasing = TRUE), ]Step 6: Summarize by group
Analysis constantly asks for a statistic per category, such as average salary per department. The aggregate function takes a formula, with the value on the left of the tilde and the grouping column on the right, plus the data frame and the function to apply. The result is a new, smaller data frame. The tapply function does a similar job and returns a named vector. For several statistics at once, or for more readable code, the dplyr package in the next lesson is usually easier.
aggregate(salary ~ dept, data = employees, FUN = mean)
# dept salary
# 1 Eng 90000
# 2 HR 55000
# 3 Sales 62500
tapply(employees$salary, employees$dept, mean)FAQs
- What is the difference between a data frame and a matrix?
- A matrix holds a single type across the whole table, while a data frame lets each column have its own type. Use a data frame for mixed data such as names, dates, and numbers, and a matrix for purely numeric calculations.
- What is a tibble?
- A tibble is the tidyverse version of a data frame. It prints more compactly, never changes column names or types behind your back, and works with all data frame code. You create one with tibble or read one with readr's read_csv.
- Why does my filter return NA rows?
- When the test column contains NA, the comparison gives NA, and base R indexing returns a row of NAs for it. Add !is.na(column) to the condition, or use dplyr's filter, which drops those rows automatically.
Summary
You can now create and import data frames, inspect them with head, str, and summary, select rows and columns with square brackets, derive new columns, sort with order, and summarize by group with aggregate. These base R skills remain useful everywhere. Next you will learn to write your own functions and use R's apply family, which let you reuse logic instead of copying code.
Related tutorials
- Schedule and Debug Shell Scripts: cron, bash -x, and Portability
- Shell Scripting Project: A Backup Script with Rotation and a Log Report
- Writing Safe Bash Scripts: set -euo pipefail, getopts, and trap
- Shell Pipes, Redirection, grep, sed, and awk for Text Processing
- Bash Loops, Arrays, and Functions Explained
Comments
Log in or create a free account to join the discussion.