Mathew K Analytics

Lesson 6 · Market Research Analytics in Python

Python Basics for Market Research Analysis: Essential Training

This lesson helps you analyze real customer survey and behavior datasets using Python. Market research insights guide product, campaign, and service…

⬇ 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

Python Basics for Market Research Analysis#

  • This lesson helps you analyze real customer survey and behavior datasets using Python.
  • Market research insights guide product, campaign, and service decisions.
  • You will practice summarizing survey answers, segmenting customers, and finding data-driven insights.
  • We focus on business value, not just code.
  • By the end, you will extract actionable findings from real market data.
import pandas as pd
import numpy as np
import openml
import warnings
warnings.filterwarnings('ignore')

Core concepts in market research data#

  • Surveys capture customer opinions, experiences, or intent.
  • Each row usually represents one response or customer.
  • Columns can be answers, demographic info, or purchase behavior.
  • Ratings (e.g. 1-10 scale), choices, and open-ended text are common.
  • Beginners often confuse scales (e.g., interpreting high NPS as bad) or mix up who answered questions.
  • Market analysis looks for patterns that drive business action.
# Load customer satisfaction dataset from OpenML
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  
# Summarize gender distribution
print(df_cs['gender'].value_counts())
gender
Male      3555
Female    3488
Name: count, dtype: int64
# Calculate churn rate (percentage who left)
churn_pct = (df_cs['Churn'] == 'Yes').mean() * 100
print('Customer churn rate: {:.2f}%'.format(churn_pct))
Customer churn rate: 26.54%
# Load marketing campaign dataset from OpenML
dataset = openml.datasets.get_dataset(1461)
df_mkt, _, _, _ = dataset.get_data(dataset_format='dataframe')
df_mkt.columns = ['age','job','marital','education','default','balance','housing','loan','contact','day','month','duration','campaign','pdays','previous','poutcome','response']
print(df_mkt.shape)
print(df_mkt.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  
# Calculate basic response rate
resp_rate = (df_mkt['response'] == 'yes').mean() * 100
print('Overall campaign response rate: {:.2f}%'.format(resp_rate))
Overall campaign response rate: 0.00%
# Create a synthetic Net Promoter Score (NPS) survey data
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.head(3))
   CustomerID  Age Region  NPS_Score
0           1   56   West          2
1           2   69  North          0
2           3   46   East          4
# Average NPS by region
avg_nps_region = df_nps.groupby('Region')['NPS_Score'].mean()
print('Average NPS by region:')
print(avg_nps_region)
Average NPS by region:
Region
East     4.504425
North    5.214765
South    4.719008
West     5.025641
Name: NPS_Score, dtype: float64
# Classify NPS responders
df_nps['NPS_Type'] = pd.cut(df_nps['NPS_Score'], bins=[-1,6,8,10], labels=['Detractor','Passive','Promoter'])
print(df_nps[['CustomerID','Region','NPS_Score','NPS_Type']].head(5))
   CustomerID Region  NPS_Score   NPS_Type
0           1   West          2  Detractor
1           2  North          0  Detractor
2           3   East          4  Detractor
3           4   West          3  Detractor
4           5   East          9   Promoter
import matplotlib.pyplot as plt
# Churn rate by contract type
churn_by_contract = df_cs.groupby('Contract')['Churn'].apply(lambda x: (x=='Yes').mean()*100)
churn_by_contract.plot(kind='bar', color='skyblue', figsize=(6,4))
plt.ylabel('Churn Rate (%)')
plt.title('Churn Rate by Contract Type')
plt.xticks(rotation=15)
plt.tight_layout()
plt.show()
No description has been provided for this image
# Find top 5 most common customer jobs
print(df_mkt['job'].value_counts().head(5))
job
blue-collar    9732
management     9458
technician     7597
admin.         5171
services       4154
Name: count, dtype: int64
# Cross-tabulate response by housing loan
resp_xtab = pd.crosstab(df_mkt['housing'], df_mkt['response'], margins=True)
print(resp_xtab)
response      1     2    All
housing                     
no        16727  3354  20081
yes       23195  1935  25130
All       39922  5289  45211
# Simulate missing values in Churn field
df_cs_missing = df_cs.copy()
df_cs_missing.loc[0:9, 'Churn'] = np.nan
print(df_cs_missing['Churn'].isnull().sum())
10
# Calculate churn rate, skipping missing
valid_churn = df_cs_missing['Churn'].dropna()
true_churn_pct = (valid_churn == 'Yes').mean() * 100
print('Churn rate (excluding missing): {:.2f}%'.format(true_churn_pct))
Churn rate (excluding missing): 26.52%
# Calculate NPS index as (promoters - detractors)/total * 100
n_nps = len(df_nps)
n_promoters = (df_nps['NPS_Type'] == 'Promoter').sum()
n_detractors = (df_nps['NPS_Type'] == 'Detractor').sum()
nps_index = (n_promoters - n_detractors) / n_nps * 100
print('NPS Index: {:.2f}'.format(nps_index))
if nps_index < 0: print('Recommendation: Immediate action needed to address negative customer sentiment.')
elif nps_index < 50: print('Recommendation: Focus on converting passives to promoters.')
else: print('Recommendation: Customer advocacy is strong, maintain current strategy.')
NPS Index: -47.60
Recommendation: Immediate action needed to address negative customer sentiment.
# Bin age for segment analysis
df_mkt['age_group'] = pd.cut(df_mkt['age'], bins=[15,25,35,50,80], labels=['16-25','26-35','36-50','51+'])
resp_by_age = df_mkt.groupby('age_group')['response'].value_counts(normalize=True).unstack().fillna(0)*100
print(resp_by_age)
response           1          2
age_group                      
16-25      76.047904  23.952096
26-35      87.996917  12.003083
36-50      90.618930   9.381070
51+        86.129314  13.870686
# Text feedback: simulate a blank entry
df_fb = pd.DataFrame({'CustomerID':[1,2,3,4,5], 'Feedback':['Great service',' ','Excellent quality','Support needs improvement','Good value']})
missing_text = df_fb['Feedback'].str.strip() == ''
print('Missing text feedback:', missing_text.sum())
Missing text feedback: 1
# Example: wrong NPS logic
wrong_promoters = (df_nps['NPS_Score'] <= 6).sum()
print('Wrongly counted promoters:', wrong_promoters)
Wrongly counted promoters: 323
# Group by typo - should cause error
try:
    resp_wrong = df_mkt.groupby('agge')['response'].mean()
except KeyError as e:
    print('Grouping error:', e)
Grouping error: 'agge'
# Segmentation: Contract type and churn
seg = df_cs.groupby(['Contract','Churn']).size().unstack(fill_value=0)
print(seg)
Churn             No   Yes
Contract                  
Month-to-month  2220  1655
One year        1307   166
Two year        1647    48
# Cross-tab: payment method and churn
pay_xtab = pd.crosstab(df_cs['PaymentMethod'], df_cs['Churn'], normalize='index').round(2)
print(pay_xtab)
Churn                        No   Yes
PaymentMethod                        
Bank transfer (automatic)  0.83  0.17
Credit card (automatic)    0.85  0.15
Electronic check           0.55  0.45
Mailed check               0.81  0.19
# Create synthetic customer 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))})
# Trend over time
import matplotlib.pyplot as plt
plt.plot(df_cohort['Signup_Month'], df_cohort['Active_Users'], marker='o')
plt.title('Active Users per Cohort Month')
plt.xlabel('Signup Month')
plt.ylabel('Active Users')
plt.grid(True)
plt.tight_layout()
plt.show()
No description has been provided for this image
# End-to-end: Find top drivers of churn among senior citizens
senior = df_cs[df_cs['SeniorCitizen']==1]
senior['MonthlyChargesGroup'] = pd.cut(senior['MonthlyCharges'], bins=[0,50,100,150], labels=['Low','Medium','High'])
by_charge = senior.groupby(['MonthlyChargesGroup'])['Churn'].value_counts(normalize=True).unstack().fillna(0)*100
print(by_charge)
Churn                       No        Yes
MonthlyChargesGroup                      
Low                  64.150943  35.849057
Medium               54.278075  45.721925
High                 67.234043  32.765957
 

Found this useful?

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