Data Frames

Reviewed & published by Brayan K

A data frame is R's spreadsheet-like table — rows of observations and columns of variables, where each column can hold a different type — and it's the central structure for nearly all data analysis.

Part of the free R course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

By the end of this lesson you'll create data frames, select columns and filter rows, add computed columns, and explore a new dataset with str(), head(), and summary().

What You'll Learn in This Lesson

1️⃣ Creating a Data Frame

Build one with data.frame(), passing each column as a named vector. All columns must be the same length, but they can be different types. The columns are really just the vectors you've already learned.

# A data frame is a table where each COLUMN can be a different type.
# It's the central structure for real data analysis in R.

people <- data.frame(
  name = c("Ada", "Bob", "Cara"),
  age = c(36, 28, 41),
  city = c("London", "Paris", "Berlin")
)
people
#   name age   city
# 1  Ada  36 London
# 2  Bob  28  Paris
# 3 Cara  41 Berlin

ncol(people)         # columns -> [1] 3
nrow(people)         # rows    -> [1] 3

2️⃣ Selecting and Filtering

Grab a column with $, or use df[rows, cols] like a matrix. The most important move in analysis is filtering rows — put a logical test in the row slot.

# Access a column with $ (it's a vector), or use [row, col] like a matrix.

people <- data.frame(
  name = c("Ada", "Bob", "Cara"),
  age = c(36, 28, 41)
)

people$age           # a column -> [1] 36 28 41
people[1, ]          # first row (all columns)
people[, "name"]     # name column -> [1] "Ada"  "Bob"  "Cara"

# Filter ROWS with a logical condition on a column:
people[people$age > 30, ]   # only people older than 30

The pattern df[df$age > 30, ] keeps only the rows where the test is TRUE — the everyday verb of data analysis. (dplyr, two lessons from now, gives an even cleaner way.)

3️⃣ Exploring and Extending

When you meet a new dataset, run str(), head(), and summary() first. Adding a computed column is a single assignment — and it's vectorized, so it fills every row at once.

# Exploration functions you'll use on EVERY new dataset.

df <- data.frame(
  product = c("pen", "pad", "ink", "pad"),
  price = c(2.5, 5.0, 9.0, 5.0)
)

head(df, 2)          # first 2 rows
str(df)              # structure: types and a preview
summary(df$price)    # min, median, mean, max...

# Add a new column by assignment:
df$tax <- df$price * 0.2
df$tax               # -> [1] 0.5 1.0 1.8 1.0

Your turn. Fill in the ___ blank, run it, and compare with the expected output.

# 🎯 YOUR TURN — replace each ___ then run it.

students <- data.frame(
  name = c("Mia", "Leo", "Zoe", "Sam"),
  score = c(72, 91, 68, 85)
)

# 1) Add a "passed" column: TRUE when score >= 70
students$passed <- students$score ___ 70   # 👉 replace ___ with >=

# 2) Print only the rows where passed is TRUE
print(students[students$passed, ])

# ✅ Expected output:
#   name score passed
# 1  Mia    72   TRUE
# 2  Leo    91   TRUE
# 4  Sam    85   TRUE

Mini-Challenge: A Sales Table

Write it from the outline, run it, and check it against the example output. which.max() finds the position of the largest value — perfect for "the top row".

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Data Frames

TaskR Syntax
Createdata.frame(a = 1:3, b = ...)
A columndf$col
Filter rowsdf[df$x > 5, ]
Add a columndf$new <- ...
Explorestr(df) ; head(df)
Per-column statssummary(df)

🎉 Lesson Complete!

Practice quiz

What is true of the columns of a data frame?

  • All columns must be numeric
  • Columns may have different lengths
  • Each column can be a different type, but all have the same length
  • Columns are always factors

Answer: Each column can be a different type, but all have the same length. A data frame is a list of equal-length vectors that can differ in type.

Which function builds a data frame from named vectors?

  • data.frame()
  • as.matrix()
  • list()
  • table()

Answer: data.frame(). data.frame(name = ..., age = ...) constructs a data frame.

How do you extract the age column from a data frame people?

  • people@age
  • people->age
  • people::age
  • people$age

Answer: people$age. The $ operator returns a column as a vector.

What does people[1, ] return?

  • The first column, all rows
  • The first row, all columns
  • The element at row 1 column 1
  • The column named 1

Answer: The first row, all columns. With df[rows, cols], a blank column slot means all columns.

Which expression keeps only rows where age is over 30?

  • people[people$age > 30, ]
  • people[people$age > 30]
  • people(age > 30)
  • filter(people > 30)

Answer: people[people$age > 30, ]. Put the logical test in the row slot, and don't forget the comma.

How do you add a computed tax column equal to price times 0.2?

  • df + tax = price * 0.2
  • add(df, tax)
  • df$tax <- df$price * 0.2
  • df[tax] == price * 0.2

Answer: df$tax <- df$price * 0.2. Assigning to a new $ name creates the column; the math is vectorized.

Which function shows column types and a compact preview?

  • dim(df)
  • str(df)
  • print(df)
  • rev(df)

Answer: str(df). str() reports the structure: types and a preview of each column.

What does nrow(df) return?

  • The number of columns
  • The column names
  • The first row
  • The number of rows

Answer: The number of rows. nrow() gives rows; ncol() gives columns.

Why is df[df$age > 30] (no comma) usually wrong for filtering rows?

  • It always errors
  • Without the comma it selects columns, not rows
  • It deletes the data frame
  • It rounds the values

Answer: Without the comma it selects columns, not rows. The comma separates the row slot from the column slot; omit it and you index columns.

What does df$Age return if the column is actually named age?

  • The age column
  • An error
  • NULL
  • An empty string

Answer: NULL. Column lookup is case-sensitive; a wrong name yields NULL.

Continue this course

Frequently asked questions

What is a data frame and why is it so central to R?

A data frame is a table of data: rows are observations and columns are variables, exactly like a spreadsheet or a database table. What makes it special is that each column can be a different type — text, numbers, factors, dates — while every column has the same length. Almost all data analysis in R revolves around data frames: you read a CSV into one, clean it, filter and summarise it, and feed it to plots and models. Mastering data frames is the gateway to everything else.

How do I select rows versus columns in a data frame?

Use df[rows, columns] like a matrix. df$colname or df[, "colname"] grabs a column (as a vector). df[2, ] grabs the second row. To filter rows by a condition, put a logical test in the row slot: df[df$age > 30, ] keeps rows where age exceeds 30. Leaving a slot blank means 'all' — df[1, ] is all columns of row 1, and df[, 1] is all rows of column 1.

How do I add a new column?

Just assign to a new name with $: df$total <- df$price * df$qty. R creates the column and fills it, applying the calculation row by row thanks to vectorization. You can also overwrite an existing column the same way. Because the operation is vectorized, you don't need a loop — one line computes the value for every row at once.

What functions help me explore a new data frame?

Start with str() to see the column types and a preview, head() to view the first few rows, and summary() for per-column statistics. nrow() and ncol() give the dimensions, and names() lists the column names. Running these the moment you load data tells you what you're working with and catches problems — like a numeric column accidentally read as text — before they cause trouble.