BitBootcampBitBootcamp ← all explainers 🔄 Review ⚙ Settings

PD-01-009-P · Primer — Adding Columns

The idea in one sentence

Adding a column means writing a new named series into your table — usually computed from columns you already have — with a single vectorized expression.

Why it exists

Raw data almost never arrives with every field you need. You have price and qty, but you want total. You have a birth year, but you want an age. Rather than looping row by row (slow and error-prone), pandas lets you describe the whole new column as one expression over existing columns, and it fills every row at once. This turns "compute a value for each of a million rows" into a single readable line. Derived columns are the everyday workhorse of data prep — they are how you enrich, flag, and reshape a table before analysis.

How it works

To add a column, assign to a new key with bracket notation: df['new'] = <expression>. When the right-hand side is an operation on existing columns (like df['price'] * df['qty']), pandas applies it element-wise across every row — this is a vectorized operation, and it's fast because it runs in optimized C rather than a Python loop. You can also build boolean columns from comparisons (df['total'] > 20), and the result aligns to the same index automatically. If you prefer a style that returns a new DataFrame instead of mutating in place, use .assign(), which is handy inside method chains. Both approaches accept scalars too: df['tax_rate'] = 0.1 broadcasts a single value to every row.

import pandas as pd

df = pd.DataFrame({
    'name':  ['Ava', 'Ben', 'Cy'],
    'price': [10.0, 25.0, 8.0],
    'qty':   [3, 1, 5],
})

df['total'] = df['price'] * df['qty']   # vectorized: one expr, all rows
df['flag']  = df['total'] > 20          # boolean derived column
print(df)
#   name  price  qty  total  flag
# 0  Ava   10.0    3   30.0  True
# 1  Ben   25.0    1   25.0  True
# 2   Cy    8.0    5   40.0  True

# .assign() returns a NEW DataFrame (great for chaining):
print(df.assign(net=df['total'] * 0.9))
#   name  price  qty  total  flag   net
# 0  Ava   10.0    3   30.0  True  27.0
# 1  Ben   25.0    1   25.0  True  22.5
# 2   Cy    8.0    5   40.0  True  36.0

The one gotcha

Reach for vectorized column expressions, not Python loops. Beginners often write for i in range(len(df)): df.loc[i, 'total'] = ... — this is slow and fragile. Do the whole column at once (df['total'] = df['price'] * df['qty']). Also note the two styles differ in effect: df['x'] = ... mutates df in place, while .assign() leaves the original untouched and hands you a new DataFrame — so remember to capture its return value (df = df.assign(...)) if you want to keep the change.

See it — one expression fills the whole new column

flowchart LR
  A["Before<br/>price | qty"] --> B["Vectorized expr<br/>price * qty"]
  B --> C["After<br/>price | qty | total"]
  D["Row Ava: 10 * 3"] --> E["total = 30"]
  F["Row Cy: 8 * 5"] --> G["total = 40"]

The one-line takeaway

df['new'] = <expression over existing columns> computes a derived column for every row at once — vectorized, fast, and readable.

Where you'll meet it

Every data-prep pipeline: computing revenue from price and quantity, deriving age from a date, flagging outliers with a boolean, or normalizing a score before you group and summarize.

Named concepts: assignment, derived column, vectorized ops, assign