ABC Analysis with K-Means Clustering | by Andrew Udell | Towards Data Science

ABC analysis assumes that revenue-generating items in an inventory follow a Pareto distribution, where a very small percent of items generate the most amount of revenue. Using the following conventions, an item in inventory is assigned a letter based on importance:
A items are 20% of items, but contribute 70% of revenue
B items are 30% of items, but contribute 25% of revenue
C items are 50% of items, but contribute 5% of revenue
Keep in mind these numbers are rough and will vary significantly based on the actual distribution of sales. The key takeaway is that A items are a small percent of inventory, but contribute the most to revenue, C items are a large percent of inventory, but contribute the least to revenue, and B items fall somewhere in the middle.
Both inventory planning and warehousing strategies rely on ABC analysis to make key logistic decisions. For example, a warehouse manager usually wants A items closest to the shipping docks to reduce the time required to pick them. This boosts productivity and reduces labor costs.
Broadly speaking, K-means clustering is an unsupervised machine learning technique which attempts to group together similar observations. It does so by calculating a mean, or centroid, of each random group, or cluster, and places observations into the cluster with the nearest mean.
Once observations are added to the clusters, the centroids are calculated again and points are accordingly removed or added to the clusters. This process repeats until the clusters are stable (ie, observations are no longer added or removed from the clusters).
This simple machine learning algorithm may be used for ABC analysis.
The largest disadvantage of K-means clustering is that it requires the number of clusters need to be known in advance. While difficult to estimate in many situations, we know we want to divide the inventory in three clusters for ABC analysis: A, B, and C.
Additionally, by the nature of using distance from the mean, K-means clustering makes the assumption that the clusters are circular in shape. Under this assumption, more nuanced classification problems would fail to classify properly, but ABC analysis is simple enough that it may adequately be represented.
On a similar note, using distance as a metric often distorts high-dimensional data. Luckily, ABC analysis only examines two parameters: the revenue an item generates and how that revenue is distributed.
All these factors makes K-means clustering an optimal solution to determine the A, B, and C items.
The data used in this project are from popular online retailer, Wish. The data set includes only the online sale of clothing throughout the summer. Most importantly, it features the number of units sold and the price sold, which will yield the revenue per item. The full data set may be found on Kaggle.
The goal is to sort all the items in the data set into an ABC categorization based on importance. When viewing the results, there should be relatively few A items that drive the majority of the revenue and a large number of C items that don’t provide much revenue.
The distribution of these should be that A items sell the most, C items sell the least, and B items fall in the middle of the two. If items are randomly distributed through the data (ie, the first highest selling item is an A and the second is a C), there’s a problem that needs addressing.
All data formatting and analysis will be done in python.
# Import librariesimport pandas as pdimport numpy as np# read the data to a dataframedf = pd.read_csv("Summer_Sales_08.2020.csv")
Before crunching any numbers, the Pandas and NumPy libraries are imported to help manage the data.
Next, the data, previously saved as a CSV file, is read into a dataframe.
# Add a column for revenuesdf["revenue"] = df["units_sold"] * df["price"]
A new column is added to the dataframe for revenue by simply multiplying the number units sold by the price. It’s possible the price varied over time, especially as flash sales occurred, but without further data to analyze, the assumption is made that all items sold at a one, stable price.
import seaborn as snssns.distplot(df["revenue"])
The figure above demonstrates the Pareto distribution found in the data. The vast majority of the items generate less than €200,000 in revenue (Although it’s not explicit in the graph, further analysis found about two-thirds of the items generate less than €20,000). At the same time, a handful of items sell between €400,000 and €800,000, which drive a majority of the revenue.
# Create bins functiondef bins(x):    for bar in range(20000, 820000, 20000):        if x <= bar:            return bar# Create new column to apply the bin functiondf["rev_dist"] = df["revenue"].apply(lambda x: bins(x))
A function is written to classify the amount of revenue an item generates into bins and then applied to the dataframe. If an item generates €20,000 or less, it gets classified as €20,000. It an items makes between €20,000 and €40,000, it gets classified as €40,000 and so on for each increment of €20,000.
Choosing a bin size is crucial to making a good estimate. The bins shouldn’t be either too large or too small or else the final ABC classification will be skewed. In this case, the bin size was chosen so that each bin had fewer items in them than the preceding bin. An initial estimate may have to made and tweaked to get the desired effect.
# Create a support column of 1’s to facilitate the pivot tabledf["count"] = 1# Create a pivot table of the revenue distributionspivot_table = pd.pivot_table(df, index = ["rev_dist"], values = ["count"], aggfunc = np.sum)
A pivot table is created to enumerate the number of items that fall in each categories. For example, 1,072 items generate €20,000 or less. 120 items make between €20,000 and €40,000 and so on.
To properly train the model, it’s not sufficient to simply look at how much revenue each item generates. It also needs to know how the revenue is distributed. This pivot table provides a very manageable data set for the model to train on.
# import model from SKLearnfrom sklearn.cluster import KMeans# K -clusters is equal to 3 because things will be sorted into A, B, and Ckmeans = KMeans(n_clusters=3)kmeans.fit(pivot_table)
The K-means model from scikit-learn is imported and initialized. As mentioned before, the parameter n_clusters is set to three, because we want to divide the data into three clusters. The model is then fit onto the pivot table.
pivot_table["category"] = kmeans.labels_
A new column is placed on the pivot table giving the classification from the model. It’s worth noting that that by default the K-means model from scikit-learn will classify items on a numerical scale instead of the alphabetical scale used in ABC analysis. Consequently, each row will be labeled as either zero, one, or two.
Luckily, while the labels differ, the actual underlying pattern remains the same. As a result, the numerical labels will correlate to an alphabetical label, but will require review to determine. In this case, zero is A, one is C and two is B.
# Create a dictionary to give alphabetical labelsABC_dict = {        0: "A",    1: "C",    2: "B"}pivot_table["ABC"] = pivot_table["category"].apply(lambda x: ABC_dict[x])
A quick dictionary was written and applied to the pivot table to give each row its ABC classification.
# Merge the dataframes so that there's a new column to identify ABCdf = pd.merge(df, pivot_table, on = "rev_dist", how ="left")
Recall that the the model was trained on a pivot table. The ABC classification hasn’t been assigned to the items, yet. Instead, it’s been assigned to a revenue classification.
This means that while we don’t immediately know which items are in the A category, we know that certain revenue classifications (ie, €780,000 to €800,000) are rated as A items.
As a result, we can simply merge the main dataframe and the pivot table to give each item its ABC classification.
When analyzing the final distribution of items, it was found that:
A items represent 11.4% of items, but 61.7% of revenue
B items represent 20.5% of items, but 30.7% of revenue
C items represent 68.1% of items, but 7.6% of revenue
While these numbers don’t perfectly match a Pareto distribution, it’s very likely that the data itself varies from those idealized numbers. Instead, it’s more important that the clusters very clearly convey that a small portion of the inventory generates the majority of the revenue.
Likewise all the A items were top selling items while all the C items were bottom selling items and B items fell perfectly in the middle. The model was a success!
While K-means clustering probably isn’t the most efficient way to perform ABC analysis, it demonstrates the model’s ability to find the same underlying pattern.
With this success, it opens the door to using more unsupervised learning techniques to uncover sales and logistics insights. Clustering in particular provides an intuitive way to segment product along patterns which may or may not be obvious to human eyes and may provide the cutting edge for those who use it.