Mathew K Analytics

Lesson 19 · Python for Retail E-commerce Analytics

Encoding Categorical Retail Variables for Machine Learning in Python

In this lesson, we will learn to solve a common retail analytics problem: how to encode and work with categorical variables in retail and e-commerce…

⬇ 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

Encoding Categorical Retail Variables#

  • In this lesson, we will learn to solve a common retail analytics problem: how to encode and work with categorical variables in retail and e-commerce datasets.
  • Categorical variables like product category, country, or payment method are not directly usable in most analytics or machine learning models.
  • Transforming these variables allows analysts and data scientists to extract valuable business insights and enable advanced algorithms.
  • You will practice encoding categories in real retail data and interpreting the impact on key business metrics.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

Key Concepts in Retail Categorical Variable Analytics#

  • Retail datasets include information such as transactions, customer orders, product catalogs, and more.
  • Typical business metrics include revenue, sales quantity, product prices, and category or country breakdowns.
  • A common mistake is to treat categorical variables as numerical or forget to encode them for analysis.
  • Correct handling of categorical variables leads to better grouping, segmentation, and actionable insights.
# Example 1: Load real retail transactions with categorical columns
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/00502/online_retail_II.xlsx'
df = pd.read_excel(url, sheet_name='Year 2010-2011')
df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate'])
print(df.shape)
print(df[['Invoice', 'Country', 'Description']].head(3))
(541910, 8)
  Invoice         Country                         Description
0  536365  United Kingdom  WHITE HANGING HEART T-LIGHT HOLDER
1  536365  United Kingdom                 WHITE METAL LANTERN
2  536365  United Kingdom      CREAM CUPID HEARTS COAT HANGER
# Example 2: Count unique categories in 'Country' and 'Description'
num_countries = df['Country'].nunique()
num_products = df['Description'].nunique()
print('Unique countries:', num_countries)
print('Unique product descriptions:', num_products)
Unique countries: 38
Unique product descriptions: 4223
# Example 3: Show most common countries in transactions
print(df['Country'].value_counts().head())
Country
United Kingdom    495478
Germany             9495
France              8558
EIRE                8196
Spain               2533
Name: count, dtype: int64
# Example 4: Simple label encoding of country column
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
df['Country_code'] = le.fit_transform(df['Country'])
print(df[['Country', 'Country_code']].head(7))
          Country  Country_code
0  United Kingdom            36
1  United Kingdom            36
2  United Kingdom            36
3  United Kingdom            36
4  United Kingdom            36
5  United Kingdom            36
6  United Kingdom            36
# Example 5: One-hot encoding for countries
df_onehot = pd.get_dummies(df, columns=['Country'], prefix='cty')
print(df_onehot.filter(like='cty_').head(3))
   cty_Australia  cty_Austria  cty_Bahrain  cty_Belgium  cty_Brazil  \
0          False        False        False        False       False   
1          False        False        False        False       False   
2          False        False        False        False       False   

   cty_Canada  cty_Channel Islands  cty_Cyprus  cty_Czech Republic  \
0       False                False       False               False   
1       False                False       False               False   
2       False                False       False               False   

   cty_Denmark  ...  cty_RSA  cty_Saudi Arabia  cty_Singapore  cty_Spain  \
0        False  ...    False             False          False      False   
1        False  ...    False             False          False      False   
2        False  ...    False             False          False      False   

   cty_Sweden  cty_Switzerland  cty_USA  cty_United Arab Emirates  \
0       False            False    False                     False   
1       False            False    False                     False   
2       False            False    False                     False   

   cty_United Kingdom  cty_Unspecified  
0                True            False  
1                True            False  
2                True            False  

[3 rows x 38 columns]
# Example 6: Mean-encoding country by average unit price
country_mean_price = df.groupby('Country')['Price'].mean()
df['Country_Price_Mean'] = df['Country'].map(country_mean_price)
print(df[['Country', 'Country_Price_Mean']].head(5))
          Country  Country_Price_Mean
0  United Kingdom            4.532422
1  United Kingdom            4.532422
2  United Kingdom            4.532422
3  United Kingdom            4.532422
4  United Kingdom            4.532422
# Example 7: Frequency encoding for product Description
description_freq = df['Description'].value_counts(normalize=True)
df['Description_freq'] = df['Description'].map(description_freq)
print(df[['Description', 'Description_freq']].head(5))
                           Description  Description_freq
0   WHITE HANGING HEART T-LIGHT HOLDER          0.004383
1                  WHITE METAL LANTERN          0.000607
2       CREAM CUPID HEARTS COAT HANGER          0.000542
3  KNITTED UNION FLAG HOT WATER BOTTLE          0.000875
4       RED WOOLLY HOTTIE WHITE HEART.          0.000831
# Example 8: Prepare a product catalog with categories
np.random.seed(42)
categories = ['Electronics','Clothing','Home','Sports','Beauty']
product_ids = list(range(1001,1101))
product_categories = np.random.choice(categories,100)
product_prices = np.round(np.random.uniform(5,500,100),2)
catalog = pd.DataFrame({'ProductID':product_ids,'Category':product_categories,'Price':product_prices})
print(catalog.head(3))
   ProductID Category   Price
0       1001   Sports  457.91
1       1002   Beauty  425.77
2       1003     Home  227.48
# Example 9: One-hot encode product category in the catalog
catalog_ohe = pd.get_dummies(catalog, columns=['Category'])
print(catalog_ohe.head())
   ProductID   Price  Category_Beauty  Category_Clothing  \
0       1001  457.91            False              False   
1       1002  425.77             True              False   
2       1003  227.48            False              False   
3       1004   52.23             True              False   
4       1005  188.56             True              False   

   Category_Electronics  Category_Home  Category_Sports  
0                 False          False             True  
1                 False          False            False  
2                 False           True            False  
3                 False          False            False  
4                 False          False            False  
# Example 10: Label encoding category for tree-based models
from sklearn.preprocessing import OrdinalEncoder
oe = OrdinalEncoder()
catalog['Category_Ordinal'] = oe.fit_transform(catalog[['Category']])
print(catalog[['Category', 'Category_Ordinal']].head(10))
   Category  Category_Ordinal
0    Sports               4.0
1    Beauty               0.0
2      Home               3.0
3    Beauty               0.0
4    Beauty               0.0
5  Clothing               1.0
6      Home               3.0
7      Home               3.0
8      Home               3.0
9    Beauty               0.0
# Example 11: Create a simulated retail order dataset
np.random.seed(42)
n_orders = 1000
order_ids = list(range(1, n_orders + 1))
customer_ids = np.random.randint(1000, 1500, n_orders)
product_ids = np.random.randint(1001, 1100, n_orders)
quantities = np.random.randint(1, 5, n_orders)
order_dates = pd.date_range('2023-01-01', periods=n_orders, freq='h')
orders = pd.DataFrame({'OrderID':order_ids, 'CustomerID':customer_ids, 'ProductID':product_ids, 'Quantity':quantities, 'OrderDate':order_dates})
print(orders.head(3))
   OrderID  CustomerID  ProductID  Quantity           OrderDate
0        1        1102       1049         3 2023-01-01 00:00:00
1        2        1435       1011         1 2023-01-01 01:00:00
2        3        1348       1085         3 2023-01-01 02:00:00
# Example 12: Merge product categories into orders
orders = pd.merge(orders, catalog[['ProductID', 'Category']], on='ProductID', how='left')
print(orders[['OrderID', 'ProductID', 'Category']].head(5))
   OrderID  ProductID     Category
0        1       1049     Clothing
1        2       1011       Sports
2        3       1085  Electronics
3        4       1026         Home
4        5       1063       Beauty
# Example 13: Label encode Category in merged orders table
orders['Category_code'] = le.fit_transform(orders['Category'])
print(orders[['Category', 'Category_code']].head(10))
      Category  Category_code
0     Clothing              1
1       Sports              4
2  Electronics              2
3         Home              3
4       Beauty              0
5       Sports              4
6  Electronics              2
7       Sports              4
8         Home              3
9     Clothing              1
# Example 14: Group orders by encoded category and count
cat_groups = orders.groupby('Category_code').size()
print(cat_groups)
Category_code
0    193
1    201
2    176
3    159
4    271
dtype: int64
# Example 15: Advanced - Target encode product categories with quantity mean
cat_quant_mean = orders.groupby('Category')['Quantity'].mean()
orders['Category_Quant_Mean'] = orders['Category'].map(cat_quant_mean)
print(orders[['Category', 'Category_Quant_Mean']].drop_duplicates().head())
      Category  Category_Quant_Mean
0     Clothing             2.542289
1       Sports             2.527675
2  Electronics             2.318182
3         Home             2.509434
4       Beauty             2.523316
# Example 16: Advanced - Combine multiple encodings for same feature
orders['Category_freq'] = orders['Category'].map(orders['Category'].value_counts(normalize=True))
orders = pd.concat([orders, pd.get_dummies(orders['Category'], prefix='cat')], axis=1)
print(orders.head(3))
   OrderID  CustomerID  ProductID  Quantity           OrderDate     Category  \
0        1        1102       1049         3 2023-01-01 00:00:00     Clothing   
1        2        1435       1011         1 2023-01-01 01:00:00       Sports   
2        3        1348       1085         3 2023-01-01 02:00:00  Electronics   

   Category_code  Category_Quant_Mean  Category_freq  cat_Beauty  \
0              1             2.542289          0.201       False   
1              4             2.527675          0.271       False   
2              2             2.318182          0.176       False   

   cat_Clothing  cat_Electronics  cat_Home  cat_Sports  
0          True            False     False       False  
1         False            False     False        True  
2         False             True     False       False  
# Example 17: Advanced - Encoding errors: missing values in category
orders_with_nan = orders.copy()
orders_with_nan.loc[::50, 'Category'] = np.nan
try:
    orders_with_nan['Category_code'] = le.fit_transform(orders_with_nan['Category'].astype(str))
    print('Category codes with NaN:', orders_with_nan['Category_code'].unique())
except Exception as e:
    print('Encoding failed:', str(e))
Category codes with NaN: [5 4 2 3 0 1]
# Example 18: Debugging - Ensure all categories encoded after missing fill
orders_with_nan['Category_fill'] = orders_with_nan['Category'].fillna('Unknown')
orders_with_nan['Category_code'] = le.fit_transform(orders_with_nan['Category_fill'])
print(orders_with_nan[['Category_fill', 'Category_code']].head(10))
  Category_fill  Category_code
0       Unknown              5
1        Sports              4
2   Electronics              2
3          Home              3
4        Beauty              0
5        Sports              4
6   Electronics              2
7        Sports              4
8          Home              3
9      Clothing              1
# Example 19: Debugging - Catch grouping mistakes with encoded categories
# Incorrect: using codes instead of names for aggregation reporting
wrong_report = orders.groupby('Category_code').agg({'Quantity':'sum'})
print('WRONG: Aggregation by codes only')
print(wrong_report.head())
# Correct: map codes back to names for proper business reporting
labels = dict(zip(orders['Category_code'], orders['Category']))
wrong_report.index = wrong_report.index.to_series().map(labels)
print('CORRECTED: Aggregation with category names')
print(wrong_report.head())
WRONG: Aggregation by codes only
               Quantity
Category_code          
0                   487
1                   511
2                   408
3                   399
4                   685
CORRECTED: Aggregation with category names
               Quantity
Category_code          
Beauty              487
Clothing            511
Electronics         408
Home                399
Sports              685

Best Practices for Encoding Categorical Retail Variables#

  • Use label or ordinal encoding for tree-based models (like decision trees or random forest).
  • Use one-hot encoding for linear models or when there are few categories.
  • Always handle missing category values before encoding.
  • Avoid encoding category columns with thousands of unique values without dimensionality reduction.
  • Use mean or frequency encoding to capture business-signal, but monitor for overfitting.
# Advanced Pattern: Customer segmentation using encoded categories
customer_category = orders.groupby(['CustomerID','Category'])['Quantity'].sum().unstack(fill_value=0)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
cust_cat_scaled = scaler.fit_transform(customer_category)
print(pd.DataFrame(cust_cat_scaled, columns=customer_category.columns).head())
Category    Beauty  Clothing  Electronics      Home    Sports
0        -0.084557 -0.631143    -0.589491 -0.595127  3.128317
1         0.474312 -0.631143    -0.589491 -0.595127  2.147748
2        -0.643426 -0.631143    -0.589491  0.666722  0.676894
3        -0.643426  0.936216     1.855162  1.928571 -0.793960
4        -0.643426 -0.631143    -0.589491 -0.595127  1.167179

Tiny End-to-End Retail Analytics Task#

  • Objective: Identify top-selling product categories for a key business region.
  • Steps:
    1. Filter transactions for United Kingdom.
    1. Merge with product catalog to link to category.
    1. Aggregate sales by category and sort.
    1. Recommend which product category to prioritize.
# Filter for United Kingdom transactions and merge with product catalog
uk_df = df[df['Country'] == 'United Kingdom']
uk_df = pd.merge(uk_df, catalog[['ProductID', 'Category']], left_on='StockCode', right_on='ProductID', how='left')
print(uk_df[['Invoice', 'StockCode', 'Category']].head(7))
  Invoice StockCode Category
0  536365    85123A      NaN
1  536365     71053      NaN
2  536365    84406B      NaN
3  536365    84029G      NaN
4  536365    84029E      NaN
5  536365     22752      NaN
6  536365     21730      NaN
# Aggregate and rank top-selling categories in UK
uk_cat_sales = uk_df.groupby('Category')['Quantity'].sum().sort_values(ascending=False)
print('Top selling product categories in UK:')
print(uk_cat_sales)
Top selling product categories in UK:
Series([], Name: Quantity, dtype: int64)

Practice and Next Steps#

  • Practice encoding other retail categorical variables, such as payment type, customer segment, or region.
  • Try using encoded categories in a sales forecasting model or a customer cluster analysis.
  • For visual demos: Search "retail analytics encoding" on YouTube for more step-by-step guides.

Found this useful?

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