df$new <- …, names(), <- NULL
◈ 9 cardsorders$share <- orders$amount / sum(orders$amount) creates a column in one vectorised line (1005 → 0.2149837, 21.5 %). Rename by matching the old name — names(orders)[names(orders) == "amount"] <- "amount_cad" — never by position. orders$tmp <- NULL drops a column. Assigning to a name that does not exist creates it.
A new column is one assignment
Module 5 built Maple & Birch's derived columns by writing one formula in row 2 and filling down. R has no fill-down because it has no rows to fill: a column is a vector, and arithmetic on a vector is already every row at once (Module 9). So a derived column is one line — assign to a column name that does not exist yet, and R creates it:
> sum(orders$amount)
[1] 6140
> orders$share <- orders$amount / sum(orders$amount)
> round(orders$share, 4)
[1] 0.0684 0.0293 0.1238 0.0407 0.2150 0.0831 0.0147 0.1042 0.0497 0.1596
[11] 0.0350 0.0765
sum(orders$amount) is a single number, 6140, recycled against all twelve amounts — the $D$14 absolute reference in Sheets, without the dollar signs. Order 1005 is the fifth row:
> orders$share[orders$order_id == 1005]
[1] 0.2149837
> round(orders$share[5] * 100, 1)
[1] 21.5
The $1,320 order is 21.5 % of the quarter. A second derived column follows the same shape — orders$unit_price <- ordersunits — and head(orders, 3) shows both sitting to the right of the original six:
> head(orders, 3)
order_id prov channel amount units order_date share unit_price
1 1001 ON Online 420 3 2025-01-06 0.06840391 140
2 1002 QC Store 180 1 2025-01-08 0.02931596 180
3 1003 ON Store 760 5 2025-01-15 0.12377850 152
No for loop over rows, no mutate() (that is dplyr, and it is not loaded), no rebuilding the frame with data.frame(). One vectorised line.
Worked example — renaming by name, not by position
The dashboard team wants amount called amount_cad. names(orders) is the header row as a character vector, and it can be assigned into:
> names(orders)
[1] "order_id" "prov" "channel" "amount" "units"
[6] "order_date" "share" "unit_price"
> names(orders)[names(orders) == "amount"] <- "amount_cad"
> names(orders)
[1] "order_id" "prov" "channel" "amount_cad" "units"
[6] "order_date" "share" "unit_price"
Read the left side inside-out: names(orders) == "amount" is eight TRUE/FALSEs, one per column, TRUE only at the fourth; the bracket picks that one name; the arrow overwrites it. names(orders)[4] <- "amount_cad" does the same thing today and the wrong thing the day someone adds a column in front of amount — the position shifts, the name does not. Match on the old name.
Dropping a column
A scratch column is removed by assigning NULL to it:
> orders$tmp <- 0
> names(orders)
[1] "order_id" "prov" "channel" "amount_cad" "units"
[6] "order_date" "share" "unit_price" "tmp"
> orders$tmp <- NULL
> names(orders)
[1] "order_id" "prov" "channel" "amount_cad" "units"
[6] "order_date" "share" "unit_price"
NULL is not a blank value; it is the absence of a column. After the second call there are eight names again, and orders$tmp is NULL — not a column of zeros, not a column of NAs. (orders[, -9] also drops the ninth column, and has the same positional fragility as names(orders)[4].)
Type the three lines, then reproduce the share in Python
The code block builds the same share list and prints its first element — 420 / 6140 to four places.
CRISP-DM: a derived column is data preparation → construct data; a rename is format data.