Encoding Categorical Features in C++
The decision tree splits on numeric thresholds: it asks x[f] < t. A column of strings like {"red", "green", "blue"} has no such ordering, so it has to be turned into numbers first. The two standard ways are label encoding and one-hot encoding, and the difference between them is whether you accidentally invent an order that is not there.
Label Encoding
Label encoding maps each distinct category to an integer id. It is compact, one column stays one column, and it is the natural fit for the target labels a classifier predicts. Fitting it once records the mapping so the same ids are reused everywhere:
#include <bits/stdc++.h>
using namespace std;
struct LabelEncoder {
map<string,int> index; // category -> integer id (sorted order)
void fit(const vector<string>& col) {
set<string> uniq(col.begin(), col.end());
int id = 0;
for (auto& c : uniq) index[c] = id++;
}
int transform(const string& c) const { return index.at(c); }
int size() const { return index.size(); }
};
red=2, green=1, blue=0 tells a distance- or gradient-based model that green sits halfway between blue and red, an order that does not exist. Tree models are immune (a threshold on the ids just partitions the categories into two groups), but for most other models an unordered feature should be one-hot encoded instead.One-Hot Encoding
One-hot encoding replaces a $K$-category feature with $K$ binary columns, exactly one of which is $1$. No category is numerically larger than another, so no false order is implied. The cost is width: $K$ categories become $K$ columns.
int main() {
vector<string> col = {"red", "green", "blue", "green", "red"};
LabelEncoder enc;
enc.fit(col);
printf("categories:");
for (auto& [c, id] : enc.index) printf(" %s=%d", c.c_str(), id);
printf("\n");
printf("label: ");
for (auto& c : col) printf(" %s->%d", c.c_str(), enc.transform(c));
printf("\n");
printf("one-hot:\n");
int K = enc.size();
for (auto& c : col) {
vector<int> oneHot(K, 0);
oneHot[enc.transform(c)] = 1; // flip on this category's column
printf(" %-6s ->", c.c_str());
for (int v : oneHot) printf(" %d", v);
printf("\n");
}
}
The label row collapses each category to a single id; the one-hot block gives each its own column, so red, green, and blue are equidistant. Because the encoder is fit once and stored, a category seen at training time always maps to the same column at test time, and an unseen category can be detected (here index.at would throw) rather than silently mis-encoded.