0% found this document useful (0 votes)
8 views17 pages

Python Programming

The document outlines an E-commerce Data Analysis Toolkit that includes RFM segmentation, cohort analysis, delivery performance metrics, and city-wise analysis for customer behavior and operational insights. It provides Python code for data processing, including loading orders, enriching customer data, computing order-level metrics, and generating key performance indicators (KPIs). The toolkit aims to enhance marketing strategies and improve logistics reliability through detailed customer and delivery analysis.

Uploaded by

sudamkhamari1234
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views17 pages

Python Programming

The document outlines an E-commerce Data Analysis Toolkit that includes RFM segmentation, cohort analysis, delivery performance metrics, and city-wise analysis for customer behavior and operational insights. It provides Python code for data processing, including loading orders, enriching customer data, computing order-level metrics, and generating key performance indicators (KPIs). The toolkit aims to enhance marketing strategies and improve logistics reliability through detailed customer and delivery analysis.

Uploaded by

sudamkhamari1234
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

What’s includedRFM segmentation computes Recency, Frequency, and

Monetary value per customer to support behavior-based segmentation


suitable for marketing and lifetime value strategies.

�Cohort analysis groups customers by the month of first purchase and


tracks retention across periods to reveal repeat purchasing patterns over
time. �

Delivery performance includes on-time delivery rate using the formula [ \


text{OTD} = \frac{\text{On-time Deliveries}}{\text{Total Deliveries}} \
times 100 ], enabling reliability tracking for logistics operations.

�City-wise analysis aggregates orders, revenue, customers, and delivery


KPIs by location to surface regional performance differences and operational
opportunities.

Python code

# E-commerce Data Analysis Toolkit

# Orders • Customers • Delivery Status • City-wise Analysis

# RFM Segmentation • Cohort Retention • On-time Delivery

From __future__ import annotations

Import pandas as pd

Import numpy as np

From pathlib import Path

From dataclasses import dataclass

From typing import Optional, Dict, List, Tuple

From datetime import datetime

Import warnings

[Link](“ignore”)
@dataclass

Class Schema:

# Minimum columns expected in the orders dataset; map to actual column


names if different.

Order_id: str = “order_id”

Order_date: str = “order_date”

Customer_id: str = “customer_id”

City: str = “city”

State: str = “state”

Country: str = “country”

Status: str = “status” # e.g., ‘delivered’, ‘shipped’, ‘canceled’

Promised_date: str = “promised_date” # promised delivery date

Delivered_date: str = “delivered_date” # actual delivery date (nullable)

Item_id: str = “item_id”

Quantity: str = “quantity”

Unit_price: str = “unit_price”

Discount: str = “discount” # absolute discount amount for the line (can
be 0)

Shipping_cost: str = “shipping_cost” # per order or allocated per line


(optional)

# Optional customer attributes table (if separate)

# If provided, must include at least customer_id

Cust_table_customer_id: Optional[str] = None


@dataclass

Class Paths:

Orders_csv: Path

Customers_csv: Optional[Path] = None # optional, enrich customers if


available

Output_dir: Path = Path(“outputs”)

Def _to_datetime_safe(s: [Link]) -> [Link]:

Return pd.to_datetime(s, errors=”coerce”, utc=True).dt.tz_localize(None)

Def load_orders(paths: Paths, schema: Schema) -> [Link]:

Df = pd.read_csv(paths.orders_csv)

# Normalize column names (lowercase, strip)

[Link] = [[Link]() for c in [Link]]

# Parse dates

If schema.order_date in [Link]:

Df[schema.order_date] = _to_datetime_safe(df[schema.order_date])

If schema.promised_date in [Link]:

Df[schema.promised_date] =
_to_datetime_safe(df[schema.promised_date])

If schema.delivered_date in [Link]:

Df[schema.delivered_date] =
_to_datetime_safe(df[schema.delivered_date])

# Numeric safeguards

For col in [[Link], schema.unit_price, [Link]]:


If col in [Link]:

Df[col] = pd.to_numeric(df[col], errors=”coerce”).fillna(0)

If schema.shipping_cost in [Link]:

Df[schema.shipping_cost] = pd.to_numeric(df[schema.shipping_cost],
errors=”coerce”).fillna(0)

Else:

Df[schema.shipping_cost] = 0.0

# Compute line revenue net of discount (excluding shipping; add later per
order)

Df[“line_revenue”] = df[[Link]].fillna(0) *
df[schema.unit_price].fillna(0) – df[[Link]].fillna(0)

# Order-level shipping allocation: if shipping_cost is recorded per order,


allocate evenly across lines of the order.

# If it’s already per line, remove this allocation logic as needed.

If schema.order_id in [Link] and schema.shipping_cost in [Link]:

Ship_per_order = [Link](schema.order_id)
[schema.shipping_cost].transform(“max”)

Lines_per_order = [Link](schema.order_id)
[schema.item_id].transform(“nunique”).clip(lower=1)

Df[“line_shipping_alloc”] = ship_per_order / lines_per_order

Else:

Df[“line_shipping_alloc”] = 0.0

Df[“line_total”] = df[“line_revenue”] + df[“line_shipping_alloc”]

# Status normalization
If [Link] in [Link]:

Df[[Link]] = df[[Link]].[Link]().[Link]()

Else:

Df[[Link]] = “unknown”

# City/state/country fill

For col in [[Link], [Link], [Link]]:

If col not in [Link]:

Df[col] = “Unknown”

Df[col] = df[col].fillna(“Unknown”).astype(str).[Link]()

# Filter out canceled or invalid rows for revenue-calculating analyses

Return df

Def enrich_customers(df_orders: [Link], paths: Paths, schema:


Schema) -> [Link]:

If paths.customers_csv is None:

Return df_orders

Cust = pd.read_csv(paths.customers_csv)

[Link] = [[Link]() for c in [Link]]

Key = schema.cust_table_customer_id or schema.customer_id

If key not in [Link]:

Return df_orders

Return df_orders.merge(cust, left_on=schema.customer_id, right_on=key,


how=”left”)
Def compute_order_level(df: [Link], schema: Schema) ->
[Link]:

# Aggregate lines to orders

Agg = [Link](schema.order_id).agg(

Order_date=(schema.order_date, “max”),

Customer_id=(schema.customer_id, “first”),

City=([Link], “first”),

State=([Link], “first”),

Country=([Link], “first”),

Status=([Link], lambda s: [Link]().iat[0] if len([Link]()) else


“unknown”),

Promised_date=(schema.promised_date, “max”),

Delivered_date=(schema.delivered_date, “max”),

Items=(“line_total”, “size”),

Quantity=([Link], “sum”),

Revenue=(“line_revenue”, “sum”),

Shipping=(“line_shipping_alloc”, “sum”),

Total_value=(“line_total”, “sum”),

).reset_index()

# On-time delivery flag (consider only delivered)

Agg[“is_delivered”] = ~agg[“delivered_date”].isna()

Agg[“on_time”] = [Link](

Agg[“is_delivered”] & agg[“promised_date”].notna(),

(agg[“delivered_date”] <= agg[“promised_date”]).astype(int),

[Link], # unknown if not delivered or promise missing

)
# Calendar helpers

Agg[“order_month”] = agg[“order_date”].dt.to_period(“M”).astype(str)

Return agg

Def customer_first_order(orders: [Link], schema: Schema) ->


[Link]:

Firsts = (

[Link](schema.customer_id)[“order_date”]

.min()

.reset_index()

.rename(columns={“order_date”: “first_order_date”})

Firsts[“cohort_month”] =
firsts[“first_order_date”].dt.to_period(“M”).astype(str)

Return firsts

Def compute_customer_aggregates(orders: [Link], schema: Schema,


as_of: Optional[[Link]] = None) -> [Link]:

Firsts = customer_first_order(orders, schema)

As_of = as_of or orders[“order_date”].max()

Cx = (

[Link](schema.customer_id)

.agg(

Orders=(“order_id”, “nunique”),

Revenue=(“revenue”, “sum”),

Total_value=(“total_value”, “sum”),
Last_order=(“order_date”, “max”),

.reset_index()

.merge(firsts, on=schema.customer_id, how=”left”)

# RFM

Cx[“recency_days”] = (as_of – cx[“last_order”]).[Link]

Cx[“frequency”] = cx[“orders”].fillna(0)

Cx[“monetary”] = cx[“revenue”].fillna(0.0)

# RFM scoring (quantile-based; reverse for recency: lower days =>


better/higher score)

# Adjust bins if desired; ensure unique edges by rank method

Cx[“r_score”] = [Link](cx[“recency_days”].rank(method=”first”), q=5,


labels=[5, 4, 3, 2, 1]).astype(int)

Cx[“f_score”] = [Link](cx[“frequency”].rank(method=”first”), q=5,


labels=[1, 2, 3, 4, 5]).astype(int)

Cx[“m_score”] = [Link](cx[“monetary”].rank(method=”first”), q=5,


labels=[1, 2, 3, 4, 5]).astype(int)

Cx[“rfm_score”] = cx[“r_score”] + cx[“f_score”] + cx[“m_score”]

# Simple segmentation tiers

Cx[“rfm_segment”] = [Link](cx[“rfm_score”].rank(method=”first”), q=3,


labels=[“Low-Value”, “Mid-Value”, “High-Value”])

Return cx
Def build_cohort_retention(orders: [Link], schema: Schema) ->
Tuple[[Link], [Link]]:

# Build a customer-level table of first purchase month (cohort) and each


order month

Df = orders[[schema.customer_id, “order_id”,
“order_date”]].dropna().drop_duplicates()

Df[“cohort”] = [Link](schema.customer_id)
[“order_date”].transform(“min”).dt.to_period(“M”)

Df[“order_month”] = df[“order_date”].dt.to_period(“M”)

# Count unique active customers per cohort per period index

Cohorts = (

[Link]([“cohort”, “order_month”])

.agg(n_customers=(schema.customer_id, “nunique”))

.reset_index()

Cohorts[“period_index”] = (cohorts[“order_month”] –
cohorts[“cohort”]).apply(lambda p: p.n)

# Pivot to retention matrices

Retention_abs = cohorts.pivot_table(index=”cohort”,
columns=”order_month”, values=”n_customers”, aggfunc=”sum”).fillna(0)

Base_sizes = [Link][cohorts[“period_index”] == 0, [“cohort”,


“n_customers”]].set_index(“cohort”)[“n_customers”]

Retention_rel = (

Cohorts.pivot_table(index=”cohort”, columns=”period_index”,
values=”n_customers”, aggfunc=”sum”)

.div(base_sizes, axis=0)

.fillna(0.0)

)
# Stringify periods for output readability

Retention_abs.index = retention_abs.[Link](str)

Retention_abs.columns = retention_abs.[Link](str)

Retention_rel.index = retention_rel.[Link](str)

Retention_rel.columns = [str(c) for c in retention_rel.columns]

Return retention_abs, retention_rel

Def delivery_kpis(orders: [Link]) -> [Link]:

# Compute OTD at various grains

Df = [Link]()

# Only consider delivered orders for OTD numerator/denominator

Delivered = df[df[“is_delivered”]].copy()

Delivered[“on_time_flag”] = delivered[“on_time”].fillna(0).astype(int)

Def rate(x: [Link]) -> float:

Denom = [Link]

Num = [Link]()

Return float(num) / float(denom) if denom > 0 else [Link]

# Daily, weekly, monthly OTD rates

Otd_by_day = [Link](delivered[“delivered_date”].[Link])
[“on_time_flag”].apply(rate).rename(“otd_rate”).reset_index()

Otd_by_week =
[Link](delivered[“delivered_date”].dt.to_period(“W”))
[“on_time_flag”].apply(rate).rename(“otd_rate”).reset_index()
Otd_by_month =
[Link](delivered[“delivered_date”].dt.to_period(“M”))
[“on_time_flag”].apply(rate).rename(“otd_rate”).reset_index()

# Overall OTD

Overall = [Link]({“metric”: [“otd_overall”], “value”:


[rate(delivered[“on_time_flag”])]})

Return {

“otd_by_day”: otd_by_day,

“otd_by_week”: otd_by_week,

“otd_by_month”: otd_by_month,

“otd_overall”: overall,

Def city_level(orders: [Link], schema: Schema) -> [Link]:

# Summarize by city

G = [Link]([Link]).agg(

Orders=(“order_id”, “nunique”),

Customers=(schema.customer_id, “nunique”),

Revenue=(“revenue”, “sum”),

Shipping=(“shipping”, “sum”),

Total_value=(“total_value”, “sum”),

Delivered=(“is_delivered”, “sum”),

Delivered_orders=(“is_delivered”, “sum”),

On_time_deliveries=(“on_time”, lambda s: [Link](0).sum()),

Delivered_with_promise=(“promised_date”, lambda s: [Link]().sum()),


).reset_index()

# OTD denominator is delivered_with_promise to handle missing promised


dates

G[“otd_rate”] = [Link](

G[“delivered_with_promise”] > 0,

G[“on_time_deliveries”] / g[“delivered_with_promise”],

[Link],

# Per-order averages

G[“avg_order_value”] = [Link](g[“orders”] > 0, g[“total_value”] /


g[“orders”], [Link])

G[“avg_revenue_per_order”] = [Link](g[“orders”] > 0, g[“revenue”] /


g[“orders”], [Link])

# Sort by revenue descending

G = g.sort_values(“revenue”, ascending=False).reset_index(drop=True)

Return g

Def kpis_overview(orders: [Link]) -> [Link]:

# High-level KPIs table

Total_orders = orders[“order_id”].nunique()

Total_customers = orders[“customer_id”].nunique()

Delivered_orders = orders[“is_delivered”].sum()

Revenue = orders[“revenue”].sum()

Shipping = orders[“shipping”].sum()
Total_value = orders[“total_value”].sum()

Avg_order_value = total_value / total_orders if total_orders > 0 else [Link]

Return [Link](

“metric”: [

“total_orders”,

“total_customers”,

“delivered_orders”,

“revenue”,

“shipping”,

“total_value”,

“avg_order_value”,

],

“value”: [

Total_orders,

Total_customers,

Delivered_orders,

Revenue,

Shipping,

Total_value,

Avg_order_value,

],

)
Def save_frames(frames: Dict[str, [Link]], outdir: Path) -> None:

[Link](parents=True, exist_ok=True)

For name, df in [Link]():

P = outdir / f”{name}.csv”

Df.to_csv(p, index=False)

Def run_analysis(

Orders_path: str,

Customers_path: Optional[str] = None,

Output_dir: str = “outputs”,

Schema: Schema = Schema(),

) -> Dict[str, [Link]]:

Paths = Paths(orders_csv=Path(orders_path),
customers_csv=Path(customers_path) if customers_path else None,
output_dir=Path(output_dir))

# Load

Raw = load_orders(paths, schema)

Enriched = enrich_customers(raw, paths, schema)

# Order-level aggregation

Orders = compute_order_level(enriched, schema)

# KPIs

Kpi = kpis_overview(orders)
# Customer aggregates + RFM

Cx = compute_customer_aggregates(orders, schema)

# Cohorts

Retention_abs, retention_rel = build_cohort_retention(orders, schema)

# Delivery

Delivery = delivery_kpis(orders)

# City-level

City = city_level(orders, schema)

# Exports

Frames = {

“orders_agg”: orders,

“kpis_overview”: kpi,

“customers_rfm”: cx,

“cohort_retention_absolute”: retention_abs.reset_index(),

“cohort_retention_relative”: retention_rel.reset_index(),

“delivery_otd_by_day”: delivery[“otd_by_day”],

“delivery_otd_by_week”: delivery[“otd_by_week”],

“delivery_otd_by_month”: delivery[“otd_by_month”],

“delivery_otd_overall”: delivery[“otd_overall”],

“city_summary”: city,

Save_frames(frames, paths.output_dir)

Return frames
If __name__ == “__main__”:

# Example usage:

# 1) Ensure orders CSV has columns matching Schema or adjust Schema


accordingly.

# 2) Run the script; outputs will be saved as CSVs in ./outputs

Frames = run_analysis(

Orders_path=”data/[Link]”,

Customers_path=None, # e.g., “data/[Link]” if available

Output_dir=”outputs”,

Schema=Schema(

Order_id=”order_id”,

Order_date=”order_date”,

Customer_id=”customer_id”,

City=”city”,

State=”state”,

Country=”country”,

Status=”status”,

Promised_date=”promised_date”,

Delivered_date=”delivered_date”,

Item_id=”item_id”,

Quantity=”quantity”,

Unit_price=”unit_price”,

Discount=”discount”,

Shipping_cost=”shipping_cost”,

),
)

# After running, inspect ./outputs/*.csv

How to adaptRFM scoring granularity can be changed by adjusting quantile


bins or using fixed binning for recency, frequency, and monetary scores
depending on business thresholds. �

Cohort definitions can shift from first purchase month to acquisition channel
or campaign to align retention tracking with marketing segmentation.

�On-time delivery can be segmented by region, carrier, or product category


to isolate bottlenecks and focus operational improvements on specific lanes.

�Notes on methodsRFM uses three dimensions—Recency, Frequency,


Monetary—to classify customer value and target lifecycle actions. �Cohort
retention matrices compare cohorts at equal age since first purchase to
highlight repeat purchasing trends and seasonality effects.

�On-time delivery rate quantifies fulfillment reliability using [ \text{OTD} = \


frac{\text{On-time Deliveries}}{\text{Total Deliveries}} \times 100 ]

and benefits from routine monitoring and segmentation. �

You might also like