Adding a Column: df$new <- value
To add a column in base R, assign to a name that doesn't exist yet. The value is usually computed from columns that do:
Two things happened. orders$total got a full vector - price * qty is computed for every row at once, because arithmetic in R is vectorized. orders$currency got a single value, which R recycled to all four rows. Any other length is an error (replacement has 3 rows, data has 4), which is R protecting you from a misaligned column.
This works because a data frame is a list of equal-length columns - assigning a new named element grows the list. The data frames doc unpacks that model.
Conditional Columns: ifelse()
A column whose value depends on a condition wants ifelse() - the vectorized cousin of if. It takes a whole vector of conditions and returns a value per row:
Why not plain if? Because if evaluates exactly one TRUE/FALSE - handed a column, it errors (the condition has length > 1). ifelse(test, yes, no) checks each element and picks per row. For three or more outcomes you can nest:
Nesting two deep is fine; three or more turns into a pyramid - that is exactly the job dplyr's case_when() (below) was built for. The single-value if/else itself is covered in the if-else doc.
Several Columns at Once: transform()
Base R's transform() adds multiple columns in one call, with bare column names:
Note that transform() returns a new data frame - you must assign the result back, where df$new <- ... modifies in place. One limitation: the expressions inside one transform() call can't see each other, so discount = ifelse(total > 50, ...) fails there (total doesn't exist yet). Do it in two steps - or with mutate(), which allows it.
Modifying vs Creating
The same assignment syntax overwrites an existing column - whether that column existed already is the only difference:
This is worth pausing on: R gives you no warning when you overwrite a column. A typo like df$prise <- round(df$price, 2) silently creates a new column instead of fixing the old one. After a transform, print() or str() the frame and confirm you changed what you meant to.
Dropping a Column
Assign NULL to delete:
The column is gone, no ceremony. To drop several, df[, c("a", "b")] (keep what you list) is usually clearer than repeated NULLs.
The dplyr Way: mutate()
dplyr's verb for all of the above is mutate(). The snippets below are static - the sandbox runs base R only - but this is what the same operations look like:
library(dplyr)
orders |>
mutate(
total = price * qty,
big = total > 50 # can use total, defined one line up
)
Two advantages over base R. First, columns defined in the same mutate() can reference each other, in order - the thing transform() refuses. Second, the conditional helpers are better than nested ifelse():
students |>
mutate(
grade = if_else(score >= 60, "pass", "fail"),
band = case_when(
score >= 85 ~ "high",
score >= 60 ~ "mid",
.default = "low"
)
)
if_else() is a stricter ifelse() (both branches must be the same type - catches real bugs). case_when() reads as a flat list of condition ~ value pairs checked top to bottom, with .default as the fallback - the readable replacement for the ifelse() pyramid. Like every dplyr verb, mutate() returns a new data frame; see the dplyr intro for how it chains with the rest.
What You Take Away
df$new <- expressionadds a column; the expression runs for every row at once.ifelse(test, yes, no)builds conditional columns; plainifcannot.transform()adds several columns per call but they can't see each other;mutate()can.- The same assignment overwrites silently - check your spelling after a transform.
df$col <- NULLdrops a column;case_when()replaces nestedifelse()pyramids.
Next up: renaming columns - names(), the robust base idiom, and dplyr's rename().
Frequently Asked Questions
How do I add a new column to a data frame in R?
Assign to a column name that doesn't exist yet: df$total <- df$price * df$qty. R creates the column on the spot. The value can be a single value (recycled to every row) or a vector computed from other columns, as long as its length matches the number of rows.
How do I create a conditional column in R?
Use the vectorized ifelse(): df$grade <- ifelse(df$score >= 60, "pass", "fail") checks every row at once. Don't use a plain if - it handles a single condition, not a column. For more than two outcomes, nest ifelse() calls or use dplyr's case_when().
What does mutate() do in R?
mutate() is dplyr's verb for adding or transforming columns: df |> mutate(total = price * qty) returns a copy of the data frame with the new column. It can define several columns in one call, and later columns can use earlier ones defined in the same mutate.
How do I delete a column from a data frame in R?
Assign NULL to it: df$notes <- NULL removes the column in place. In dplyr, select(-notes) returns a copy without it.