Menu

PySpark StringIndexer – A Comprehensive Guide to master PySpark StringIndexer

Deep understanding of PySpark’s StringIndexe, how it works, and how to effectively use it in your PySpark workflow

Machine learning practitioners often encounter categorical data that needs to be transformed into a numerical format. We will delve into PySpark’s StringIndexer, an essential feature that converts categorical string columns into numerical indices.

This guide will provide a deep understanding of PySpark’s StringIndexer, complete with examples that highlight its relevance in machine learning tasks.

What is StringIndexer?

The StringIndexer is a vital PySpark feature that helps convert categorical string columns in a DataFrame into numerical indices. This conversion is necessary because most machine learning algorithms cannot work directly with string data.

The StringIndexer assigns a unique index to each distinct string value in the input column and maps it to a new output column of integer indices.

How the StringIndexer works?

The StringIndexer processes the input column’s string values based on their frequency in the dataset. By default, the most frequent label receives the index 0, the second most frequent label receives index 1, and so on.

If two categories have same frequency index value will be assigned based on the alphabetical order However, you can also set a custom ordering of the labels using the ‘stringOrderType’ parameter.

1. Import required libraries and initialize SparkSession

First, let’s import the necessary libraries and create a SparkSession, the entry point to use PySpark.

import findspark
findspark.init()

from pyspark.sql import SparkSession
from pyspark.ml.feature import StringIndexer

spark = SparkSession.builder.appName("StringIndexerExample").getOrCreate()

2. Load your data and create a DataFrame

data = [("A", 10),("A", 20),("B", 30),("B", 20),("B", 30),("C", 40),("C", 10),("D", 10)]
columns = ["Categories", "Value"]
df = spark.createDataFrame(data, columns)
df.show()
+----------+-----+
|Categories|Value|
+----------+-----+
|         A|   10|
|         A|   20|
|         B|   30|
|         B|   20|
|         B|   30|
|         C|   40|
|         C|   10|
|         D|   10|
+----------+-----+

3. Initialize the StringIndexer and fit it to your DataFrame

# StringIndexer Initialization
indexer = StringIndexer(inputCol="Categories", outputCol="Categories_Indexed")
indexerModel = indexer.fit(df)

# Transform the DataFrame using the fitted StringIndexer model
indexed_df = indexerModel.transform(df)
indexed_df.show()
+----------+-----+------------------+
|Categories|Value|Categories_Indexed|
+----------+-----+------------------+
|         A|   10|               1.0|
|         A|   20|               1.0|
|         B|   30|               0.0|
|         B|   20|               0.0|
|         B|   30|               0.0|
|         C|   40|               2.0|
|         C|   10|               2.0|
|         D|   10|               3.0|
+----------+-----+------------------+

Please note for categories A and C have the same frequency StringIndexer assigned the index based on the alphabetical order

4. Handling unseen labels in test data

In real-world scenarios, your model may encounter unseen labels in the test data. By default, StringIndexer throws an error when it comes across an unseen label. To handle such cases, you can set the handleInvalid1 parameter to 'skip', 'keep', or 'error', depending on your requirements.

For instance, consider a dataset with a “Color” column containing the values “Red”, “Blue”, and “Green” in train data.
In the test dataset, however, there might be a new value, “Yellow”. To handle this scenario, you can set the 'handleInvalid' parameter to keep:

data = [("A", 10),("A", 20),("B", 30),("B", 20),("B", 30),("C", 40),("C", 10),("D", 10)]
columns = ["Categories", "Value"]
train_df = spark.createDataFrame(data, columns)
train_df.show()
+----------+-----+
|Categories|Value|
+----------+-----+
|         A|   10|
|         A|   20|
|         B|   30|
|         B|   20|
|         B|   30|
|         C|   40|
|         C|   10|
|         D|   10|
+----------+-----+

Initialize the StringIndexer uaing handleInvalid="keep" and fit on train_df where dataframe train_df is having four categories A, B, C, D

indexer = StringIndexer(inputCol="Categories", outputCol="Categories_Indexed", handleInvalid="keep")
train_indexerModel = indexer.fit(train_df)

Create Test DataFrame

data = [("A", 15),("A", 22),("B", 38),("B", 20),("C", 18),("E", 19),("F", 17)]
columns = ["Categories", "Value"]
test_df = spark.createDataFrame(data, columns)
test_df.show()
+----------+-----+
|Categories|Value|
+----------+-----+
|         A|   15|
|         A|   22|
|         B|   38|
|         B|   20|
|         C|   18|
|         E|   19|
|         F|   17|
+----------+-----+

Transform DataFrame test_df where in dataframe test_df is having tow new categories E, F and category D is missing

test_indexed_df = train_indexerModel.transform(test_df)
test_indexed_df.show()
+----------+-----+------------------+
|Categories|Value|Categories_Indexed|
+----------+-----+------------------+
|         A|   15|               1.0|
|         A|   22|               1.0|
|         B|   38|               0.0|
|         B|   20|               0.0|
|         C|   18|               2.0|
|         E|   19|               4.0|
|         F|   17|               4.0|
+----------+-----+------------------+

This configuration will assign a new index to the unseen “Yellow” value during transformation, ensuring that the model does not throw an error when processing the test data.

5. Reversing StringIndexer transformation with IndexToString

In some cases, you may need to reverse the transformation applied by StringIndexer to interpret your model’s predictions.

For instance, after training a classifier, you might want to convert the predicted numerical indices back into their original string representations. PySpark provides the IndexToString transformer to accomplish this.

# Example Data
data = [("A", 10),("A", 20),("B", 30),("B", 20),("B", 30),("C", 40),("C", 10),("D", 10)]
columns = ["Categories", "Value"]

df = spark.createDataFrame(data, columns)
df.show()
+----------+-----+
|Categories|Value|
+----------+-----+
|         A|   10|
|         A|   20|
|         B|   30|
|         B|   20|
|         B|   30|
|         C|   40|
|         C|   10|
|         D|   10|
+----------+-----+

Initialize the StringIndexer and Transform the DataFrame using the fitted StringIndexer model

# StringIndexer Initialization
indexer = StringIndexer(inputCol="Categories", outputCol="Categories_Indexed")
indexerModel = indexer.fit(df)

# Transform the DataFrame using the fitted StringIndexer model
indexed_df = indexerModel.transform(df)
indexed_df.show()
+----------+-----+------------------+
|Categories|Value|Categories_Indexed|
+----------+-----+------------------+
|         A|   10|               1.0|
|         A|   20|               1.0|
|         B|   30|               0.0|
|         B|   20|               0.0|
|         B|   30|               0.0|
|         C|   40|               2.0|
|         C|   10|               2.0|
|         D|   10|               3.0|
+----------+-----+------------------+

Initialize the IndexToString transformer using the labels from the original StringIndexer

#Import the IndexToString transformer
from pyspark.ml.feature import IndexToString

#Initialize the IndexToString
index_to_string = IndexToString(inputCol="Categories_Indexed", outputCol="Pred_Category",
                                labels=indexerModel.labels)

Transform the DataFrame indexed labels back to original categories

# Transform the DataFrame
result_df = index_to_string.transform(indexed_df)

result_df.show()
+----------+-----+------------------+-------------+
|Categories|Value|Categories_Indexed|Pred_Category|
+----------+-----+------------------+-------------+
|         A|   10|               1.0|            A|
|         A|   20|               1.0|            A|
|         B|   30|               0.0|            B|
|         B|   20|               0.0|            B|
|         B|   30|               0.0|            B|
|         C|   40|               2.0|            C|
|         C|   10|               2.0|            C|
|         D|   10|               3.0|            D|
+----------+-----+------------------+-------------+

Conclusion

The PySpark StringIndexer is an invaluable tool for transforming categorical data into a format suitable for machine learning models. By mastering its usage and combining it with other PySpark transformers, you can create efficient and effective machine learning pipelines for various tasks.

With this comprehensive guide and examples, you are now well-equipped to harness the power of PySpark StringIndexer in your machine learning projects.

Course Preview

Machine Learning A-Z™: Hands-On Python & R In Data Science

Free Sample Videos:

Machine Learning A-Z™: Hands-On Python & R In Data Science

Machine Learning A-Z™: Hands-On Python & R In Data Science

Machine Learning A-Z™: Hands-On Python & R In Data Science

Machine Learning A-Z™: Hands-On Python & R In Data Science

Machine Learning A-Z™: Hands-On Python & R In Data Science