Module 4 · Combining and Summarizing Data

Build Derived Columns and Reusable Transformations

Lesson 13 of 32 5 min read Updated

Module 4: Combining and Summarizing Data · Lecture 13 of 32

Turn a written metric contract into small, reviewable transformations. Derive monetary columns with vector expressions and reusable functions while keeping the input table intact for comparison and testing.

What you will learn

  • Express revenue rules as vectorized transformations.
  • Write a function that returns a new result without mutating its input.
  • Check row-level invariants before aggregating.

Understand the problem

Translate each clause

The contract says gross equals units times unit price; discounts and refunds reduce merchandise revenue; cancellations contribute zero. Implement these as named intermediate columns so a reviewer can reconcile one row by hand. A dense one-line total hides which part of the contract is wrong when a result disagrees.

Prefer column expressions for column rules

Arithmetic, comparisons, where, mask, and string or datetime methods often describe a rule directly. A row-wise apply is useful when a genuinely irregular operation is required, but it should not be the default for arithmetic that already has a vector form. Compare semantic clarity and measured cost rather than turning “never use apply” into a slogan.

Make dependencies explicit

A reusable function accepts a table and returns a table. It should not depend on an unrelated notebook variable named df or overwrite the raw CSV as a side effect. Small pure transformations are easier to test, compose with pipe, and reuse in a command-line pipeline. Output writing belongs at a deliberate boundary after validation.

Preserve exactness until presentation

Derive gross, discount, refund, and net in integer paise. Ratios and averages can use floating-point values, but record their numerators and denominators. Check nonnegativity and refund bounds at row level. A grand total can still look reasonable when one negative row offsets one inflated positive row, so aggregate reconciliation alone is insufficient.

Run the worked example

Download and extract the course kit linked below. Open a terminal in the extracted folder, complete the setup in Lecture 2, and run python run_course.py --lesson 13 --examples-only. Each script starts from the shared dataset; it does not depend on a notebook cell from another lecture. All retail records are fictional and amounts are integer paise unless a chart explicitly says INR.

from pathlib import Path
import numpy as np
import pandas as pd
from course_data import load_orders, load_raw, load_customers, clean_orders, ROOT, OUT

def derive_revenue(frame):
    result = frame.assign(gross_check=frame['units'] * frame['unit_price_paise'])
    after_adjustments = result['gross_check'] - result['discount_paise'] - result['refunded_paise']
    return result.assign(net_check=after_adjustments.mask(result['status'].eq('cancelled'), 0))

orders = load_orders()
before = orders.copy(deep=True)
derived = orders.pipe(derive_revenue)
pd.testing.assert_frame_equal(orders, before)
assert derived['gross_check'].eq(derived['gross_paise']).all()
assert derived['net_check'].eq(derived['net_paise']).all()
assert derived['net_check'].ge(0).all()
print(derived[['order_id', 'gross_check', 'discount_paise', 'refunded_paise', 'net_check']].head(6).to_string(index=False))

Verified output

order_id  gross_check  discount_paise  refunded_paise  net_check
   O0001        19900            1990               0          0
   O0002        39800               0               0      39800
   O0003        59700               0               0      59700
   O0004       119600               0               0     119600
   O0005        29900               0               0      29900
   O0006        59800               0               0      59800

Walk through the result

The first assign creates a named gross value. The second assign uses that value and applies the cancellation policy in one visible expression. pipe passes the table to the function without hiding where it came from. Equality against the shared cleaner verifies that the independent derivation agrees with the established contract. The deep-copy comparison verifies an ownership property rather than merely checking the final total.

Common mistakes and diagnosis

Do not reuse a column name such as revenue for different definitions in different cells. Do not subtract an order-level discount once per item after a line-item join. A function that logs, writes files, mutates a global table, and calculates a ratio is hard to reuse safely; split transformation and delivery responsibilities. Avoid rounding each row into whole rupees before adding the result.

Practice assignment

Add a refund_ratio column defined only for non-cancelled orders with positive discounted gross. Leave the ratio missing when it is undefined. Assert that all observed ratios lie between zero and one.

Worked solution and explanation

Add the following code below the worked example, or run the matching file in the solutions folder. Compare the result with the business definition before trusting a passing assertion.

base_amount = orders['gross_paise'] - orders['discount_paise']
defined = orders['status'].ne('cancelled') & base_amount.gt(0)
ratios = orders.assign(refund_ratio=(orders['refunded_paise'] / base_amount).where(defined))
assert ratios.loc[defined, 'refund_ratio'].between(0, 1).all()
assert ratios.loc[~defined, 'refund_ratio'].isna().all()

Check your understanding

Why retain intermediate columns when the final expression can be written in one line?

Answer guide

They expose the business clauses, support row-level reconciliation, and make unexpected values easier to diagnose. Intermediates are particularly valuable during development and review. A production pipeline may drop them from a final delivery table after validation, while preserving the rules and audit evidence in code and tests.

Reference and next step

Download the complete course kit: datasets, all examples, solutions, and capstone.

Return to the full Data Analysis with Python and pandas curriculum.

Progress is saved in this browser.