Mathew K Analytics

Lesson 58 · Market Research Analytics in Python

Market Insights and Recommendations Case Study with Python Analytics

In this lesson, we will analyze real-world customer and market research data to uncover actionable insights. Understanding market research helps businesses…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb

Market Insights and Recommendations Case Study#

  • In this lesson, we will analyze real-world customer and market research data to uncover actionable insights.
  • Understanding market research helps businesses make informed decisions that can improve customer satisfaction, retention, and revenue.
  • We will practice transforming survey and feedback data into recommendations that support your organization.
import pandas as pd
import numpy as np
import openml
import warnings
warnings.filterwarnings('ignore')

Core Market Research Concepts for This Case Study#

  • Market research data often comes from surveys, transactional records, and collected customer feedback.
  • Survey data includes demographics, satisfaction ratings, and behavioral indicators.
  • Free-text responses provide qualitative insight that numbers alone may not reveal.
  • Errors sometimes occur if responses are regrouped wrongly, or if scales like NPS are misinterpreted.
  • Understanding what your data represents is essential before giving recommendations.
# Load a customer satisfaction survey dataset
dataset = openml.datasets.get_dataset(42178)
df_cs, _, _, _ = dataset.get_data(dataset_format='dataframe')
print(df_cs.shape)
print(df_cs.head(3))
(7043, 20)
   gender  SeniorCitizen Partner Dependents  tenure PhoneService  \
0  Female              0     Yes         No       1           No   
1    Male              0      No         No      34          Yes   
2    Male              0      No         No       2          Yes   

      MultipleLines InternetService OnlineSecurity OnlineBackup  \
0  No phone service             DSL             No          Yes   
1                No             DSL            Yes           No   
2                No             DSL            Yes          Yes   

  DeviceProtection TechSupport StreamingTV StreamingMovies        Contract  \
0               No          No          No              No  Month-to-month   
1              Yes          No          No              No        One year   
2               No          No          No              No  Month-to-month   

  PaperlessBilling     PaymentMethod  MonthlyCharges TotalCharges Churn  
0              Yes  Electronic check           29.85        29.85    No  
1               No      Mailed check           56.95       1889.5    No  
2              Yes      Mailed check           53.85       108.15   Yes  
# Load a marketing campaign performance dataset
dataset = openml.datasets.get_dataset(1461)
df_mc, _, _, _ = dataset.get_data(dataset_format='dataframe')
df_mc.columns = ['age','job','marital','education','default','balance','housing','loan','contact','day','month','duration','campaign','pdays','previous','poutcome','response']
print(df_mc.shape)
print(df_mc.head(3))
(45211, 17)
   age           job  marital  education default  balance housing loan  \
0   58    management  married   tertiary      no   2143.0     yes   no   
1   44    technician   single  secondary      no     29.0     yes   no   
2   33  entrepreneur  married  secondary      no      2.0     yes  yes   

   contact  day month  duration  campaign  pdays  previous poutcome response  
0  unknown    5   may     261.0         1   -1.0       0.0  unknown        1  
1  unknown    5   may     151.0         1   -1.0       0.0  unknown        1  
2  unknown    5   may      76.0         1   -1.0       0.0  unknown        1  
# Load a synthetic Net Promoter Score (NPS) survey dataset
np.random.seed(42)
df_nps = pd.DataFrame({
    'CustomerID': range(1,501),
    'Age': np.random.randint(18,70,500),
    'Region': np.random.choice(['North','South','East','West'],500),
    'NPS_Score': np.random.randint(0,11,500)
})
print(df_nps.shape)
print(df_nps.head(3))
(500, 4)
   CustomerID  Age Region  NPS_Score
0           1   56   West          2
1           2   69  North          0
2           3   46   East          4
# Basic aggregation: Average satisfaction by gender
avg_satisfaction = df_cs.groupby('gender')['MonthlyCharges'].mean()
print(avg_satisfaction)
gender
Female    65.204243
Male      64.327482
Name: MonthlyCharges, dtype: float64
# Cross-tabulation: Churn rate by contract type
ct = pd.crosstab(df_cs['Contract'], df_cs['Churn'], normalize='index')
print(ct)
Churn                 No       Yes
Contract                          
Month-to-month  0.572903  0.427097
One year        0.887305  0.112695
Two year        0.971681  0.028319
# Simple NPS calculation: Promoters, Passives, Detractors
promoters = (df_nps['NPS_Score'] >= 9).sum()
detractors = (df_nps['NPS_Score'] <= 6).sum()
passives = ((df_nps['NPS_Score'] > 6) & (df_nps['NPS_Score'] < 9)).sum()
total = len(df_nps)
nps = ((promoters - detractors) / total) * 100
print(f'Net Promoter Score: {nps:.1f}')
Net Promoter Score: -47.6
# Visualize customer churn by age group
age_bins = [18, 30, 40, 50, 60, 80]
df_cs['age_group'] = pd.cut(df_cs['SeniorCitizen']*30 + 25, bins=age_bins, right=False)
churn_by_age = pd.crosstab(df_cs['age_group'], df_cs['Churn'], normalize='index')
print(churn_by_age)
Churn            No       Yes
age_group                    
[18, 30)   0.763938  0.236062
[50, 60)   0.583187  0.416813
# Load open-ended customer feedback dataset
df_feedback = pd.DataFrame({
    'CustomerID':[1,2,3,4,5],
    'Feedback':['Great service and friendly staff','Delivery was slow and packaging was poor','Excellent quality, will buy again','Customer support needs improvement','Good value for money']
})
print(df_feedback)
   CustomerID                                  Feedback
0           1          Great service and friendly staff
1           2  Delivery was slow and packaging was poor
2           3         Excellent quality, will buy again
3           4        Customer support needs improvement
4           5                      Good value for money
# Simple sentiment analysis using keyword matching
def simple_sentiment(comment):
    positive = ['great', 'excellent', 'good', 'friendly']
    negative = ['poor', 'slow', 'needs improvement']
    text = comment.lower()
    if any(word in text for word in positive):
        return 'Positive'
    elif any(word in text for word in negative):
        return 'Negative'
    else:
        return 'Neutral'
df_feedback['Sentiment'] = df_feedback['Feedback'].apply(simple_sentiment)
print(df_feedback[['Feedback', 'Sentiment']])
                                   Feedback Sentiment
0          Great service and friendly staff  Positive
1  Delivery was slow and packaging was poor  Negative
2         Excellent quality, will buy again  Positive
3        Customer support needs improvement  Negative
4                      Good value for money  Positive
# Find most common NPS score by region
most_common_nps = df_nps.groupby('Region')['NPS_Score'].agg(lambda x: x.value_counts().idxmax())
print(most_common_nps)
Region
East     0
North    7
South    4
West     3
Name: NPS_Score, dtype: int32
# Correlation between contract type and monthly charges
avg_charges_by_contract = df_cs.groupby('Contract')['MonthlyCharges'].mean().sort_values()
print(avg_charges_by_contract)
# Tip: High monthly charges may reveal your best upsell opportunities.
Contract
Two year          60.770413
One year          65.048608
Month-to-month    66.398490
Name: MonthlyCharges, dtype: float64
# Handle missing survey responses
missing = df_cs.isnull().sum()
print('Missing values per column:')
print(missing)
Missing values per column:
gender              0
SeniorCitizen       0
Partner             0
Dependents          0
tenure              0
PhoneService        0
MultipleLines       0
InternetService     0
OnlineSecurity      0
OnlineBackup        0
DeviceProtection    0
TechSupport         0
StreamingTV         0
StreamingMovies     0
Contract            0
PaperlessBilling    0
PaymentMethod       0
MonthlyCharges      0
TotalCharges        0
Churn               0
age_group           0
dtype: int64
# Example of misinterpreting NPS scale
wrong_nps_mean = df_nps['NPS_Score'].mean()
print(f'Incorrect NPS if using mean: {wrong_nps_mean:.2f}')
Incorrect NPS if using mean: 4.89
# Aggregation error: double-counting in groupby
try:
    double_count = df_nps.groupby(['Region'])['NPS_Score'].sum().sum()
    print('Total NPS scores (possible double count):', double_count)
except Exception as e:
    print(e)
Total NPS scores (possible double count): 2445
# Customer segmentation by contract and churn
segment = df_cs.groupby(['Contract', 'Churn']).size().unstack().fillna(0)
print(segment)
Churn             No   Yes
Contract                  
Month-to-month  2220  1655
One year        1307   166
Two year        1647    48
# Trend analysis: Monthly new signups (synthetic cohort data)
np.random.seed(0)
dates = pd.date_range('2021-01-01', periods=24, freq='ME')
df_cohort = pd.DataFrame({
    'CustomerID': np.random.randint(1000,2000,len(dates)),
    'Signup_Month': dates,
    'Active_Users': np.random.randint(50,300,len(dates))
})
print(df_cohort.head(5))
signup_trend = df_cohort.groupby(df_cohort['Signup_Month'].dt.to_period('M'))['Active_Users'].sum()
print(signup_trend)
   CustomerID Signup_Month  Active_Users
0        1684   2021-01-31           138
1        1559   2021-02-28           131
2        1629   2021-03-31           215
3        1192   2021-04-30            75
4        1835   2021-05-31           127
Signup_Month
2021-01    138
2021-02    131
2021-03    215
2021-04     75
2021-05    127
2021-06    122
2021-07     59
2021-08    198
2021-09    165
2021-10    258
2021-11    293
2021-12    247
2022-01    129
2022-02    225
2022-03    242
2022-04    132
2022-05    149
2022-06    266
2022-07    227
2022-08    293
2022-09     79
2022-10    197
2022-11    197
2022-12    192
Freq: M, Name: Active_Users, dtype: int32
# Construct a Customer Satisfaction Index
df_cs['MonthlyCharges'] = pd.to_numeric(df_cs['MonthlyCharges'], errors='coerce')
df_cs['TotalCharges'] = pd.to_numeric(df_cs['TotalCharges'], errors='coerce')
df_cs['Satisfaction_Index'] = (
    df_cs['MonthlyCharges'] /
    df_cs['TotalCharges'].replace(0, np.nan)
) * 100
index_mean = df_cs['Satisfaction_Index'].mean(skipna=True)
print(f"Mean Satisfaction Index: {index_mean:.2f}")
Mean Satisfaction Index: 15.76
# 1. Load market research data: marketing campaign performance
dataset = openml.datasets.get_dataset(1461)
df_mc, _, _, _ = dataset.get_data(dataset_format='dataframe')
df_mc.columns = ['age','job','marital','education','default','balance','housing','loan','contact','day','month','duration','campaign','pdays','previous','poutcome','response']
# 2. Find customer group most likely to respond positively
response_rate = df_mc.groupby('job')['response'].apply(lambda x: (x == 'yes').mean()).sort_values(ascending=False)
print('Response rate by job:')
print(response_rate)
Response rate by job:
job
admin.           0.0
blue-collar      0.0
entrepreneur     0.0
housemaid        0.0
management       0.0
retired          0.0
self-employed    0.0
services         0.0
student          0.0
technician       0.0
unemployed       0.0
unknown          0.0
Name: response, dtype: float64
# 3. Write recommendation to file
with open('campaign_recommendation.txt', 'w') as f:
    top_job = response_rate.idxmax()
    top_rate = response_rate.max()*100
    f.write(f'Target job group: {top_job}\nResponse rate: {top_rate:.2f}%\nRecommendation: Focus next campaign on {top_job}s.')
 

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.