Mathew K Analytics

Lesson 21 · Market Research Analytics in Python

Descriptive Statistics for Market Research Using Python | Comprehensive Analysis Guide

In this lesson, we will learn how to use descriptive statistics to understand customer and market research data. Descriptive statistics help 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

Descriptive Statistics for Market Research#

  • In this lesson, we will learn how to use descriptive statistics to understand customer and market research data.
  • Descriptive statistics help businesses summarize, visualize, and interpret key patterns in survey responses and customer behavior.
  • These insights help businesses make informed decisions about products, services, and marketing campaigns.
  • By the end, you will be able to describe customer ratings, NPS, and behaviors using Python.
import pandas as pd
import numpy as np
import openml
import warnings
warnings.filterwarnings('ignore')

Understanding Market Research Data#

  • Market research data may come from customer satisfaction surveys, NPS surveys, or tracking online retail behavior.
  • Data is often structured as rows of responses, with columns for demographics, ratings, and text feedback.
  • Each row usually represents one customer or one transaction.
  • Beginners often forget to check for missing values, misunderstand categorical variables, or incorrectly interpret survey scales like NPS.
  • Always review your data types and check for any unusual or unexpected values before analysis.
# Beginner Example 1: Load an NPS survey dataset (synthetic, reproducible)
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
# Beginner Example 2: Calculate the mean NPS score
mean_nps = df_nps['NPS_Score'].mean()
print(f'Mean NPS score: {mean_nps:.2f}')
Mean NPS score: 4.89
# Beginner Example 3: Find the minimum and maximum NPS scores
min_nps = df_nps['NPS_Score'].min()
max_nps = df_nps['NPS_Score'].max()
print(f'Minimum NPS score: {min_nps}')
print(f'Maximum NPS score: {max_nps}')
Minimum NPS score: 0
Maximum NPS score: 10
# Beginner Example 4: Count NPS promoters, passives, detractors
nps_bins = pd.cut(df_nps['NPS_Score'], bins=[-0.1,6,8,10], labels=['Detractor','Passive','Promoter'])
counts = nps_bins.value_counts()
print(counts)
NPS_Score
Detractor    323
Passive       92
Promoter      85
Name: count, dtype: int64
# Beginner Example 5: Calculate region-wise average NPS
region_avg = df_nps.groupby('Region')['NPS_Score'].mean()
print(region_avg)
Region
East     4.504425
North    5.214765
South    4.719008
West     5.025641
Name: NPS_Score, dtype: float64
# Intermediate Example 1: Load a customer satisfaction dataset from OpenML
dataset = openml.datasets.get_dataset(42178)
df_csat, _, _, _ = dataset.get_data(dataset_format='dataframe')
print(df_csat.shape)
print(df_csat.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  
# Intermediate Example 2: Calculate overall monthly charges statistics
monthly_desc = df_csat['MonthlyCharges'].describe()
print(monthly_desc)
count    7043.000000
mean       64.761692
std        30.090047
min        18.250000
25%        35.500000
50%        70.350000
75%        89.850000
max       118.750000
Name: MonthlyCharges, dtype: float64
# Intermediate Example 3: Gender segmentation of monthly charges
gender_means = df_csat.groupby('gender')['MonthlyCharges'].mean()
print(gender_means)
gender
Female    65.204243
Male      64.327482
Name: MonthlyCharges, dtype: float64
# Intermediate Example 4: Churn rate calculation
churn_counts = df_csat['Churn'].value_counts(normalize=True)
print(f'Churn rate:\n{churn_counts}')
Churn rate:
Churn
No     0.73463
Yes    0.26537
Name: proportion, dtype: float64
# Intermediate Example 5: Cross-tabulation - Payment Method vs Churn
payment_churn_crosstab = pd.crosstab(df_csat['PaymentMethod'], df_csat['Churn'])
print(payment_churn_crosstab)
Churn                        No   Yes
PaymentMethod                        
Bank transfer (automatic)  1286   258
Credit card (automatic)    1290   232
Electronic check           1294  1071
Mailed check               1304   308
# Advanced Example 1: Load the Online Retail Behavior dataset from UCI
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 2: Calculate average order value (AOV) by country
df_retail['OrderValue'] = df_retail['Quantity'] * df_retail['Price']
aov_country = df_retail.groupby('Country')['OrderValue'].mean().sort_values(ascending=False)
print(aov_country.head(5))
Country
Netherlands    120.059696
Australia      108.877895
Japan           98.716816
Sweden          79.211926
Denmark         48.247147
Name: OrderValue, dtype: float64
# Advanced Example 3: Identify top products by revenue
product_revenue = df_retail.groupby('Description')['OrderValue'].sum().sort_values(ascending=False)
print(product_revenue.head(5))
Description
DOTCOM POSTAGE                        206245.48
REGENCY CAKESTAND 3 TIER              164762.19
WHITE HANGING HEART T-LIGHT HOLDER     99668.47
PARTY BUNTING                          98302.98
JUMBO BAG RED RETROSPOT                92356.03
Name: OrderValue, dtype: float64
# Advanced Example 4: Trend analysis of monthly sales
df_retail['Month'] = df_retail['InvoiceDate'].dt.to_period('M')
monthly_sales = df_retail.groupby('Month')['OrderValue'].sum()
print(monthly_sales.tail(6))
Month
2011-07     681300.111
2011-08     682680.510
2011-09    1019687.622
2011-10    1070704.670
2011-11    1461756.250
2011-12     433686.010
Freq: M, Name: OrderValue, dtype: float64
# Error Handling Example 1: Detect missing survey responses
missing_nps = df_nps.isnull().sum()
print(f'Missing values in NPS dataset:\n{missing_nps}')
Missing values in NPS dataset:
CustomerID    0
Age           0
Region        0
NPS_Score     0
dtype: int64
# Error Handling Example 2: Guard against incorrect groupings (e.g., misspelled region names)
df_nps['Region'] = df_nps['Region'].str.title()
unique_regions = df_nps['Region'].unique()
print(f'Unique region names: {unique_regions}')
Unique region names: ['West' 'North' 'East' 'South']
# Error Handling Example 3: Avoid misinterpreting the NPS scale
if df_nps['NPS_Score'].max() > 10 or df_nps['NPS_Score'].min() < 0:
    print('Warning: NPS scores should be between 0 and 10! Check your data.')
else:
    print('All NPS scores are within the correct range.')
All NPS scores are within the correct range.

Best Practices and Market Research Patterns#

  • Always segment customers by demographics, behavior, or engagement for clearer insights.
  • Use cross-tabulation to find relationships and dependencies between survey questions or attributes.
  • Build customer indices or scores by combining multiple survey questions, such as satisfaction and loyalty.
  • Analyze trends to capture seasonality, campaign effects, or signals of decline and growth.
  • Document every transformation or calculation so that insights can be reproduced easily.
# Pattern Example: Create a composite customer satisfaction score
df_csat['SatisfactionIndex'] = (df_csat['MonthlyCharges'].rank(pct=True) +
                                 (1 - df_csat['tenure'].rank(pct=True))) / 2
print(df_csat[['MonthlyCharges','tenure','SatisfactionIndex']].head(3))
   MonthlyCharges  tenure  SatisfactionIndex
0           29.85       1           0.594314
1           56.95      34           0.422192
2           53.85       2           0.624805
# Pattern Example: Cross-tabulate NPS segment by age group
age_bins = pd.cut(df_nps['Age'], bins=[17,25,35,50,70], labels=['18-25','26-35','36-50','51-70'])
nps_age_crosstab = pd.crosstab(age_bins, nps_bins)
print(nps_age_crosstab)
NPS_Score  Detractor  Passive  Promoter
Age                                    
18-25             55       10        16
26-35             48       15        10
36-50            109       29        22
51-70            111       38        37
# Pattern Example: Trend in average NPS score by region
df_nps['ResponseMonth'] = np.random.choice(pd.date_range('2023-01-01', periods=12, freq='MS'), df_nps.shape[0])
monthly_nps_region = df_nps.groupby([df_nps['ResponseMonth'].dt.to_period('M'),'Region'])['NPS_Score'].mean().unstack()
print(monthly_nps_region.tail(6))
Region             East     North     South      West
ResponseMonth                                        
2023-07        5.333333  5.909091  4.666667  7.250000
2023-08        3.166667  4.555556  4.615385  4.500000
2023-09        3.272727  5.266667  5.090909  5.600000
2023-10        3.266667  4.500000  3.444444  5.181818
2023-11        4.555556  6.052632  4.333333  4.357143
2023-12        4.714286  4.300000  5.100000  4.750000
# Tiny End-to-End Example: From NPS survey to recommendation
overall_nps = (counts['Promoter'] - counts['Detractor']) / len(df_nps) * 100
print(f'Your Net Promoter Score is: {overall_nps:.1f}')
if overall_nps < 0:
    print('Your NPS is negative. Business should urgently investigate negative customer experiences.')
elif overall_nps < 30:
    print('NPS is low. Consider targeting detractors with win-back offers and collecting further feedback.')
elif overall_nps < 70:
    print('NPS is moderate. Maintain current strengths but look for ways to create more promoters.')
else:
    print('NPS is excellent. Promote your high Net Promoter Score in marketing!')
Your Net Promoter Score is: -47.6
Your NPS is negative. Business should urgently investigate negative customer experiences.
 

Found this useful?

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