Clustering Product Names with Python — Part 1 | by Lily Wu | Towards Data Science

Natural Language Processing (NLP) refers to the automatic computational processing of human language like text and speech.
It is particularly useful for analysing large amounts of unlabelled text to quickly extract meaning which is exactly the problem when it comes to categorising eCommerce products. Products can be either labelled with incorrect categories or not labelled at all. Manual categorisation is not efficient, if not impossible for some.
Today we are going to talk about how we can use NLP and K-means in Python to automatically cluster unlabelled product names to quickly understand what kinds of products are in a data set. This method is unsupervised (the categories and number of categories are not set) and differs from classification which is supervised and allocates product names to target labels (known categories).
For this guide I’ll be using a data set from the Australian Food Composition Database which contains data on the nutrient content of Australian foods. I’ll show you how I clustered 1,534 food names with a lot of uniqueness...
...to get an efficient, high level view of all the types of foods in the data set — with little human input.
The method consists of the following steps:
Preprocessing the text (the food names) into clean words so that we can turn it into numerical data.
Vectorisation which is the process of turning words into numerical features to prepare for machine learning.
Applying K-means clustering, an unsupervised machine learning algorithm, to group food names with similar words together.
Assessing cluster quality through cluster labelling and visualisation.
Finetuning steps 1–4 to improve cluster quality.
This article is Part 1 and will cover: Preprocessing and Vectorisation.
Be sure to also check out Part 2 which will cover: K-means Clustering, Assessing Cluster Quality and Finetuning.
Full disclosure: this data set actually comes with a column ‘Classification Name’ with 268 categories but for demonstration purposes, let’s pretend it’s not there ;)
This guide will use Pandas, NumPy, scikit-learn, FuzzyWuzzy, Matplotlib and Plotly. Optional are libraries used to preprocess this specific data set. I used Gensim, String, NLTK and Webcolors. If you don’t already have everything installed, the easiest way is to install via pip.
pip install fuzzywuzzypip install plotlypip install gensimpip install webcolors
Let’s import the libraries, load the data set and put the food names in a Pandas Series.
The aim of the game here is to remove unnecessary words and characters so that the words in our food names are meaningful for clustering later.
There are many preprocessing techniques and selecting which ones to use depends on how they’ll affect the clusters. Here are the techniques I used and why.
Stopwords are the common words in language like ‘the’, ‘a’, ‘is’, ‘and’. Yielding a cluster because all the food names contain the word ‘and’ for example, which isn’t relevant to what the foods are, isn’t useful.
We’ll remove stopwords using the Gensim library, and punctuation and numbers using the String library.
Stemming words involves shortening them to their root forms. For example ‘apple’ and ‘apples’ both become ‘appl’ and are treated as the same word in the vectorisation stage.
Note: lemmatisation would reduce both words to the real word ‘apple’ based on context. It is more computationally expensive and wasn’t required for this exercise as I could easily tell what the stemmed words referred to.
Using the NLTK library will also make all words lower case. This is useful so that ‘Appl’ and ‘appl’ are treated as the same word in the vectorisation stage.
Having colours in our food names will likely yield clusters of same-coloured but otherwise unrelated foods. We’ll remove colours using the Webcolors dictionary, but not the colours that are also foods (eg: ‘chocolate’ and ‘lime’).
Here’s a before and after of our food names. We ended up with 851 distinct words in our text.
Some Python libraries used in the vectorisation stage have some of these techniques built-in. However, if testing multiple vectorisation models, it’s best to start with a consistent, clean text to be able to compare output.
We now want to turn our cleaned text into numerical data so that we can perform statistical analysis on it.
Just like preprocessing, there are many techniques to choose from. These are the models I tested.
Bag of words (using sci-kit learn’s CountVectorizer) is a basic model that counts the occurrences of words in a document. Here, each row — one food name — is a document. The result is a matrix containing a feature for each distinct word in the text and the count of each word in a row (or vector) as its numerical values.
Here’s the matrix again, subsetted to show only the words in the first row. Note the counts for each row.
A basic count of words like this may not be enough to extract meaning. In this case, some words in a food name are more pertinent to what type of food it is. For example in ‘Broccolini, fresh, raw’, the word ‘broccolini’ is more important than ‘fresh’ or ‘raw’ for our clustering but bag of words gives all three equal weight.
TF-IDF (using sci-kit learn’s TfidfVectorizer) measures the frequency of a word in a document and compares it to the frequencies of all words in the text to assign it a weighted score of importance.
Let’s see it in action at a word level where words are considered independently of each other for meaning.
Now we have different values for each word of the vector. In the first row, ‘cardamom’ has the highest score which is great!
We can use TF-IDF at an n-gram level where the frequency of sequences of words is also considered.
Here I used the ngram_range parameter to tell the model to consider n-grams between 1 (individual words) and 2 (sequences of 2 words or bi-grams). The first row has values against the bi-grams ‘cardamom seed’, ‘seed dried’ and ‘dried ground’, with the highest score for ‘cardamom seed’. Awesome!
I tested increasing the ngram_range upper limit to 3, 4 and 5 but 2 worked best for this data set in the clustering later. The documents (our food names) are relatively short and there weren’t many meaningful n-grams that were longer than 2 words.
LDA identifies patterns in word frequency to probabilistically estimate the topics of documents and the words used in those topics. It assumes each document is made up of several topics and similar topics use similar words. Like in bag of words, each word is considered independently of each other in the model.
LDA can be very useful for large documents, such as news articles, in uncovering its high level themes. However, it wasn’t quite right for this data set as each food name isn’t necessarily made up of multiple topics.
I tested for the optimal learning rate and number of topics to tell the model to find. Here is the result at 30 topics and the top 5 words per topic.
It’s done exactly what it’s meant to do. We have patterns of words used together to describe our foods but they don’t illuminate the types of foods in our data set.
Lastly, Fuzzywuzzy calculates a similarity score between two strings and is great for fuzzy (as opposed to exact) matching. This one was some outside-the-box thinking. My logic was that if food names of the same food type were worded slightly differently (eg: different kinds of apples), they would yield a high similarity score.
Here I created a matrix of each food name and its similarity score with every other food name. I ended up with 1,534 rows (one for each food name) and 1,500 columns (one for each distinct food name after preprocessing).
Admittedly this was the slowest vectorisation method as it performed in-memory calculations for a 1,534 x 1,500 Pandas Dataframe. After clustering it performed similarly to bag of words.
Keep reading in Part 2 to find out what the results of clustering were.
This article is Part 1 and covered: Preprocessing and Vectorisation.
Please keep reading in Part 2 which will cover: K-means Clustering, Assessing Cluster Quality and Finetuning.
The data set used in this guide is from the Australian Food Composition Database and is licenced by Food Standards Australia New Zealand.