Add a column to a table in base R and with dplyr’s mutate, build one column from another in the same call, and keep only the columns you need.
A column is a vector the same length as the table. Give R a vector of that length and a name, and you have a new column.
In this lesson I add a column two ways, base R first and then mutate(), build a second column out of the first in one call, and cut the table back down to the columns I want.
Step 1. Base R: name it on the left, vector on the right
p$Value <- ... creates the column if it does not exist and overwrites it if it does. The right-hand side has to be as long as the table, or length one, which R recycles down every row.
p <-data.frame( # a small table typed out by handTicker =c("AAA", "CCC", "DDD"), # first column, one entry per rowClose =c(186.66, 416.98, 125.18) # second column, same length as the first)print(ncol(p)) # -> [1] 2
[1] 2
print(nrow(p)) # -> [1] 3
[1] 3
Two columns, three rows. Now value ten shares of each.
p$Value <- p$Close *10# ten shares each, priced at the closeprint(p)
Ticker Close Value
1 AAA 186.66 1866.6
2 CCC 416.98 4169.8
3 DDD 125.18 1251.8
p$Close * 10 is a vector of length three, one number per row, so it fits. The table went from two columns to three.
Assigning NULL removes a column.
p$Value <-NULL# drop the column againprint(ncol(p)) # -> [1] 2
[1] 2
print(names(p)) # -> [1] "Ticker" "Close"
[1] "Ticker" "Close"
Step 2. The same move on the price file
prices.csv holds simulated daily closes and volumes for three tickers. read.csv() hands back Date as text, so I convert it with as.Date() before doing anything else.
library(dplyr)prices <-read.csv("prices.csv") # 120 rows, three tickers, simulatedprices$Date <-as.Date(prices$Date) # text to real datesprint(head(prices, 3)) # first three rows, all four columns
Date Ticker Close Volume
1 2026-01-02 AAA 186.66 2183494
2 2026-01-02 CCC 416.98 3117814
3 2026-01-02 DDD 125.18 7447373
prices$Traded_Value <-NULL# take it off againprint(ncol(prices)) # -> [1] 4
[1] 4
That line says prices three times. mutate() says it once.
Step 3. mutate names the table once
Inside mutate() you write column names bare, with no prices$ in front, because it already knows which table you mean.
with_traded <- prices |># prices goes in, a new table comes outmutate(Traded_Value = Close * Volume) # Close and Volume are columns of pricesprint(head(with_traded, 3)) # the new column sits on the end
Same numbers as the base R version. The difference is what happened to prices.
print(ncol(prices)) # -> [1] 4
[1] 4
print(ncol(with_traded)) # -> [1] 5
[1] 5
mutate() returned a new table and left prices alone. Four columns against five. If you want the column to stick, assign the result back with prices <- prices |> mutate(...). Running a mutate() without assigning it prints a table and changes nothing.
Step 4. Two columns in one call
You can list several columns in a single mutate(), separated by commas. They are created in the order you write them, so a later one can use an earlier one.
wide <- prices |>mutate(Volume_m = Volume /1e6, # shares traded, in millionsTraded_Value_m = Close * Volume_m # uses the column made one line above )print(head(wide, 3))
Traded_Value_m reads Volume_m, which did not exist when the call started. Check the first row: Traded_Value in Step 3 was 407570990, and 407570990 divided by a million is 407.5710. Swap the two lines around and R stops with an object not found error, because Volume_m is not there yet.
Step 5. Keep only what you need
Six columns is more than I want to look at. select() keeps the ones you name, in the order you name them.
small <- wide |>select(Date, Ticker, Traded_Value_m) # keep three columns, drop the other threeprint(head(small, 3))
prices still has its four columns. Nothing I did in this lesson touched it after Step 2.
Your turn
In one mutate() call, add Value holding what 100 shares are worth at the close, and Value_k holding that in thousands. Then keep only Date, Ticker and Value_k. Confirm prices still has four columns.
TipShow answer
book <- prices |>mutate(Value = Close *100, # 100 shares at the closeValue_k = Value /1000# the same figure in thousands ) |>select(Date, Ticker, Value_k) # keep three columnsprint(head(book, 3))# -> Date Ticker Value_k# -> 1 2026-01-02 AAA 18.666# -> 2 2026-01-02 CCC 41.698# -> 3 2026-01-02 DDD 12.518print(ncol(book)) # -> [1] 3print(ncol(prices)) # -> [1] 4 <- unchanged
Every column here is built row by row on its own. Lesson 15 builds one that depends on the row before it: a return.