Mathew K Analytics

Lesson 1 · Market Research Analytics in Python

Introduction to Market Research and Analytics in Python

In this lesson, we will learn how to analyze real market and customer data. We will focus on how to turn customer feedback and survey responses into…

⬇ 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

Introduction to Market Research and Analytics#

  • In this lesson, we will learn how to analyze real market and customer data.
  • We will focus on how to turn customer feedback and survey responses into actionable business insights.
  • You will explore different datasets, understand customer needs, and learn key metrics like satisfaction, segmentation, and trends.
  • By the end, you will be able to identify opportunities and make recommendations based on customer and market analytics.
import pandas as pd
import numpy as np
import openml
import warnings
warnings.filterwarnings('ignore')

What is Market Research? What Data Will We Use?#

  • Market research is the study of customers, competitors, and markets to discover opportunities.
  • Customer analytics uses real data from surveys, transactions, and feedback to uncover trends and drivers.
  • Our data can include:
    • Survey ratings on service satisfaction or NPS (Net Promoter Score).
    • Customer demographics like age, gender, and region.
    • Open-text responses about quality or experiences.
    • Purchase transactions over time.
  • Common beginner mistakes include misreading codes, ignoring missing data, and grouping responses incorrectly.
# Beginner Example 1: 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  
# Beginner Example 2: Summary statistics for survey responses
print(df_cs.describe(include='all'))
       gender  SeniorCitizen Partner Dependents       tenure PhoneService  \
count    7043    7043.000000    7043       7043  7043.000000         7043   
unique      2            NaN       2          2          NaN            2   
top      Male            NaN      No         No          NaN          Yes   
freq     3555            NaN    3641       4933          NaN         6361   
mean      NaN       0.162147     NaN        NaN    32.371149          NaN   
std       NaN       0.368612     NaN        NaN    24.559481          NaN   
min       NaN       0.000000     NaN        NaN     0.000000          NaN   
25%       NaN       0.000000     NaN        NaN     9.000000          NaN   
50%       NaN       0.000000     NaN        NaN    29.000000          NaN   
75%       NaN       0.000000     NaN        NaN    55.000000          NaN   
max       NaN       1.000000     NaN        NaN    72.000000          NaN   

       MultipleLines InternetService OnlineSecurity OnlineBackup  \
count           7043            7043           7043         7043   
unique             3               3              3            3   
top               No     Fiber optic             No           No   
freq            3390            3096           3498         3088   
mean             NaN             NaN            NaN          NaN   
std              NaN             NaN            NaN          NaN   
min              NaN             NaN            NaN          NaN   
25%              NaN             NaN            NaN          NaN   
50%              NaN             NaN            NaN          NaN   
75%              NaN             NaN            NaN          NaN   
max              NaN             NaN            NaN          NaN   

       DeviceProtection TechSupport StreamingTV StreamingMovies  \
count              7043        7043        7043            7043   
unique                3           3           3               3   
top                  No          No          No              No   
freq               3095        3473        2810            2785   
mean                NaN         NaN         NaN             NaN   
std                 NaN         NaN         NaN             NaN   
min                 NaN         NaN         NaN             NaN   
25%                 NaN         NaN         NaN             NaN   
50%                 NaN         NaN         NaN             NaN   
75%                 NaN         NaN         NaN             NaN   
max                 NaN         NaN         NaN             NaN   

              Contract PaperlessBilling     PaymentMethod  MonthlyCharges  \
count             7043             7043              7043     7043.000000   
unique               3                2                 4             NaN   
top     Month-to-month              Yes  Electronic check             NaN   
freq              3875             4171              2365             NaN   
mean               NaN              NaN               NaN       64.761692   
std                NaN              NaN               NaN       30.090047   
min                NaN              NaN               NaN       18.250000   
25%                NaN              NaN               NaN       35.500000   
50%                NaN              NaN               NaN       70.350000   
75%                NaN              NaN               NaN       89.850000   
max                NaN              NaN               NaN      118.750000   

       TotalCharges Churn  
count          7043  7043  
unique         6531     2  
top            20.2    No  
freq             11  5174  
mean            NaN   NaN  
std             NaN   NaN  
min             NaN   NaN  
25%             NaN   NaN  
50%             NaN   NaN  
75%             NaN   NaN  
max             NaN   NaN  
# Beginner Example 3: Checking for missing values
missing = df_cs.isnull().sum()
print(missing[missing > 0])
Series([], dtype: int64)
# Beginner Example 4: Load a marketing campaign dataset
dataset2 = openml.datasets.get_dataset(1461)
df_marketing, _, _, _ = dataset2.get_data(dataset_format='dataframe')
df_marketing.columns = ['age','job','marital','education','default','balance','housing','loan','contact','day','month','duration','campaign','pdays','previous','poutcome','response']
print(df_marketing.shape)
print(df_marketing.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  
# Beginner Example 5: Count successful campaign responses
success_count = (df_marketing['response'] == 'yes').sum()
print('Number of customers who subscribed:', success_count)
Number of customers who subscribed: 0
# Beginner Example 6: Load a Net Promoter Score survey dataset (synthetic)
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
# Intermediate Example 1: Calculate average NPS score by region
avg_nps_by_region = df_nps.groupby('Region')['NPS_Score'].mean()
print(avg_nps_by_region)
Region
East     4.504425
North    5.214765
South    4.719008
West     5.025641
Name: NPS_Score, dtype: float64
# Intermediate Example 2: Identify most common customer job types in the marketing campaign
top_jobs = df_marketing['job'].value_counts().head(5)
print(top_jobs)
job
blue-collar    9732
management     9458
technician     7597
admin.         5171
services       4154
Name: count, dtype: int64
# Intermediate Example 3: Segment satisfaction by customer contract type
segment_satisfaction = df_cs.groupby('Contract')['Churn'].value_counts(normalize=True).unstack().fillna(0)
print(segment_satisfaction)
Churn                 No       Yes
Contract                          
Month-to-month  0.572903  0.427097
One year        0.887305  0.112695
Two year        0.971681  0.028319
# Intermediate Example 4: Open-ended feedback (synthetic small sample)
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.head(3))
   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
# Intermediate Example 5: Quick sentiment keyword search
positive_words = ['great', 'excellent', 'good']
df_feedback['Is_Positive'] = df_feedback['Feedback'].str.lower().apply(lambda x: any(word in x for word in positive_words))
print(df_feedback[['Feedback', 'Is_Positive']])
                                   Feedback  Is_Positive
0          Great service and friendly staff         True
1  Delivery was slow and packaging was poor        False
2         Excellent quality, will buy again         True
3        Customer support needs improvement        False
4                      Good value for money         True
# Intermediate Example 6: Load and preview online retail behavior
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/00502/online_retail_II.xlsx'
df_retail = pd.read_excel(url, sheet_name='Year 2010-2011')
df_retail['InvoiceDate'] = pd.to_datetime(df_retail['InvoiceDate'])
print(df_retail.shape)
print(df_retail.head(3))
(541910, 8)
  Invoice StockCode                         Description  Quantity  \
0  536365    85123A  WHITE HANGING HEART T-LIGHT HOLDER         6   
1  536365     71053                 WHITE METAL LANTERN         6   
2  536365    84406B      CREAM CUPID HEARTS COAT HANGER         8   

          InvoiceDate  Price  Customer ID         Country  
0 2010-12-01 08:26:00   2.55      17850.0  United Kingdom  
1 2010-12-01 08:26:00   3.39      17850.0  United Kingdom  
2 2010-12-01 08:26:00   2.75      17850.0  United Kingdom  
# Advanced Example 1: Calculate monthly sales and detect trends
df_retail['Month'] = df_retail['InvoiceDate'].dt.to_period('M')
monthly_sales = df_retail.groupby('Month')['Price'].sum()
print(monthly_sales.tail(12))
Month
2011-01    172752.800
2011-02    127448.770
2011-03    171486.510
2011-04    129164.961
2011-05    190685.460
2011-06    200717.340
2011-07    171906.791
2011-08    150385.680
2011-09    199235.212
2011-10    263434.090
2011-11    327149.850
2011-12    133933.660
Freq: M, Name: Price, dtype: float64
# Advanced Example 2: Customer segmentation using satisfaction and contract type
df_cs['SeniorCitizen'] = df_cs['SeniorCitizen'].fillna(0)
segments = df_cs.groupby(['SeniorCitizen', 'Contract'])['Churn'].value_counts(normalize=True).unstack().fillna(0)
print(segments)
Churn                               No       Yes
SeniorCitizen Contract                          
0             Month-to-month  0.604302  0.395698
              One year        0.893219  0.106781
              Two year        0.972903  0.027097
1             Month-to-month  0.453532  0.546468
              One year        0.847368  0.152632
              Two year        0.958621  0.041379
# Advanced Example 3: Calculate NPS as a business health indicator
promoters = (df_nps['NPS_Score'] >= 9).sum()
detractors = (df_nps['NPS_Score'] <= 6).sum()
total_responses = len(df_nps)
nps_score = ((promoters - detractors)/total_responses) * 100
print('Overall NPS:', round(nps_score,2))
Overall NPS: -47.6
# Error Handling Example 1: What if survey responses are missing?
df_cs_copy = df_cs.copy()
df_cs_copy.loc[0, 'Churn'] = None
missing_churn = df_cs_copy['Churn'].isnull().sum()
print('Missing Churn responses:', missing_churn)
Missing Churn responses: 1
# Error Handling Example 2: Incorrect grouping (wrong aggregation key)
try:
    mistake = df_marketing.groupby('Month')['balance'].mean()
except Exception as e:
    print('Error:', e)
Error: 'Month'
# Error Handling Example 3: Misinterpreting NPS scales
if df_nps['NPS_Score'].max() > 10 or df_nps['NPS_Score'].min() < 0:
    print('Error: NPS Scores should be between 0 and 10.')
else:
    print('NPS Score range is valid.')
NPS Score range is valid.
# Best Practice 1: Customer segmentation by age and satisfaction
bins = [17, 30, 45, 60, 100]
labels = ['18-30','31-45','46-60','61+']
df_nps['AgeGroup'] = pd.cut(df_nps['Age'], bins=bins, labels=labels)
segment_nps = df_nps.groupby('AgeGroup')['NPS_Score'].mean()
print(segment_nps)
AgeGroup
18-30    4.839286
31-45    4.795918
46-60    4.711409
61+      5.391304
Name: NPS_Score, dtype: float64
# Best Practice 2: Cross-tabulate churn by contract and payment method
churn_crosstab = pd.crosstab(df_cs['Contract'], df_cs['PaymentMethod'], values=df_cs['Churn'] == 'Yes', aggfunc='mean').fillna(0)
print(churn_crosstab)
PaymentMethod   Bank transfer (automatic)  Credit card (automatic)  \
Contract                                                             
Month-to-month                   0.341256                 0.327808   
One year                         0.097187                 0.103015   
Two year                         0.033688                 0.022375   

PaymentMethod   Electronic check  Mailed check  
Contract                                        
Month-to-month          0.537297      0.315789  
One year                0.184438      0.068249  
Two year                0.077381      0.007853  
# Best Practice 3: Constructing an overall satisfaction index
df_cs['satisfaction_index'] = (df_cs['tenure']/df_cs['tenure'].max())*0.5 + (1 - (df_cs['Churn'] == 'Yes').astype(int))*0.5
print(df_cs[['tenure', 'Churn', 'satisfaction_index']].head(5))
   tenure Churn  satisfaction_index
0       1    No            0.506944
1      34    No            0.736111
2       2   Yes            0.013889
3      45    No            0.812500
4       2   Yes            0.013889
# Best Practice 4: Trend analysis in monthly active users (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())
trend = df_cohort.set_index('Signup_Month')['Active_Users'].rolling(window=6).mean()
print('6-month rolling average of active users:')
print(trend.dropna().tail())
   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
6-month rolling average of active users:
Signup_Month
2022-08-31    218.166667
2022-09-30    191.000000
2022-10-31    201.833333
2022-11-30    209.833333
2022-12-31    197.500000
Name: Active_Users, dtype: float64
# End-to-End MARKET RESEARCH mini-project
# 1. Load customer survey data
dataset = openml.datasets.get_dataset(42178)
df, _, _, _ = dataset.get_data(dataset_format='dataframe')
# 2. Calculate churn rate by contract type
churn_rate = df.groupby('Contract')['Churn'].apply(lambda x: (x=='Yes').mean())
print('Churn rate by contract type:')
print(churn_rate)
# 3. Recommend the contract for best retention
lowest_churn = churn_rate.idxmin()
print('\nBest contract for customer retention is:', lowest_churn)
Churn rate by contract type:
Contract
Month-to-month    0.427097
One year          0.112695
Two year          0.028319
Name: Churn, dtype: float64

Best contract for customer retention is: Two year
 

Found this useful?

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