Practice: sales pipeline

Load, clean, join, group, pivot, and export a small sales report.

Attach all five sample files (sales.csv, sales_log.csv, products.csv, regions.csv, messy.csv) with Add files. Paste each block as the whole editor — later blocks repeat the load so they still run alone.

Goal

Produce sales_report.csv with region, category, monthly revenue, and a margin column. Then print a city × product pivot.

1. Load and parse

import os

print("uploads:", os.listdir("/uploads"))

log = pd.read_csv("sales_log.csv", parse_dates=["date"])
products = pd.read_csv("products.csv")
regions = pd.read_csv("regions.csv")
print(log.head())
print(log.dtypes)
print("rows:", len(log))

2. Revenue and margin

log = pd.read_csv("sales_log.csv", parse_dates=["date"])
products = pd.read_csv("products.csv")
regions = pd.read_csv("regions.csv")
sales = log.merge(products, on="product", how="left", validate="many_to_one")
sales = sales.merge(regions, on="city", how="left", validate="many_to_one")
sales["revenue"] = sales["units"] * sales["price"]
sales["cost_total"] = sales["units"] * sales["cost"]
sales["margin"] = sales["revenue"] - sales["cost_total"]
print(sales.head())
print()
print("merge check — any NA region or category?")
print(sales[["region", "category"]].isna().sum())

validate="many_to_one" errors if a lookup key is duplicated — better than a silent row explosion.

3. Monthly totals by region

log = pd.read_csv("sales_log.csv", parse_dates=["date"])
products = pd.read_csv("products.csv")
regions = pd.read_csv("regions.csv")
sales = log.merge(products, on="product", how="left", validate="many_to_one")
sales = sales.merge(regions, on="city", how="left", validate="many_to_one")
sales["revenue"] = sales["units"] * sales["price"]
sales["cost_total"] = sales["units"] * sales["cost"]
sales["margin"] = sales["revenue"] - sales["cost_total"]
sales["month"] = sales["date"].dt.to_period("M").astype(str)
monthly = (
    sales.groupby(["month", "region"], as_index=False)
    .agg(
        units=("units", "sum"),
        revenue=("revenue", "sum"),
        margin=("margin", "sum"),
        orders=("units", "count"),
    )
    .sort_values(["month", "region"])
)
print(monthly)

4. Pivot: city × product

log = pd.read_csv("sales_log.csv", parse_dates=["date"])
sales = log.copy()
sales["revenue"] = sales["units"] * sales["price"]
grid = sales.pivot_table(
    index="city",
    columns="product",
    values="revenue",
    aggfunc="sum",
    fill_value=0,
    margins=True,
    margins_name="total",
)
print(grid)

5. Clean messy.csv

messy = pd.read_csv("messy.csv")
messy["name"] = messy["name"].str.strip().str.title()
messy["city"] = messy["city"].str.strip()
messy["joined"] = pd.to_datetime(messy["joined"], errors="coerce", format="mixed")
messy["score"] = pd.to_numeric(messy["score"], errors="coerce")
messy = messy.drop_duplicates()
print(messy)
print()
print(messy.isna().sum())

6. Export the report

import os

log = pd.read_csv("sales_log.csv", parse_dates=["date"])
products = pd.read_csv("products.csv")
regions = pd.read_csv("regions.csv")
sales = log.merge(products, on="product", how="left", validate="many_to_one")
sales = sales.merge(regions, on="city", how="left", validate="many_to_one")
sales["revenue"] = sales["units"] * sales["price"]
sales["cost_total"] = sales["units"] * sales["cost"]
sales["margin"] = sales["revenue"] - sales["cost_total"]
sales["month"] = sales["date"].dt.to_period("M").astype(str)
monthly = (
    sales.groupby(["month", "region"], as_index=False)
    .agg(
        units=("units", "sum"),
        revenue=("revenue", "sum"),
        margin=("margin", "sum"),
        orders=("units", "count"),
    )
    .sort_values(["month", "region"])
)
grid = sales.pivot_table(
    index="city",
    columns="product",
    values="revenue",
    aggfunc="sum",
    fill_value=0,
    margins=True,
    margins_name="total",
)
monthly.to_csv("sales_report.csv", index=False)
grid.to_csv("sales_pivot.csv")
print("wrote sales_report.csv and sales_pivot.csv")
print(os.listdir("/uploads"))

Download both chips. You now have a repeatable path: read → inspect → clean → join → group → pivot → write.

Extra drills

log = pd.read_csv("sales_log.csv", parse_dates=["date"])
products = pd.read_csv("products.csv")
regions = pd.read_csv("regions.csv")
sales = log.merge(products, on="product", how="left", validate="many_to_one")
sales = sales.merge(regions, on="city", how="left", validate="many_to_one")
sales["revenue"] = sales["units"] * sales["price"]
sales["cost_total"] = sales["units"] * sales["cost"]
sales["margin"] = sales["revenue"] - sales["cost_total"]

print("March margin by product")
print(sales.loc[sales["date"].dt.month == 3].groupby("product")["margin"].sum())
print()

nairobi = sales[sales["city"] == "Nairobi"]
print("Nairobi revenue share by product")
print((nairobi.groupby("product")["revenue"].sum() / nairobi["revenue"].sum()).round(3))
print()

sales["city_share"] = sales["revenue"] / sales.groupby("city")["revenue"].transform("sum")
print("shares should sum to 1 per city")
print(sales.groupby("city")["city_share"].sum())
print()

print("Hardware only, city x product")
print(
    sales[sales["category"] == "Hardware"].pivot_table(
        index="city", columns="product", values="revenue", aggfunc="sum", fill_value=0
    )
)
You should see

If a merge validation fails, print(products["product"].duplicated().sum()) and print(regions["city"].duplicated().sum()). Lookups must be unique on the key.