EthicsComputerCourses
Join training +91 90532 10052

Data Frames

R Programming · Lesson 10 of 29 Beginner 6 min read Updated

A data frame is R's table: rows are records (one shop item, one student, one day) and columns are variables (price, marks, temperature). Unlike a matrix, each column can have its own type, so a name column of text can sit next to a price column of numbers. Nearly all data analysis in R happens in data frames.

In this lesson you will create a data frame, inspect it, add and compute columns, sort and add rows, and combine two tables with merge().

Creating a data frame

data.frame() takes named vectors of the same length; each one becomes a column. Here is the stock list of a kirana (grocery) shop:

RTry it yourself
shop <- data.frame(
  item  = c("Rice 5kg", "Atta 10kg", "Toor dal 1kg", "Sugar 1kg", "Tea 500g"),
  price = c(420, 460, 165, 48, 280),
  stock = c(25, 18, 40, 60, 12)
)
shop
str(shop)
dim(shop)
names(shop)
Output
          item price stock
1     Rice 5kg   420    25
2    Atta 10kg   460    18
3 Toor dal 1kg   165    40
4    Sugar 1kg    48    60
5     Tea 500g   280    12
'data.frame':	5 obs. of  3 variables:
 $ item : chr  "Rice 5kg" "Atta 10kg" "Toor dal 1kg" "Sugar 1kg" ...
 $ price: num  420 460 165 48 280
 $ stock: num  25 18 40 60 12
[1] 5 3
[1] "item"  "price" "stock"
  • The numbers 1 to 5 on the left are row names; R adds them automatically.
  • str() is the most useful first look at any data frame: 5 observations (rows) of 3 variables (columns), with each column's type (chr text, num numbers) and first values.
  • dim() gives rows and columns; nrow() and ncol() give each one alone.
  • names() lists the column names.

Prices here are made-up practice values.

Working with columns

A data frame is a list of columns, so $ gives you a column as a vector. Because columns are vectors, vectorised arithmetic creates new columns in one line:

RTry it yourself
shop <- data.frame(
  item  = c("Rice 5kg", "Atta 10kg", "Toor dal 1kg", "Sugar 1kg", "Tea 500g"),
  price = c(420, 460, 165, 48, 280),
  stock = c(25, 18, 40, 60, 12)
)
shop$value <- shop$price * shop$stock
shop$low_stock <- shop$stock < 20
shop
sum(shop$value)
summary(shop$price)
Output
          item price stock value low_stock
1     Rice 5kg   420    25 10500     FALSE
2    Atta 10kg   460    18  8280      TRUE
3 Toor dal 1kg   165    40  6600     FALSE
4    Sugar 1kg    48    60  2880     FALSE
5     Tea 500g   280    12  3360      TRUE
[1] 31620
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   48.0   165.0   280.0   274.6   420.0   460.0 

Assigning to a column name that does not exist yet creates it. value is the money tied up in each item and low_stock flags items to reorder. The whole stock is worth ₹31,620. To delete a column, assign NULL: shop$low_stock <- NULL.

Sorting and adding rows

Sort rows with order() inside the row position of [rows, columns]. Add rows with rbind(), giving a one-row data frame with the same column names:

RTry it yourself
shop <- data.frame(
  item  = c("Rice 5kg", "Atta 10kg", "Toor dal 1kg", "Sugar 1kg", "Tea 500g"),
  price = c(420, 460, 165, 48, 280),
  stock = c(25, 18, 40, 60, 12)
)
shop[order(shop$price, decreasing = TRUE), ]
shop <- rbind(shop, data.frame(item = "Salt 1kg", price = 28, stock = 50))
nrow(shop)
tail(shop, 2)
Output
          item price stock
2    Atta 10kg   460    18
1     Rice 5kg   420    25
5     Tea 500g   280    12
3 Toor dal 1kg   165    40
4    Sugar 1kg    48    60
[1] 6
      item price stock
5 Tea 500g   280    12
6 Salt 1kg    28    50

order(shop$price, decreasing = TRUE) returns the row positions from the most to the least expensive, and the empty column position after the comma keeps all columns. The row names travel with their rows, so you can see the original positions. Note that sorting does not change shop unless you assign the result back. tail() shows the last rows, just as head() shows the first.

Combining two tables with merge()

Real data is often split across tables. Orders store which customer bought which item; prices are in the stock table. merge() joins them on a shared column, like a lookup:

RTry it yourself
shop <- data.frame(
  item  = c("Rice 5kg", "Atta 10kg", "Sugar 1kg", "Tea 500g"),
  price = c(420, 460, 48, 280)
)
orders <- data.frame(
  customer = c("Aarav", "Isha", "Aarav", "Farhan"),
  item     = c("Tea 500g", "Rice 5kg", "Sugar 1kg", "Tea 500g"),
  qty      = c(2, 1, 3, 1)
)
bill <- merge(orders, shop, by = "item")
bill$amount <- bill$qty * bill$price
bill
aggregate(amount ~ customer, data = bill, FUN = sum)
Output
       item customer qty price amount
1  Rice 5kg     Isha   1   420    420
2 Sugar 1kg    Aarav   3    48    144
3  Tea 500g    Aarav   2   280    560
4  Tea 500g   Farhan   1   280    280
  customer amount
1    Aarav    704
2   Farhan    280
3     Isha    420

merge() matches rows where item is equal and puts the key column first. By default it keeps only items found in both tables, like an SQL inner join; all.x = TRUE keeps every order even without a price. aggregate(amount ~ customer, ...) then adds up the amounts for each customer. The ~ is a formula: "amount, grouped by customer". You will see more grouping in The apply Family.

Built-in data frames

The practice data sets that come with R are data frames, and you can use them anywhere:

RTry it yourself
str(airquality)
head(iris, 3)
Output
'data.frame':	153 obs. of  6 variables:
 $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
 $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
 $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
 $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...
 $ Month  : int  5 5 5 5 5 5 5 5 5 5 ...
 $ Day    : int  1 2 3 4 5 6 7 8 9 10 ...
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa

airquality has 153 days of New York air readings, with NA for missing values. Type ?airquality in RStudio or Positron to read what each column means.

Common mistakes

  • Columns of different lengths. data.frame(a = 1:3, b = 1:2) gives an error: every column needs one value per row.
  • Forgetting the comma. shop[order(shop$price)] (no comma) picks columns, not rows. Write shop[order(shop$price), ].
  • Expecting sorting to save itself. Assign the result: shop <- shop[order(shop$price), ].
  • Numbers stored as text. If str() shows chr for a price column, convert it with as.numeric() after cleaning.

Key points

  • A data frame is a table whose columns are vectors of equal length; columns can have different types.
  • str(), head(), dim(), names() and summary() are your first look at any data.
  • df$col gets a column; assigning to a new name adds a column, and assigning NULL deletes it.
  • Sort rows with df[order(df$col), ] and add rows with rbind().
  • merge() joins two tables on a shared column; aggregate() summarises by group.

Exercise

Create a data frame of five trains with columns train ("Shatabdi", "Duronto", "Garib Rath", "Jan Shatabdi", "Vande Bharat"), distance_km (450, 1350, 980, 520, 760) and hours (6.5, 16, 15, 8.5, 8). Add a column speed with the average speed in km/h rounded to 1 decimal place, then print the trains sorted from fastest to slowest. The numbers are invented for practice.

Show solution
R
trains <- data.frame(
  train = c("Shatabdi", "Duronto", "Garib Rath", "Jan Shatabdi", "Vande Bharat"),
  distance_km = c(450, 1350, 980, 520, 760),
  hours = c(6.5, 16, 15, 8.5, 8)
)
trains$speed <- round(trains$distance_km / trains$hours, 1)
trains[order(trains$speed, decreasing = TRUE), ]
Output
         train distance_km hours speed
5 Vande Bharat         760   8.0  95.0
2      Duronto        1350  16.0  84.4
1     Shatabdi         450   6.5  69.2
3   Garib Rath         980  15.0  65.3
4 Jan Shatabdi         520   8.5  61.2

Dividing one column by another works row by row, and order(..., decreasing = TRUE) returns the row positions from fastest to slowest.

Quick Quiz

1. What is the main difference between a matrix and a data frame?

2. Which function gives a compact overview of every column's type and first values?

3. How do you sort df by the age column?

4. What does merge(a, b, by = "id") keep by default?

Learn R Programming with a mentor

Free lessons get you started. Our Data Science & Machine Learning training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call