Data Analytics project 14
Customer Satisfaction Analysis Project
Build a complete customer satisfaction analysis portfolio project with documented metrics, reproducible Python code, validation checks, and responsible interpretation.
Explore Data Analytics training in VizagView all project ideas
Business question
What are satisfaction and recommendation levels, their uncertainty, and channel differences?
Dataset and grain
One thousand two hundred synthetic survey responses with channel, 1-to-5 CSAT, and 0-to-10 recommendation score.
Requirements
Python 3.10 or later with numpy and pandas installed.
Method and validation checks
Define satisfied as CSAT four or five, promoters as NPS nine or ten, detractors as zero through six, calculate NPS, and form a simple normal-approximation interval for CSAT.
- CSAT and NPS use explicit standard thresholds
- NPS remains between minus 100 and 100
- Confidence bounds include the observed CSAT rate
- Response counts accompany every comparison
Complete Python code
Save the code as da_customer_satisfaction_analysis.py. Review the stated model and field assumptions before using another dataset.
"""Calculate CSAT, NPS, and a confidence interval from synthetic surveys."""
from __future__ import annotations
import numpy as np
import pandas as pd
def make_demo_surveys(n: int = 1_200, seed: int = 42) -> pd.DataFrame:
rng = np.random.default_rng(seed)
channel = rng.choice(["Web", "Store", "Phone", "Email"], n)
csat = np.clip(np.rint(rng.normal(4.0, 0.9, n)), 1, 5).astype(int)
nps = np.clip(np.rint((csat - 1) * 2.15 + rng.normal(1.0, 1.6, n)), 0, 10).astype(int)
return pd.DataFrame({"response_id": range(1, n + 1), "channel": channel, "csat_1_to_5": csat, "nps_0_to_10": nps})
def satisfaction_metrics(data: pd.DataFrame) -> tuple[dict[str, float], pd.DataFrame]:
surveys = data.copy()
surveys["satisfied"] = surveys["csat_1_to_5"] >= 4
surveys["promoter"] = surveys["nps_0_to_10"] >= 9
surveys["detractor"] = surveys["nps_0_to_10"] <= 6
p, n = surveys["satisfied"].mean(), len(surveys)
margin = 1.96 * np.sqrt(p * (1 - p) / n)
kpis = {"responses": float(n), "csat_rate": float(p), "csat_ci_low": float(max(0, p - margin)),
"csat_ci_high": float(min(1, p + margin)), "nps": float((surveys["promoter"].mean() - surveys["detractor"].mean()) * 100)}
by_channel = surveys.groupby("channel", as_index=False).agg(responses=("response_id", "size"), csat_rate=("satisfied", "mean"), promoters=("promoter", "mean"), detractors=("detractor", "mean"))
by_channel["nps"] = (by_channel["promoters"] - by_channel["detractors"]) * 100
return kpis, by_channel
def main() -> None:
kpis, channels = satisfaction_metrics(make_demo_surveys())
print({k: round(v, 3) for k, v in kpis.items()})
print(channels.round(3).to_string(index=False))
print("Survey results can be biased by who receives and answers the survey.")
if __name__ == "__main__":
main()
Build and run the project
Run python da_customer_satisfaction_analysis.py to calculate CSAT, a 95 percent interval, and NPS overall and by channel.
- Confirm the source grain and field definitions.
- Reconcile record counts and additive totals.
- Validate rate denominators and date filters.
- Review outliers and missing values.
- Read the interpretation limits before sharing conclusions.
Expected analytical output
Response count, CSAT rate and interval, NPS, and a channel comparison table.
Interpretation and responsible-use limits
Survey estimates can be biased by sampling, delivery, nonresponse, language, and timing. A narrow statistical interval does not correct selection bias or prove a channel caused satisfaction.
Ways to extend the project
Use weighted sampling, exact intervals for small groups, trend monitoring, comment themes, driver modelling, and a documented follow-up experiment.
Continue learning Data Analytics
Try the next project, return to the Softenant project library, or explore the Data Analytics course in Vizag for guided SQL, Excel, Power BI, Python, dashboard, and portfolio practice.